From cbd36dc257146b79e3fb613500b50826262aef61 Mon Sep 17 00:00:00 2001 From: Chandrashekhar Chanagonda Date: Tue, 4 Jan 2022 21:49:32 +0530 Subject: [PATCH 01/13] added NightwatchJS folder --- nightwatchJS/.gitignore | 4 + nightwatchJS/nightwatch.conf.js | 273 ++++++++++++++++++++++++++++++++ nightwatchJS/package.json | 16 ++ nightwatchJS/tests/Demo.js | 14 ++ 4 files changed, 307 insertions(+) create mode 100644 nightwatchJS/.gitignore create mode 100644 nightwatchJS/nightwatch.conf.js create mode 100644 nightwatchJS/package.json create mode 100644 nightwatchJS/tests/Demo.js diff --git a/nightwatchJS/.gitignore b/nightwatchJS/.gitignore new file mode 100644 index 0000000..54cfdad --- /dev/null +++ b/nightwatchJS/.gitignore @@ -0,0 +1,4 @@ +node_modules +chromedriver.log +geckodriver.log +tests_output diff --git a/nightwatchJS/nightwatch.conf.js b/nightwatchJS/nightwatch.conf.js new file mode 100644 index 0000000..8a0a17c --- /dev/null +++ b/nightwatchJS/nightwatch.conf.js @@ -0,0 +1,273 @@ +// Autogenerated by Nightwatch +// Refer to the online docs for more details: https://nightwatchjs.org/gettingstarted/configuration/ +const Services = {}; loadServices(); + +module.exports = { + // An array of folders (excluding subfolders) where your tests are located; + // if this is not specified, the test source must be passed as the second argument to the test runner. + src_folders: [], + + // See https://nightwatchjs.org/guide/working-with-page-objects/ + page_objects_path: '', + + // See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands + custom_commands_path: '', + + // See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-assertions + custom_assertions_path: '', + + // See https://nightwatchjs.org/guide/#external-globals + globals_path : '', + + webdriver: {}, + + test_settings: { + default: { + disable_error_log: false, + launch_url: 'https://nightwatchjs.org', + + screenshots: { + enabled: false, + path: 'screens', + on_failure: true + }, + + desiredCapabilities: { + browserName : 'chrome' + }, + + webdriver: { + start_process: true, + server_path: (Services.chromedriver ? Services.chromedriver.path : '') + } + }, + + safari: { + desiredCapabilities : { + browserName : 'safari', + alwaysMatch: { + acceptInsecureCerts: false + } + }, + webdriver: { + port: 4445, + start_process: true, + server_path: '/usr/bin/safaridriver' + } + }, + + firefox: { + desiredCapabilities : { + browserName : 'firefox', + alwaysMatch: { + acceptInsecureCerts: true, + 'moz:firefoxOptions': { + args: [ + // '-headless', + // '-verbose' + ] + } + } + + }, + webdriver: { + start_process: true, + port: 4444, + server_path: (Services.geckodriver ? Services.geckodriver.path : ''), + cli_args: [ + // very verbose geckodriver logs + // '-vv' + ] + } + }, + + chrome: { + desiredCapabilities : { + browserName : 'chrome', + 'goog:chromeOptions' : { + // More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/ + // + // This tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78) + w3c: false, + args: [ + //'--no-sandbox', + //'--ignore-certificate-errors', + //'--allow-insecure-localhost', + //'--headless' + ] + } + }, + + webdriver: { + start_process: true, + port: 9515, + server_path: (Services.chromedriver ? Services.chromedriver.path : ''), + cli_args: [ + // --verbose + ] + } + }, + + edge: { + desiredCapabilities : { + browserName : 'MicrosoftEdge', + 'ms:edgeOptions' : { + w3c: false, + // More info on EdgeDriver: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options + args: [ + //'--headless' + ] + } + }, + + webdriver: { + start_process: true, + // Download msedgedriver from https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/ + // and set the location below: + server_path: '', + cli_args: [ + // --verbose + ] + } + }, + + ////////////////////////////////////////////////////////////////////////////////// + // Configuration for when using the browserstack.com cloud service | + // | + // Please set the username and access key by setting the environment variables: | + // - BROWSERSTACK_USER | + // - BROWSERSTACK_KEY | + // .env files are supported | + ////////////////////////////////////////////////////////////////////////////////// + browserstack: { + selenium: { + host: 'hub-cloud.browserstack.com', + port: 443 + }, + // More info on configuring capabilities can be found on: + // https://www.browserstack.com/automate/capabilities?tag=selenium-4 + desiredCapabilities: { + 'bstack:options' : { + userName: '${BROWSERSTACK_USER}', + accessKey: '${BROWSERSTACK_KEY}', + } + }, + + disable_error_log: true, + webdriver: { + timeout_options: { + timeout: 15000, + retry_attempts: 3 + }, + keep_alive: true, + start_process: false + } + }, + + 'browserstack.local': { + extends: 'browserstack', + desiredCapabilities: { + 'browserstack.local': true + } + }, + + 'browserstack.chrome': { + extends: 'browserstack', + desiredCapabilities: { + browserName: 'chrome', + chromeOptions : { + w3c: false + } + } + }, + + 'browserstack.firefox': { + extends: 'browserstack', + desiredCapabilities: { + browserName: 'firefox' + } + }, + + 'browserstack.ie': { + extends: 'browserstack', + desiredCapabilities: { + browserName: 'internet explorer', + browserVersion: '11.0' + } + }, + + 'browserstack.safari': { + extends: 'browserstack', + desiredCapabilities: { + browserName: 'safari' + } + }, + + 'browserstack.local_chrome': { + extends: 'browserstack.local', + desiredCapabilities: { + browserName: 'chrome' + } + }, + + 'browserstack.local_firefox': { + extends: 'browserstack.local', + desiredCapabilities: { + browserName: 'firefox' + } + }, + ////////////////////////////////////////////////////////////////////////////////// + // Configuration for when using the Selenium service, either locally or remote, | + // like Selenium Grid | + ////////////////////////////////////////////////////////////////////////////////// + selenium_server: { + // Selenium Server is running locally and is managed by Nightwatch + selenium: { + start_process: true, + port: 4444, + server_path: (Services.seleniumServer ? Services.seleniumServer.path : ''), + cli_args: { + 'webdriver.gecko.driver': (Services.geckodriver ? Services.geckodriver.path : ''), + 'webdriver.chrome.driver': (Services.chromedriver ? Services.chromedriver.path : '') + } + } + }, + + 'selenium.chrome': { + extends: 'selenium_server', + desiredCapabilities: { + browserName: 'chrome', + chromeOptions : { + w3c: false + } + } + }, + + 'selenium.firefox': { + extends: 'selenium_server', + desiredCapabilities: { + browserName: 'firefox', + 'moz:firefoxOptions': { + args: [ + // '-headless', + // '-verbose' + ] + } + } + } + } +}; + +function loadServices() { + try { + Services.seleniumServer = require('selenium-server'); + } catch (err) {} + + try { + Services.chromedriver = require('chromedriver'); + } catch (err) {} + + try { + Services.geckodriver = require('geckodriver'); + } catch (err) {} +} diff --git a/nightwatchJS/package.json b/nightwatchJS/package.json new file mode 100644 index 0000000..3f67939 --- /dev/null +++ b/nightwatchJS/package.json @@ -0,0 +1,16 @@ +{ + "name": "nightwatchjs", + "version": "1.0.0", + "description": "Test 'https://www.ecosia.org/' web application using NightwatchJS ", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "chanaggonda", + "license": "ISC", + "devDependencies": { + "chromedriver": "^96.0.0", + "geckodriver": "^3.0.1", + "nightwatch": "^1.7.13" + } +} diff --git a/nightwatchJS/tests/Demo.js b/nightwatchJS/tests/Demo.js new file mode 100644 index 0000000..8e84739 --- /dev/null +++ b/nightwatchJS/tests/Demo.js @@ -0,0 +1,14 @@ +describe('Demo test Ecosia.org', function() { + test('search for nightwatch', function(browser) { + browser + .url('https://www.ecosia.org/') + .waitForElementVisible('body') + .assert.titleContains('Ecosia') + .assert.visible('input[type=search]') + .sendKeys('input[type=search]', 'nightwatch') + .assert.visible('button[type=submit]') + .click('button[type=submit]') + .assert.containsText('.mainline-results', 'Nightwatch.js') + .end(); + }) +}); \ No newline at end of file From 84353ec921df85efc71d56829e2a25a1ec751c9e Mon Sep 17 00:00:00 2001 From: Chandrashekhar Chanagonda Date: Tue, 11 Jan 2022 22:10:05 +0530 Subject: [PATCH 02/13] updated feature --- nightwatchJS/features/homepage.feature | 10 ++++++++++ nightwatchJS/nightwatch.conf.js | 22 ++++++++++++++++------ nightwatchJS/package.json | 5 +++++ nightwatchJS/pages/Homepage.js | 0 nightwatchJS/steps/Homepage/given.js | 0 nightwatchJS/steps/Homepage/then.js | 0 nightwatchJS/steps/Homepage/when.js | 0 nightwatchJS/tests/Demo.js | 7 ++++--- 8 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 nightwatchJS/features/homepage.feature create mode 100644 nightwatchJS/pages/Homepage.js create mode 100644 nightwatchJS/steps/Homepage/given.js create mode 100644 nightwatchJS/steps/Homepage/then.js create mode 100644 nightwatchJS/steps/Homepage/when.js diff --git a/nightwatchJS/features/homepage.feature b/nightwatchJS/features/homepage.feature new file mode 100644 index 0000000..0355563 --- /dev/null +++ b/nightwatchJS/features/homepage.feature @@ -0,0 +1,10 @@ +Feature: Ecosia website uses + To verify all the advantages in using Ecosia.com + + Scenario: Search for a keyword in Ecosia search engine + Given I open the Ecosia home page + And the title is "Ecosia" + Then the user enters keyword "Docker" + Then the user submits the keyword + Then the user verifies the search results for "Docker" + \ No newline at end of file diff --git a/nightwatchJS/nightwatch.conf.js b/nightwatchJS/nightwatch.conf.js index 8a0a17c..0bfaa0b 100644 --- a/nightwatchJS/nightwatch.conf.js +++ b/nightwatchJS/nightwatch.conf.js @@ -1,11 +1,20 @@ -// Autogenerated by Nightwatch +var nightwatchCucumber = require('nightwatch-cucumber'); + // Refer to the online docs for more details: https://nightwatchjs.org/gettingstarted/configuration/ const Services = {}; loadServices(); + +var nightwatchCucumberConf = { + runner: 'nightwatch', + featureFiles: 'features', + stepDefinitions: 'steps', + closeSession: 'afterFeature' +} + module.exports = { // An array of folders (excluding subfolders) where your tests are located; // if this is not specified, the test source must be passed as the second argument to the test runner. - src_folders: [], + src_folders: [nightwatchCucumber(nightwatchCucumberConf)], // See https://nightwatchjs.org/guide/working-with-page-objects/ page_objects_path: '', @@ -27,9 +36,10 @@ module.exports = { launch_url: 'https://nightwatchjs.org', screenshots: { - enabled: false, - path: 'screens', - on_failure: true + enabled: true, + on_error: true, + on_failure: true, + path: '/screenshots' }, desiredCapabilities: { @@ -270,4 +280,4 @@ function loadServices() { try { Services.geckodriver = require('geckodriver'); } catch (err) {} -} +} \ No newline at end of file diff --git a/nightwatchJS/package.json b/nightwatchJS/package.json index 3f67939..28c33eb 100644 --- a/nightwatchJS/package.json +++ b/nightwatchJS/package.json @@ -12,5 +12,10 @@ "chromedriver": "^96.0.0", "geckodriver": "^3.0.1", "nightwatch": "^1.7.13" + }, + "dependencies": { + "cucumber": "^7.0.0-rc.0", + "nightwatch-cucumber": "^9.1.3", + "selenium-server": "^3.141.59" } } diff --git a/nightwatchJS/pages/Homepage.js b/nightwatchJS/pages/Homepage.js new file mode 100644 index 0000000..e69de29 diff --git a/nightwatchJS/steps/Homepage/given.js b/nightwatchJS/steps/Homepage/given.js new file mode 100644 index 0000000..e69de29 diff --git a/nightwatchJS/steps/Homepage/then.js b/nightwatchJS/steps/Homepage/then.js new file mode 100644 index 0000000..e69de29 diff --git a/nightwatchJS/steps/Homepage/when.js b/nightwatchJS/steps/Homepage/when.js new file mode 100644 index 0000000..e69de29 diff --git a/nightwatchJS/tests/Demo.js b/nightwatchJS/tests/Demo.js index 8e84739..b148d05 100644 --- a/nightwatchJS/tests/Demo.js +++ b/nightwatchJS/tests/Demo.js @@ -1,5 +1,5 @@ -describe('Demo test Ecosia.org', function() { - test('search for nightwatch', function(browser) { +describe('Home page search - Ecosia.org', function() { + test('search for keyword - nightwatch', function(browser) { browser .url('https://www.ecosia.org/') .waitForElementVisible('body') @@ -11,4 +11,5 @@ describe('Demo test Ecosia.org', function() { .assert.containsText('.mainline-results', 'Nightwatch.js') .end(); }) -}); \ No newline at end of file +}); + \ No newline at end of file From 7257eb75f3171256d045d32790087b49f9aa8a80 Mon Sep 17 00:00:00 2001 From: Chandrashekhar Chanagonda Date: Sat, 15 Jan 2022 21:22:03 +0530 Subject: [PATCH 03/13] Pytest folder --- Pytest/.gitignore | 4 ++++ Pytest/tests/conftest.py | 7 +++++++ Pytest/tests/pytest.ini | 5 +++++ Pytest/tests/test_div_by_13.py | 5 +++++ Pytest/tests/test_multiplication.py | 6 ++++++ Pytest/tests/test_square.py | 19 +++++++++++++++++++ 6 files changed, 46 insertions(+) create mode 100644 Pytest/.gitignore create mode 100644 Pytest/tests/conftest.py create mode 100644 Pytest/tests/pytest.ini create mode 100644 Pytest/tests/test_div_by_13.py create mode 100644 Pytest/tests/test_multiplication.py create mode 100644 Pytest/tests/test_square.py diff --git a/Pytest/.gitignore b/Pytest/.gitignore new file mode 100644 index 0000000..8a6b89d --- /dev/null +++ b/Pytest/.gitignore @@ -0,0 +1,4 @@ +venv +.pytest_cache +.idea +result.xml diff --git a/Pytest/tests/conftest.py b/Pytest/tests/conftest.py new file mode 100644 index 0000000..1e00795 --- /dev/null +++ b/Pytest/tests/conftest.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.fixture +def input_value(): + numb = 30 + return numb diff --git a/Pytest/tests/pytest.ini b/Pytest/tests/pytest.ini new file mode 100644 index 0000000..2f95331 --- /dev/null +++ b/Pytest/tests/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +markers = + great: all with great. + low: all with low + diff --git a/Pytest/tests/test_div_by_13.py b/Pytest/tests/test_div_by_13.py new file mode 100644 index 0000000..5194338 --- /dev/null +++ b/Pytest/tests/test_div_by_13.py @@ -0,0 +1,5 @@ +import pytest + + +def test_divisibleby13(input_value): + assert input_value % 13 == 0 diff --git a/Pytest/tests/test_multiplication.py b/Pytest/tests/test_multiplication.py new file mode 100644 index 0000000..c88df12 --- /dev/null +++ b/Pytest/tests/test_multiplication.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.mark.parametrize("num, output", [(1, 11), (2, 22), (3, 35), (4, 44)]) +def test_multiply_11(num, output): + assert 11 * num == output diff --git a/Pytest/tests/test_square.py b/Pytest/tests/test_square.py new file mode 100644 index 0000000..72ed718 --- /dev/null +++ b/Pytest/tests/test_square.py @@ -0,0 +1,19 @@ +import math +import pytest + + +@pytest.mark.great +def test_square_root(): + num = 25 + assert math.sqrt(num) == 5 + + +@pytest.mark.first +def test_square(): + num = 7 + assert 7 * 7 == 42 + + +@pytest.mark.second +def tes_equality(): + assert 10 == 10 From 7eb89d950eec01d9400ed65ed443d70f4b2041ce Mon Sep 17 00:00:00 2001 From: Chandrashekhar Chanagonda Date: Mon, 17 Jan 2022 12:21:23 +0530 Subject: [PATCH 04/13] Added Pytest folder --- Pytest/main.py | 16 ++++++++++++++++ Pytest/tests/test_compare.py | 21 +++++++++++++++++++++ Pytest/tests/test_div_by_3_6.py | 9 +++++++++ 3 files changed, 46 insertions(+) create mode 100644 Pytest/main.py create mode 100644 Pytest/tests/test_compare.py create mode 100644 Pytest/tests/test_div_by_3_6.py diff --git a/Pytest/main.py b/Pytest/main.py new file mode 100644 index 0000000..94e3a87 --- /dev/null +++ b/Pytest/main.py @@ -0,0 +1,16 @@ +# This is a sample Python script. + +# Press ⌃R to execute it or replace it with your code. +# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings. + + +def print_hi(name): + # Use a breakpoint in the code line below to debug your script. + print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint. + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + print_hi('PyCharm') + +# See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/Pytest/tests/test_compare.py b/Pytest/tests/test_compare.py new file mode 100644 index 0000000..1a140e3 --- /dev/null +++ b/Pytest/tests/test_compare.py @@ -0,0 +1,21 @@ +import pytest + + +@pytest.mark.xfail +def test_greater(): + num = 100 + assert num > 200 + + +@pytest.mark.xfail +@pytest.mark.great +def test_greater_equal(): + num = 300 + assert num >= 300 + + +@pytest.mark.skip +@pytest.mark.others +def test_lesser(): + num = 10 + assert num < 11 diff --git a/Pytest/tests/test_div_by_3_6.py b/Pytest/tests/test_div_by_3_6.py new file mode 100644 index 0000000..35516ff --- /dev/null +++ b/Pytest/tests/test_div_by_3_6.py @@ -0,0 +1,9 @@ +import pytest + + +def test_divisibleby3(input_value): + assert input_value % 3 == 0 + + +def test_divisibleby6(input_value): + assert input_value % 6 == 0 From 9ba6085633232ed2d68968f3adce415d7da3a699 Mon Sep 17 00:00:00 2001 From: Chandrashekhar Chanagonda Date: Thu, 3 Feb 2022 13:04:02 +0530 Subject: [PATCH 05/13] added pytest_project folder -testing in pyenv --- ...est_capitalize.cpython-39-pytest-6.2.5.pyc | Bin 0 -> 1297 bytes .../test_wallet.cpython-39-pytest-6.2.5.pyc | Bin 0 -> 3253 bytes .../__pycache__/wallet.cpython-39.pyc | Bin 0 -> 999 bytes pytest_project/pytest-env/bin/Activate.ps1 | 241 + pytest_project/pytest-env/bin/activate | 66 + pytest_project/pytest-env/bin/activate.csh | 25 + pytest_project/pytest-env/bin/activate.fish | 64 + pytest_project/pytest-env/bin/easy_install | 8 + .../pytest-env/bin/easy_install-3.9 | 8 + pytest_project/pytest-env/bin/pip | 8 + pytest_project/pytest-env/bin/pip3 | 8 + pytest_project/pytest-env/bin/pip3.9 | 8 + pytest_project/pytest-env/bin/py.test | 8 + pytest_project/pytest-env/bin/pytest | 8 + pytest_project/pytest-env/bin/python | 1 + pytest_project/pytest-env/bin/python3 | 1 + pytest_project/pytest-env/bin/python3.9 | 1 + .../__pycache__/easy_install.cpython-39.pyc | Bin 0 -> 353 bytes .../site-packages/_pytest/__init__.py | 8 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 346 bytes .../__pycache__/_argcomplete.cpython-39.pyc | Bin 0 -> 4094 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 270 bytes .../__pycache__/cacheprovider.cpython-39.pyc | Bin 0 -> 19048 bytes .../__pycache__/capture.cpython-39.pyc | Bin 0 -> 32078 bytes .../_pytest/__pycache__/compat.cpython-39.pyc | Bin 0 -> 10853 bytes .../__pycache__/debugging.cpython-39.pyc | Bin 0 -> 11462 bytes .../__pycache__/deprecated.cpython-39.pyc | Bin 0 -> 2259 bytes .../__pycache__/doctest.cpython-39.pyc | Bin 0 -> 21423 bytes .../__pycache__/faulthandler.cpython-39.pyc | Bin 0 -> 3829 bytes .../__pycache__/fixtures.cpython-39.pyc | Bin 0 -> 45664 bytes .../__pycache__/freeze_support.cpython-39.pyc | Bin 0 -> 1479 bytes .../__pycache__/helpconfig.cpython-39.pyc | Bin 0 -> 7402 bytes .../__pycache__/hookspec.cpython-39.pyc | Bin 0 -> 32493 bytes .../__pycache__/junitxml.cpython-39.pyc | Bin 0 -> 21258 bytes .../__pycache__/logging.cpython-39.pyc | Bin 0 -> 26875 bytes .../_pytest/__pycache__/main.cpython-39.pyc | Bin 0 -> 22975 bytes .../__pycache__/monkeypatch.cpython-39.pyc | Bin 0 -> 11246 bytes .../_pytest/__pycache__/nodes.cpython-39.pyc | Bin 0 -> 18158 bytes .../_pytest/__pycache__/nose.cpython-39.pyc | Bin 0 -> 1452 bytes .../__pycache__/outcomes.cpython-39.pyc | Bin 0 -> 7458 bytes .../__pycache__/pastebin.cpython-39.pyc | Bin 0 -> 3609 bytes .../__pycache__/pathlib.cpython-39.pyc | Bin 0 -> 18239 bytes .../__pycache__/pytester.cpython-39.pyc | Bin 0 -> 67648 bytes .../pytester_assertions.cpython-39.pyc | Bin 0 -> 1605 bytes .../_pytest/__pycache__/python.cpython-39.pyc | Bin 0 -> 49564 bytes .../__pycache__/python_api.cpython-39.pyc | Bin 0 -> 26388 bytes .../__pycache__/recwarn.cpython-39.pyc | Bin 0 -> 9757 bytes .../__pycache__/reports.cpython-39.pyc | Bin 0 -> 16797 bytes .../_pytest/__pycache__/runner.cpython-39.pyc | Bin 0 -> 13674 bytes .../__pycache__/setuponly.cpython-39.pyc | Bin 0 -> 2907 bytes .../__pycache__/setupplan.cpython-39.pyc | Bin 0 -> 1419 bytes .../__pycache__/skipping.cpython-39.pyc | Bin 0 -> 8611 bytes .../__pycache__/stepwise.cpython-39.pyc | Bin 0 -> 3724 bytes .../_pytest/__pycache__/store.cpython-39.pyc | Bin 0 -> 4412 bytes .../__pycache__/terminal.cpython-39.pyc | Bin 0 -> 40859 bytes .../threadexception.cpython-39.pyc | Bin 0 -> 3317 bytes .../_pytest/__pycache__/timing.cpython-39.pyc | Bin 0 -> 604 bytes .../_pytest/__pycache__/tmpdir.cpython-39.pyc | Bin 0 -> 8490 bytes .../__pycache__/unittest.cpython-39.pyc | Bin 0 -> 11248 bytes .../unraisableexception.cpython-39.pyc | Bin 0 -> 3311 bytes .../__pycache__/warning_types.cpython-39.pyc | Bin 0 -> 4466 bytes .../__pycache__/warnings.cpython-39.pyc | Bin 0 -> 3957 bytes .../site-packages/_pytest/_argcomplete.py | 117 + .../site-packages/_pytest/_code/__init__.py | 22 + .../_code/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 651 bytes .../_code/__pycache__/code.cpython-39.pyc | Bin 0 -> 37769 bytes .../_code/__pycache__/source.cpython-39.pyc | Bin 0 -> 7032 bytes .../site-packages/_pytest/_code/code.py | 1259 +++ .../site-packages/_pytest/_code/source.py | 212 + .../site-packages/_pytest/_io/__init__.py | 8 + .../_io/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 346 bytes .../_io/__pycache__/saferepr.cpython-39.pyc | Bin 0 -> 4438 bytes .../__pycache__/terminalwriter.cpython-39.pyc | Bin 0 -> 5987 bytes .../_io/__pycache__/wcwidth.cpython-39.pyc | Bin 0 -> 1332 bytes .../site-packages/_pytest/_io/saferepr.py | 129 + .../_pytest/_io/terminalwriter.py | 210 + .../site-packages/_pytest/_io/wcwidth.py | 55 + .../site-packages/_pytest/_version.py | 5 + .../_pytest/assertion/__init__.py | 179 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6662 bytes .../__pycache__/rewrite.cpython-39.pyc | Bin 0 -> 32989 bytes .../__pycache__/truncate.cpython-39.pyc | Bin 0 -> 2923 bytes .../assertion/__pycache__/util.cpython-39.pyc | Bin 0 -> 12720 bytes .../_pytest/assertion/rewrite.py | 1125 +++ .../_pytest/assertion/truncate.py | 100 + .../site-packages/_pytest/assertion/util.py | 477 + .../site-packages/_pytest/cacheprovider.py | 575 ++ .../site-packages/_pytest/capture.py | 967 ++ .../python3.9/site-packages/_pytest/compat.py | 400 + .../site-packages/_pytest/config/__init__.py | 1606 ++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 44768 bytes .../__pycache__/argparsing.cpython-39.pyc | Bin 0 -> 17371 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 727 bytes .../__pycache__/findpaths.cpython-39.pyc | Bin 0 -> 5879 bytes .../_pytest/config/argparsing.py | 522 + .../_pytest/config/exceptions.py | 11 + .../site-packages/_pytest/config/findpaths.py | 211 + .../site-packages/_pytest/debugging.py | 388 + .../site-packages/_pytest/deprecated.py | 87 + .../site-packages/_pytest/doctest.py | 724 ++ .../site-packages/_pytest/faulthandler.py | 116 + .../site-packages/_pytest/fixtures.py | 1680 ++++ .../site-packages/_pytest/freeze_support.py | 45 + .../site-packages/_pytest/helpconfig.py | 261 + .../site-packages/_pytest/hookspec.py | 891 ++ .../site-packages/_pytest/junitxml.py | 700 ++ .../site-packages/_pytest/logging.py | 821 ++ .../python3.9/site-packages/_pytest/main.py | 876 ++ .../site-packages/_pytest/mark/__init__.py | 282 + .../mark/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 8731 bytes .../__pycache__/expression.cpython-39.pyc | Bin 0 -> 7377 bytes .../__pycache__/structures.cpython-39.pyc | Bin 0 -> 17377 bytes .../site-packages/_pytest/mark/expression.py | 221 + .../site-packages/_pytest/mark/structures.py | 559 ++ .../site-packages/_pytest/monkeypatch.py | 379 + .../python3.9/site-packages/_pytest/nodes.py | 591 ++ .../python3.9/site-packages/_pytest/nose.py | 39 + .../site-packages/_pytest/outcomes.py | 227 + .../site-packages/_pytest/pastebin.py | 110 + .../site-packages/_pytest/pathlib.py | 654 ++ .../python3.9/site-packages/_pytest/py.typed | 0 .../site-packages/_pytest/pytester.py | 1922 ++++ .../_pytest/pytester_assertions.py | 66 + .../python3.9/site-packages/_pytest/python.py | 1689 ++++ .../site-packages/_pytest/python_api.py | 786 ++ .../site-packages/_pytest/recwarn.py | 296 + .../site-packages/_pytest/reports.py | 572 ++ .../python3.9/site-packages/_pytest/runner.py | 462 + .../site-packages/_pytest/setuponly.py | 94 + .../site-packages/_pytest/setupplan.py | 40 + .../site-packages/_pytest/skipping.py | 324 + .../site-packages/_pytest/stepwise.py | 119 + .../python3.9/site-packages/_pytest/store.py | 125 + .../site-packages/_pytest/terminal.py | 1405 +++ .../site-packages/_pytest/threadexception.py | 90 + .../python3.9/site-packages/_pytest/timing.py | 12 + .../python3.9/site-packages/_pytest/tmpdir.py | 254 + .../site-packages/_pytest/unittest.py | 405 + .../_pytest/unraisableexception.py | 93 + .../site-packages/_pytest/warning_types.py | 132 + .../site-packages/_pytest/warnings.py | 139 + .../python3.9/site-packages/attr/__init__.py | 80 + .../python3.9/site-packages/attr/__init__.pyi | 484 + .../attr/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1750 bytes .../attr/__pycache__/_cmp.cpython-39.pyc | Bin 0 -> 3906 bytes .../attr/__pycache__/_compat.cpython-39.pyc | Bin 0 -> 6249 bytes .../attr/__pycache__/_config.cpython-39.pyc | Bin 0 -> 1131 bytes .../attr/__pycache__/_funcs.cpython-39.pyc | Bin 0 -> 10451 bytes .../attr/__pycache__/_make.cpython-39.pyc | Bin 0 -> 75845 bytes .../attr/__pycache__/_next_gen.cpython-39.pyc | Bin 0 -> 4866 bytes .../__pycache__/_version_info.cpython-39.pyc | Bin 0 -> 2454 bytes .../__pycache__/converters.cpython-39.pyc | Bin 0 -> 3840 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 3283 bytes .../attr/__pycache__/filters.cpython-39.pyc | Bin 0 -> 1779 bytes .../attr/__pycache__/setters.cpython-39.pyc | Bin 0 -> 1657 bytes .../__pycache__/validators.cpython-39.pyc | Bin 0 -> 15913 bytes .../lib/python3.9/site-packages/attr/_cmp.py | 154 + .../lib/python3.9/site-packages/attr/_cmp.pyi | 13 + .../python3.9/site-packages/attr/_compat.py | 261 + .../python3.9/site-packages/attr/_config.py | 33 + .../python3.9/site-packages/attr/_funcs.py | 422 + .../lib/python3.9/site-packages/attr/_make.py | 3173 +++++++ .../python3.9/site-packages/attr/_next_gen.py | 216 + .../site-packages/attr/_version_info.py | 87 + .../site-packages/attr/_version_info.pyi | 9 + .../site-packages/attr/converters.py | 155 + .../site-packages/attr/converters.pyi | 13 + .../site-packages/attr/exceptions.py | 94 + .../site-packages/attr/exceptions.pyi | 17 + .../python3.9/site-packages/attr/filters.py | 54 + .../python3.9/site-packages/attr/filters.pyi | 6 + .../lib/python3.9/site-packages/attr/py.typed | 0 .../python3.9/site-packages/attr/setters.py | 79 + .../python3.9/site-packages/attr/setters.pyi | 19 + .../site-packages/attr/validators.py | 561 ++ .../site-packages/attr/validators.pyi | 78 + .../attrs-21.4.0.dist-info/AUTHORS.rst | 11 + .../attrs-21.4.0.dist-info/INSTALLER | 1 + .../attrs-21.4.0.dist-info/LICENSE | 21 + .../attrs-21.4.0.dist-info/METADATA | 232 + .../attrs-21.4.0.dist-info/RECORD | 56 + .../attrs-21.4.0.dist-info/WHEEL | 6 + .../attrs-21.4.0.dist-info/top_level.txt | 2 + .../python3.9/site-packages/attrs/__init__.py | 70 + .../site-packages/attrs/__init__.pyi | 63 + .../attrs/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1110 bytes .../__pycache__/converters.cpython-39.pyc | Bin 0 -> 247 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 247 bytes .../attrs/__pycache__/filters.cpython-39.pyc | Bin 0 -> 241 bytes .../attrs/__pycache__/setters.cpython-39.pyc | Bin 0 -> 241 bytes .../__pycache__/validators.cpython-39.pyc | Bin 0 -> 247 bytes .../site-packages/attrs/converters.py | 3 + .../site-packages/attrs/exceptions.py | 3 + .../python3.9/site-packages/attrs/filters.py | 3 + .../python3.9/site-packages/attrs/py.typed | 0 .../python3.9/site-packages/attrs/setters.py | 3 + .../site-packages/attrs/validators.py | 3 + .../python3.9/site-packages/easy_install.py | 5 + .../iniconfig-1.1.1.dist-info/INSTALLER | 1 + .../iniconfig-1.1.1.dist-info/LICENSE | 19 + .../iniconfig-1.1.1.dist-info/METADATA | 78 + .../iniconfig-1.1.1.dist-info/RECORD | 10 + .../iniconfig-1.1.1.dist-info/WHEEL | 6 + .../iniconfig-1.1.1.dist-info/top_level.txt | 1 + .../site-packages/iniconfig/__init__.py | 165 + .../site-packages/iniconfig/__init__.pyi | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5271 bytes .../site-packages/iniconfig/py.typed | 0 .../packaging-21.3.dist-info/INSTALLER | 1 + .../packaging-21.3.dist-info/LICENSE | 3 + .../packaging-21.3.dist-info/LICENSE.APACHE | 177 + .../packaging-21.3.dist-info/LICENSE.BSD | 23 + .../packaging-21.3.dist-info/METADATA | 453 + .../packaging-21.3.dist-info/RECORD | 31 + .../packaging-21.3.dist-info/WHEEL | 5 + .../packaging-21.3.dist-info/top_level.txt | 1 + .../site-packages/packaging/__about__.py | 26 + .../site-packages/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 614 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 470 bytes .../__pycache__/_manylinux.cpython-39.pyc | Bin 0 -> 7318 bytes .../__pycache__/_musllinux.cpython-39.pyc | Bin 0 -> 4633 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2827 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9465 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 3986 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 21545 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 12276 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 3635 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 13176 bytes .../site-packages/packaging/_manylinux.py | 301 + .../site-packages/packaging/_musllinux.py | 136 + .../site-packages/packaging/_structures.py | 61 + .../site-packages/packaging/markers.py | 304 + .../site-packages/packaging/py.typed | 0 .../site-packages/packaging/requirements.py | 146 + .../site-packages/packaging/specifiers.py | 802 ++ .../python3.9/site-packages/packaging/tags.py | 487 + .../site-packages/packaging/utils.py | 136 + .../site-packages/packaging/version.py | 504 + .../pip-20.2.3.dist-info/INSTALLER | 1 + .../pip-20.2.3.dist-info/LICENSE.txt | 20 + .../pip-20.2.3.dist-info/METADATA | 88 + .../site-packages/pip-20.2.3.dist-info/RECORD | 752 ++ .../pip-20.2.3.dist-info/REQUESTED | 0 .../site-packages/pip-20.2.3.dist-info/WHEEL | 6 + .../pip-20.2.3.dist-info/entry_points.txt | 5 + .../pip-20.2.3.dist-info/top_level.txt | 1 + .../python3.9/site-packages/pip/__init__.py | 18 + .../python3.9/site-packages/pip/__main__.py | 26 + .../pip/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 701 bytes .../pip/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 545 bytes .../site-packages/pip/_internal/__init__.py | 17 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 750 bytes .../__pycache__/build_env.cpython-39.pyc | Bin 0 -> 7585 bytes .../__pycache__/cache.cpython-39.pyc | Bin 0 -> 9142 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 10933 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 14576 bytes .../__pycache__/locations.cpython-39.pyc | Bin 0 -> 4521 bytes .../_internal/__pycache__/main.cpython-39.pyc | Bin 0 -> 687 bytes .../__pycache__/pyproject.cpython-39.pyc | Bin 0 -> 3790 bytes .../self_outdated_check.cpython-39.pyc | Bin 0 -> 4621 bytes .../__pycache__/wheel_builder.cpython-39.pyc | Bin 0 -> 6883 bytes .../site-packages/pip/_internal/build_env.py | 241 + .../site-packages/pip/_internal/cache.py | 346 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 308 bytes .../__pycache__/autocompletion.cpython-39.pyc | Bin 0 -> 4987 bytes .../__pycache__/base_command.cpython-39.pyc | Bin 0 -> 6823 bytes .../cli/__pycache__/cmdoptions.cpython-39.pyc | Bin 0 -> 20837 bytes .../command_context.cpython-39.pyc | Bin 0 -> 1388 bytes .../cli/__pycache__/main.cpython-39.pyc | Bin 0 -> 1492 bytes .../__pycache__/main_parser.cpython-39.pyc | Bin 0 -> 2279 bytes .../cli/__pycache__/parser.cpython-39.pyc | Bin 0 -> 9024 bytes .../__pycache__/progress_bars.cpython-39.pyc | Bin 0 -> 7743 bytes .../__pycache__/req_command.cpython-39.pyc | Bin 0 -> 9950 bytes .../cli/__pycache__/spinners.cpython-39.pyc | Bin 0 -> 4837 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 0 -> 437 bytes .../pip/_internal/cli/autocompletion.py | 164 + .../pip/_internal/cli/base_command.py | 265 + .../pip/_internal/cli/cmdoptions.py | 975 ++ .../pip/_internal/cli/command_context.py | 36 + .../site-packages/pip/_internal/cli/main.py | 75 + .../pip/_internal/cli/main_parser.py | 99 + .../site-packages/pip/_internal/cli/parser.py | 266 + .../pip/_internal/cli/progress_bars.py | 280 + .../pip/_internal/cli/req_command.py | 402 + .../pip/_internal/cli/spinners.py | 173 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 122 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3008 bytes .../commands/__pycache__/cache.cpython-39.pyc | Bin 0 -> 4447 bytes .../commands/__pycache__/check.cpython-39.pyc | Bin 0 -> 1624 bytes .../__pycache__/completion.cpython-39.pyc | Bin 0 -> 3230 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 8180 bytes .../commands/__pycache__/debug.cpython-39.pyc | Bin 0 -> 6545 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 4123 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 3069 bytes .../commands/__pycache__/hash.cpython-39.pyc | Bin 0 -> 2191 bytes .../commands/__pycache__/help.cpython-39.pyc | Bin 0 -> 1417 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 17291 bytes .../commands/__pycache__/list.cpython-39.pyc | Bin 0 -> 8871 bytes .../__pycache__/search.cpython-39.pyc | Bin 0 -> 4935 bytes .../commands/__pycache__/show.cpython-39.pyc | Bin 0 -> 6466 bytes .../__pycache__/uninstall.cpython-39.pyc | Bin 0 -> 3002 bytes .../commands/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 5136 bytes .../pip/_internal/commands/cache.py | 182 + .../pip/_internal/commands/check.py | 51 + .../pip/_internal/commands/completion.py | 98 + .../pip/_internal/commands/configuration.py | 284 + .../pip/_internal/commands/debug.py | 229 + .../pip/_internal/commands/download.py | 143 + .../pip/_internal/commands/freeze.py | 103 + .../pip/_internal/commands/hash.py | 63 + .../pip/_internal/commands/help.py | 44 + .../pip/_internal/commands/install.py | 749 ++ .../pip/_internal/commands/list.py | 320 + .../pip/_internal/commands/search.py | 160 + .../pip/_internal/commands/show.py | 186 + .../pip/_internal/commands/uninstall.py | 95 + .../pip/_internal/commands/wheel.py | 188 + .../pip/_internal/configuration.py | 418 + .../pip/_internal/distributions/__init__.py | 24 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 884 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 2000 bytes .../__pycache__/installed.cpython-39.pyc | Bin 0 -> 1280 bytes .../__pycache__/sdist.cpython-39.pyc | Bin 0 -> 3559 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1624 bytes .../pip/_internal/distributions/base.py | 45 + .../pip/_internal/distributions/installed.py | 24 + .../pip/_internal/distributions/sdist.py | 104 + .../pip/_internal/distributions/wheel.py | 36 + .../site-packages/pip/_internal/exceptions.py | 381 + .../pip/_internal/index/__init__.py | 2 + .../index/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 262 bytes .../__pycache__/collector.cpython-39.pyc | Bin 0 -> 18597 bytes .../__pycache__/package_finder.cpython-39.pyc | Bin 0 -> 26015 bytes .../pip/_internal/index/collector.py | 692 ++ .../pip/_internal/index/package_finder.py | 1014 ++ .../site-packages/pip/_internal/locations.py | 194 + .../site-packages/pip/_internal/main.py | 16 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 296 bytes .../__pycache__/candidate.cpython-39.pyc | Bin 0 -> 1523 bytes .../__pycache__/direct_url.cpython-39.pyc | Bin 0 -> 6547 bytes .../__pycache__/format_control.cpython-39.pyc | Bin 0 -> 2781 bytes .../models/__pycache__/index.cpython-39.pyc | Bin 0 -> 1262 bytes .../models/__pycache__/link.cpython-39.pyc | Bin 0 -> 7192 bytes .../models/__pycache__/scheme.cpython-39.pyc | Bin 0 -> 984 bytes .../__pycache__/search_scope.cpython-39.pyc | Bin 0 -> 3477 bytes .../selection_prefs.cpython-39.pyc | Bin 0 -> 1694 bytes .../__pycache__/target_python.cpython-39.pyc | Bin 0 -> 3377 bytes .../models/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 3248 bytes .../pip/_internal/models/candidate.py | 38 + .../pip/_internal/models/direct_url.py | 245 + .../pip/_internal/models/format_control.py | 92 + .../pip/_internal/models/index.py | 34 + .../pip/_internal/models/link.py | 245 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 135 + .../pip/_internal/models/selection_prefs.py | 49 + .../pip/_internal/models/target_python.py | 120 + .../pip/_internal/models/wheel.py | 78 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 284 bytes .../network/__pycache__/auth.cpython-39.pyc | Bin 0 -> 7136 bytes .../network/__pycache__/cache.cpython-39.pyc | Bin 0 -> 2860 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 4473 bytes .../__pycache__/lazy_wheel.cpython-39.pyc | Bin 0 -> 8100 bytes .../__pycache__/session.cpython-39.pyc | Bin 0 -> 9263 bytes .../network/__pycache__/utils.cpython-39.pyc | Bin 0 -> 1437 bytes .../network/__pycache__/xmlrpc.cpython-39.pyc | Bin 0 -> 1899 bytes .../pip/_internal/network/auth.py | 310 + .../pip/_internal/network/cache.py | 79 + .../pip/_internal/network/download.py | 182 + .../pip/_internal/network/lazy_wheel.py | 235 + .../pip/_internal/network/session.py | 421 + .../pip/_internal/network/utils.py | 97 + .../pip/_internal/network/xmlrpc.py | 52 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 232 bytes .../__pycache__/check.cpython-39.pyc | Bin 0 -> 3641 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 5974 bytes .../__pycache__/prepare.cpython-39.pyc | Bin 0 -> 11577 bytes .../_internal/operations/build/__init__.py | 0 .../build/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 238 bytes .../build/__pycache__/metadata.cpython-39.pyc | Bin 0 -> 1259 bytes .../metadata_legacy.cpython-39.pyc | Bin 0 -> 2035 bytes .../build/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1376 bytes .../__pycache__/wheel_legacy.cpython-39.pyc | Bin 0 -> 2658 bytes .../_internal/operations/build/metadata.py | 37 + .../operations/build/metadata_legacy.py | 77 + .../pip/_internal/operations/build/wheel.py | 46 + .../operations/build/wheel_legacy.py | 115 + .../pip/_internal/operations/check.py | 158 + .../pip/_internal/operations/freeze.py | 272 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 296 bytes .../editable_legacy.cpython-39.pyc | Bin 0 -> 1414 bytes .../install/__pycache__/legacy.cpython-39.pyc | Bin 0 -> 3304 bytes .../install/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 21288 bytes .../operations/install/editable_legacy.py | 52 + .../_internal/operations/install/legacy.py | 130 + .../pip/_internal/operations/install/wheel.py | 861 ++ .../pip/_internal/operations/prepare.py | 562 ++ .../site-packages/pip/_internal/pyproject.py | 196 + .../pip/_internal/req/__init__.py | 103 + .../req/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2533 bytes .../__pycache__/constructors.cpython-39.pyc | Bin 0 -> 11306 bytes .../req/__pycache__/req_file.cpython-39.pyc | Bin 0 -> 13249 bytes .../__pycache__/req_install.cpython-39.pyc | Bin 0 -> 21458 bytes .../req/__pycache__/req_set.cpython-39.pyc | Bin 0 -> 5852 bytes .../__pycache__/req_tracker.cpython-39.pyc | Bin 0 -> 4274 bytes .../__pycache__/req_uninstall.cpython-39.pyc | Bin 0 -> 17602 bytes .../pip/_internal/req/constructors.py | 486 + .../pip/_internal/req/req_file.py | 592 ++ .../pip/_internal/req/req_install.py | 905 ++ .../pip/_internal/req/req_set.py | 203 + .../pip/_internal/req/req_tracker.py | 150 + .../pip/_internal/req/req_uninstall.py | 648 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 232 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 1064 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 239 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 11816 bytes .../_internal/resolution/legacy/resolver.py | 485 + .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 243 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 3154 bytes .../__pycache__/candidates.cpython-39.pyc | Bin 0 -> 17845 bytes .../__pycache__/factory.cpython-39.pyc | Bin 0 -> 10862 bytes .../__pycache__/provider.cpython-39.pyc | Bin 0 -> 4045 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 5081 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 7142 bytes .../_internal/resolution/resolvelib/base.py | 82 + .../resolution/resolvelib/candidates.py | 600 ++ .../resolution/resolvelib/factory.py | 459 + .../resolution/resolvelib/provider.py | 153 + .../resolution/resolvelib/requirements.py | 137 + .../resolution/resolvelib/resolver.py | 259 + .../pip/_internal/self_outdated_check.py | 205 + .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 227 bytes .../utils/__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 1417 bytes .../utils/__pycache__/compat.cpython-39.pyc | Bin 0 -> 6052 bytes .../compatibility_tags.cpython-39.pyc | Bin 0 -> 3611 bytes .../utils/__pycache__/datetime.cpython-39.pyc | Bin 0 -> 548 bytes .../__pycache__/deprecation.cpython-39.pyc | Bin 0 -> 2874 bytes .../direct_url_helpers.cpython-39.pyc | Bin 0 -> 2719 bytes .../__pycache__/distutils_args.cpython-39.pyc | Bin 0 -> 1167 bytes .../utils/__pycache__/encoding.cpython-39.pyc | Bin 0 -> 1347 bytes .../__pycache__/entrypoints.cpython-39.pyc | Bin 0 -> 1371 bytes .../__pycache__/filesystem.cpython-39.pyc | Bin 0 -> 5708 bytes .../__pycache__/filetypes.cpython-39.pyc | Bin 0 -> 628 bytes .../utils/__pycache__/glibc.cpython-39.pyc | Bin 0 -> 1774 bytes .../utils/__pycache__/hashes.cpython-39.pyc | Bin 0 -> 4546 bytes .../inject_securetransport.cpython-39.pyc | Bin 0 -> 1000 bytes .../utils/__pycache__/logging.cpython-39.pyc | Bin 0 -> 9267 bytes .../utils/__pycache__/misc.cpython-39.pyc | Bin 0 -> 25097 bytes .../utils/__pycache__/models.cpython-39.pyc | Bin 0 -> 2027 bytes .../__pycache__/packaging.cpython-39.pyc | Bin 0 -> 2680 bytes .../utils/__pycache__/parallel.cpython-39.pyc | Bin 0 -> 3245 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 0 -> 1896 bytes .../setuptools_build.cpython-39.pyc | Bin 0 -> 2973 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 0 -> 5736 bytes .../utils/__pycache__/temp_dir.cpython-39.pyc | Bin 0 -> 7166 bytes .../utils/__pycache__/typing.cpython-39.pyc | Bin 0 -> 1509 bytes .../__pycache__/unpacking.cpython-39.pyc | Bin 0 -> 6671 bytes .../utils/__pycache__/urls.cpython-39.pyc | Bin 0 -> 1571 bytes .../__pycache__/virtualenv.cpython-39.pyc | Bin 0 -> 3404 bytes .../utils/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 6383 bytes .../pip/_internal/utils/appdirs.py | 44 + .../pip/_internal/utils/compat.py | 271 + .../pip/_internal/utils/compatibility_tags.py | 166 + .../pip/_internal/utils/datetime.py | 14 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 130 + .../pip/_internal/utils/distutils_args.py | 48 + .../pip/_internal/utils/encoding.py | 41 + .../pip/_internal/utils/entrypoints.py | 31 + .../pip/_internal/utils/filesystem.py | 224 + .../pip/_internal/utils/filetypes.py | 16 + .../pip/_internal/utils/glibc.py | 98 + .../pip/_internal/utils/hashes.py | 145 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 399 + .../site-packages/pip/_internal/utils/misc.py | 959 ++ .../pip/_internal/utils/models.py | 44 + .../pip/_internal/utils/packaging.py | 94 + .../pip/_internal/utils/parallel.py | 107 + .../pip/_internal/utils/pkg_resources.py | 44 + .../pip/_internal/utils/setuptools_build.py | 181 + .../pip/_internal/utils/subprocess.py | 280 + .../pip/_internal/utils/temp_dir.py | 274 + .../pip/_internal/utils/typing.py | 38 + .../pip/_internal/utils/unpacking.py | 281 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 119 + .../pip/_internal/utils/wheel.py | 225 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 520 bytes .../vcs/__pycache__/bazaar.cpython-39.pyc | Bin 0 -> 3784 bytes .../vcs/__pycache__/git.cpython-39.pyc | Bin 0 -> 9603 bytes .../vcs/__pycache__/mercurial.cpython-39.pyc | Bin 0 -> 5092 bytes .../vcs/__pycache__/subversion.cpython-39.pyc | Bin 0 -> 8571 bytes .../__pycache__/versioncontrol.cpython-39.pyc | Bin 0 -> 21075 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 119 + .../site-packages/pip/_internal/vcs/git.py | 397 + .../pip/_internal/vcs/mercurial.py | 158 + .../pip/_internal/vcs/subversion.py | 336 + .../pip/_internal/vcs/versioncontrol.py | 811 ++ .../pip/_internal/wheel_builder.py | 308 + .../site-packages/pip/_vendor/__init__.py | 110 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3007 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 21435 bytes .../__pycache__/contextlib2.cpython-39.pyc | Bin 0 -> 15611 bytes .../_vendor/__pycache__/distro.cpython-39.pyc | Bin 0 -> 36914 bytes .../__pycache__/ipaddress.cpython-39.pyc | Bin 0 -> 64715 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 240471 bytes .../__pycache__/retrying.cpython-39.pyc | Bin 0 -> 8082 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 26981 bytes .../site-packages/pip/_vendor/appdirs.py | 633 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 585 bytes .../__pycache__/_cmd.cpython-39.pyc | Bin 0 -> 1606 bytes .../__pycache__/adapter.cpython-39.pyc | Bin 0 -> 3111 bytes .../__pycache__/cache.cpython-39.pyc | Bin 0 -> 1858 bytes .../__pycache__/compat.cpython-39.pyc | Bin 0 -> 782 bytes .../__pycache__/controller.cpython-39.pyc | Bin 0 -> 7799 bytes .../__pycache__/filewrapper.cpython-39.pyc | Bin 0 -> 2207 bytes .../__pycache__/heuristics.cpython-39.pyc | Bin 0 -> 4739 bytes .../__pycache__/serialize.cpython-39.pyc | Bin 0 -> 4258 bytes .../__pycache__/wrapper.cpython-39.pyc | Bin 0 -> 709 bytes .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 329 bytes .../__pycache__/file_cache.cpython-39.pyc | Bin 0 -> 3345 bytes .../__pycache__/redis_cache.cpython-39.pyc | Bin 0 -> 1601 bytes .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 376 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 188 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 311 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 488 bytes .../certifi/__pycache__/core.cpython-39.pyc | Bin 0 -> 1218 bytes .../pip/_vendor/certifi/cacert.pem | 4620 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 60 + .../pip/_vendor/chardet/__init__.py | 39 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 885 bytes .../__pycache__/big5freq.cpython-39.pyc | Bin 0 -> 27214 bytes .../__pycache__/big5prober.cpython-39.pyc | Bin 0 -> 1169 bytes .../chardistribution.cpython-39.pyc | Bin 0 -> 6255 bytes .../charsetgroupprober.cpython-39.pyc | Bin 0 -> 2286 bytes .../__pycache__/charsetprober.cpython-39.pyc | Bin 0 -> 3518 bytes .../codingstatemachine.cpython-39.pyc | Bin 0 -> 2945 bytes .../chardet/__pycache__/compat.cpython-39.pyc | Bin 0 -> 390 bytes .../__pycache__/cp949prober.cpython-39.pyc | Bin 0 -> 1176 bytes .../chardet/__pycache__/enums.cpython-39.pyc | Bin 0 -> 2683 bytes .../__pycache__/escprober.cpython-39.pyc | Bin 0 -> 2668 bytes .../chardet/__pycache__/escsm.cpython-39.pyc | Bin 0 -> 7117 bytes .../__pycache__/eucjpprober.cpython-39.pyc | Bin 0 -> 2482 bytes .../__pycache__/euckrfreq.cpython-39.pyc | Bin 0 -> 12098 bytes .../__pycache__/euckrprober.cpython-39.pyc | Bin 0 -> 1177 bytes .../__pycache__/euctwfreq.cpython-39.pyc | Bin 0 -> 27218 bytes .../__pycache__/euctwprober.cpython-39.pyc | Bin 0 -> 1177 bytes .../__pycache__/gb2312freq.cpython-39.pyc | Bin 0 -> 19142 bytes .../__pycache__/gb2312prober.cpython-39.pyc | Bin 0 -> 1185 bytes .../__pycache__/hebrewprober.cpython-39.pyc | Bin 0 -> 3054 bytes .../__pycache__/jisfreq.cpython-39.pyc | Bin 0 -> 22170 bytes .../chardet/__pycache__/jpcntx.cpython-39.pyc | Bin 0 -> 37643 bytes .../langbulgarianmodel.cpython-39.pyc | Bin 0 -> 23667 bytes .../langcyrillicmodel.cpython-39.pyc | Bin 0 -> 29131 bytes .../__pycache__/langgreekmodel.cpython-39.pyc | Bin 0 -> 23625 bytes .../langhebrewmodel.cpython-39.pyc | Bin 0 -> 22252 bytes .../langhungarianmodel.cpython-39.pyc | Bin 0 -> 23656 bytes .../__pycache__/langthaimodel.cpython-39.pyc | Bin 0 -> 22231 bytes .../langturkishmodel.cpython-39.pyc | Bin 0 -> 22254 bytes .../__pycache__/latin1prober.cpython-39.pyc | Bin 0 -> 2990 bytes .../mbcharsetprober.cpython-39.pyc | Bin 0 -> 2297 bytes .../mbcsgroupprober.cpython-39.pyc | Bin 0 -> 1166 bytes .../chardet/__pycache__/mbcssm.cpython-39.pyc | Bin 0 -> 15753 bytes .../sbcharsetprober.cpython-39.pyc | Bin 0 -> 3052 bytes .../sbcsgroupprober.cpython-39.pyc | Bin 0 -> 1662 bytes .../__pycache__/sjisprober.cpython-39.pyc | Bin 0 -> 2518 bytes .../universaldetector.cpython-39.pyc | Bin 0 -> 5866 bytes .../__pycache__/utf8prober.cpython-39.pyc | Bin 0 -> 2027 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 474 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 231 bytes .../cli/__pycache__/chardetect.cpython-39.pyc | Bin 0 -> 2730 bytes .../pip/_vendor/chardet/cli/chardetect.py | 85 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 34 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 228 + .../pip/_vendor/chardet/langcyrillicmodel.py | 333 + .../pip/_vendor/chardet/langgreekmodel.py | 225 + .../pip/_vendor/chardet/langhebrewmodel.py | 200 + .../pip/_vendor/chardet/langhungarianmodel.py | 225 + .../pip/_vendor/chardet/langthaimodel.py | 199 + .../pip/_vendor/chardet/langturkishmodel.py | 193 + .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/sbcharsetprober.py | 132 + .../pip/_vendor/chardet/sbcsgroupprober.py | 73 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 479 bytes .../colorama/__pycache__/ansi.cpython-39.pyc | Bin 0 -> 3264 bytes .../__pycache__/ansitowin32.cpython-39.pyc | Bin 0 -> 7695 bytes .../__pycache__/initialise.cpython-39.pyc | Bin 0 -> 1746 bytes .../colorama/__pycache__/win32.cpython-39.pyc | Bin 0 -> 3978 bytes .../__pycache__/winterm.cpython-39.pyc | Bin 0 -> 4700 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 257 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../site-packages/pip/_vendor/contextlib2.py | 518 + .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1092 bytes .../distlib/__pycache__/compat.cpython-39.pyc | Bin 0 -> 32194 bytes .../__pycache__/database.cpython-39.pyc | Bin 0 -> 42516 bytes .../distlib/__pycache__/index.cpython-39.pyc | Bin 0 -> 17537 bytes .../__pycache__/locators.cpython-39.pyc | Bin 0 -> 38525 bytes .../__pycache__/manifest.cpython-39.pyc | Bin 0 -> 10228 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 4519 bytes .../__pycache__/metadata.cpython-39.pyc | Bin 0 -> 26445 bytes .../__pycache__/resources.cpython-39.pyc | Bin 0 -> 11058 bytes .../__pycache__/scripts.cpython-39.pyc | Bin 0 -> 10966 bytes .../distlib/__pycache__/util.cpython-39.pyc | Bin 0 -> 48238 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 20343 bytes .../distlib/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 26427 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 519 bytes .../_backport/__pycache__/misc.cpython-39.pyc | Bin 0 -> 1139 bytes .../__pycache__/shutil.cpython-39.pyc | Bin 0 -> 21713 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 0 -> 16003 bytes .../__pycache__/tarfile.cpython-39.pyc | Bin 0 -> 62767 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 764 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 786 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 +++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 516 + .../pip/_vendor/distlib/locators.py | 1302 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1056 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 419 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 96768 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 105984 bytes .../site-packages/pip/_vendor/distlib/util.py | 1761 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 90112 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1018 ++ .../site-packages/pip/_vendor/distro.py | 1230 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1338 bytes .../__pycache__/_ihatexml.cpython-39.pyc | Bin 0 -> 13807 bytes .../__pycache__/_inputstream.cpython-39.pyc | Bin 0 -> 21666 bytes .../__pycache__/_tokenizer.cpython-39.pyc | Bin 0 -> 39761 bytes .../__pycache__/_utils.cpython-39.pyc | Bin 0 -> 4838 bytes .../__pycache__/constants.cpython-39.pyc | Bin 0 -> 66376 bytes .../__pycache__/html5parser.cpython-39.pyc | Bin 0 -> 91047 bytes .../__pycache__/serializer.cpython-39.pyc | Bin 0 -> 10849 bytes .../pip/_vendor/html5lib/_ihatexml.py | 289 + .../pip/_vendor/html5lib/_inputstream.py | 918 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1735 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 5 + .../_trie/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 388 bytes .../_trie/__pycache__/_base.cpython-39.pyc | Bin 0 -> 1632 bytes .../_trie/__pycache__/py.cpython-39.pyc | Bin 0 -> 2293 bytes .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 159 + .../pip/_vendor/html5lib/constants.py | 2946 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 236 bytes .../alphabeticalattributes.cpython-39.pyc | Bin 0 -> 1358 bytes .../filters/__pycache__/base.cpython-39.pyc | Bin 0 -> 906 bytes .../inject_meta_charset.cpython-39.pyc | Bin 0 -> 1912 bytes .../filters/__pycache__/lint.cpython-39.pyc | Bin 0 -> 2654 bytes .../__pycache__/optionaltags.cpython-39.pyc | Bin 0 -> 2799 bytes .../__pycache__/sanitizer.cpython-39.pyc | Bin 0 -> 16922 bytes .../__pycache__/whitespace.cpython-39.pyc | Bin 0 -> 1404 bytes .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 916 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2795 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 973 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 0 -> 1581 bytes .../__pycache__/sax.cpython-39.pyc | Bin 0 -> 1500 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3368 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 11352 bytes .../__pycache__/dom.cpython-39.pyc | Bin 0 -> 9489 bytes .../__pycache__/etree.cpython-39.pyc | Bin 0 -> 11857 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 0 -> 13040 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4034 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 7033 bytes .../__pycache__/dom.cpython-39.pyc | Bin 0 -> 1768 bytes .../__pycache__/etree.cpython-39.pyc | Bin 0 -> 3530 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 0 -> 6667 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 0 -> 1924 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../idna/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 293 bytes .../idna/__pycache__/codec.cpython-39.pyc | Bin 0 -> 2947 bytes .../idna/__pycache__/compat.cpython-39.pyc | Bin 0 -> 665 bytes .../idna/__pycache__/core.cpython-39.pyc | Bin 0 -> 9213 bytes .../idna/__pycache__/idnadata.cpython-39.pyc | Bin 0 -> 22174 bytes .../idna/__pycache__/intranges.cpython-39.pyc | Bin 0 -> 1845 bytes .../__pycache__/package_data.cpython-39.pyc | Bin 0 -> 248 bytes .../idna/__pycache__/uts46data.cpython-39.pyc | Bin 0 -> 146182 bytes .../site-packages/pip/_vendor/idna/codec.py | 118 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 400 + .../pip/_vendor/idna/idnadata.py | 2050 ++++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8357 +++++++++++++++++ .../site-packages/pip/_vendor/ipaddress.py | 2420 +++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1450 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 254 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 1888 bytes .../msgpack/__pycache__/ext.cpython-39.pyc | Bin 0 -> 6283 bytes .../__pycache__/fallback.cpython-39.pyc | Bin 0 -> 25948 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 191 + .../pip/_vendor/msgpack/fallback.py | 1063 +++ .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 735 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 581 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1179 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2933 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 0 -> 1536 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9331 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4130 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 20616 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 17295 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1686 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 13353 bytes .../pip/_vendor/packaging/_compat.py | 38 + .../pip/_vendor/packaging/_structures.py | 86 + .../pip/_vendor/packaging/_typing.py | 48 + .../pip/_vendor/packaging/markers.py | 328 + .../pip/_vendor/packaging/requirements.py | 145 + .../pip/_vendor/packaging/specifiers.py | 863 ++ .../pip/_vendor/packaging/tags.py | 751 ++ .../pip/_vendor/packaging/utils.py | 65 + .../pip/_vendor/packaging/version.py | 535 ++ .../pip/_vendor/pep517/__init__.py | 4 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 318 bytes .../__pycache__/_in_process.cpython-39.pyc | Bin 0 -> 8302 bytes .../pep517/__pycache__/build.cpython-39.pyc | Bin 0 -> 3489 bytes .../pep517/__pycache__/check.cpython-39.pyc | Bin 0 -> 5028 bytes .../__pycache__/colorlog.cpython-39.pyc | Bin 0 -> 2974 bytes .../pep517/__pycache__/compat.cpython-39.pyc | Bin 0 -> 1151 bytes .../__pycache__/dirtools.cpython-39.pyc | Bin 0 -> 1383 bytes .../__pycache__/envbuild.cpython-39.pyc | Bin 0 -> 4516 bytes .../pep517/__pycache__/meta.cpython-39.pyc | Bin 0 -> 2956 bytes .../__pycache__/wrappers.cpython-39.pyc | Bin 0 -> 10630 bytes .../pip/_vendor/pep517/_in_process.py | 280 + .../site-packages/pip/_vendor/pep517/build.py | 124 + .../site-packages/pip/_vendor/pep517/check.py | 203 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 34 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 167 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 308 + .../pip/_vendor/pkg_resources/__init__.py | 3296 +++++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 100367 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 0 -> 686 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5693 bytes .../progress/__pycache__/bar.cpython-39.pyc | Bin 0 -> 2675 bytes .../__pycache__/counter.cpython-39.pyc | Bin 0 -> 1509 bytes .../__pycache__/spinner.cpython-39.pyc | Bin 0 -> 1426 bytes .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 7107 ++++++++++++++ .../pip/_vendor/requests/__init__.py | 144 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3679 bytes .../__pycache__/__version__.cpython-39.pyc | Bin 0 -> 591 bytes .../_internal_utils.cpython-39.pyc | Bin 0 -> 1338 bytes .../__pycache__/adapters.cpython-39.pyc | Bin 0 -> 17009 bytes .../requests/__pycache__/api.cpython-39.pyc | Bin 0 -> 6795 bytes .../requests/__pycache__/auth.cpython-39.pyc | Bin 0 -> 8367 bytes .../requests/__pycache__/certs.cpython-39.pyc | Bin 0 -> 669 bytes .../__pycache__/compat.cpython-39.pyc | Bin 0 -> 1648 bytes .../__pycache__/cookies.cpython-39.pyc | Bin 0 -> 18858 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 5281 bytes .../requests/__pycache__/help.cpython-39.pyc | Bin 0 -> 2752 bytes .../requests/__pycache__/hooks.cpython-39.pyc | Bin 0 -> 1026 bytes .../__pycache__/models.cpython-39.pyc | Bin 0 -> 23889 bytes .../__pycache__/packages.cpython-39.pyc | Bin 0 -> 538 bytes .../__pycache__/sessions.cpython-39.pyc | Bin 0 -> 19503 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 0 -> 4275 bytes .../__pycache__/structures.cpython-39.pyc | Bin 0 -> 4496 bytes .../requests/__pycache__/utils.cpython-39.pyc | Bin 0 -> 22383 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 ++ .../site-packages/pip/_vendor/requests/api.py | 161 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 76 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 123 + .../pip/_vendor/requests/help.py | 119 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 954 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 769 ++ .../pip/_vendor/requests/status_codes.py | 123 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 982 ++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 642 bytes .../__pycache__/providers.cpython-39.pyc | Bin 0 -> 5481 bytes .../__pycache__/reporters.cpython-39.pyc | Bin 0 -> 2379 bytes .../__pycache__/resolvers.cpython-39.pyc | Bin 0 -> 14673 bytes .../__pycache__/structs.cpython-39.pyc | Bin 0 -> 3142 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 237 bytes .../collections_abc.cpython-39.pyc | Bin 0 -> 382 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 109 + .../pip/_vendor/resolvelib/reporters.py | 42 + .../pip/_vendor/resolvelib/resolvers.py | 428 + .../pip/_vendor/resolvelib/structs.py | 68 + .../site-packages/pip/_vendor/retrying.py | 267 + .../site-packages/pip/_vendor/six.py | 982 ++ .../pip/_vendor/toml/__init__.py | 25 + .../toml/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 768 bytes .../toml/__pycache__/common.cpython-39.pyc | Bin 0 -> 384 bytes .../toml/__pycache__/decoder.cpython-39.pyc | Bin 0 -> 23145 bytes .../toml/__pycache__/encoder.cpython-39.pyc | Bin 0 -> 9431 bytes .../toml/__pycache__/ordered.cpython-39.pyc | Bin 0 -> 1007 bytes .../toml/__pycache__/tz.cpython-39.pyc | Bin 0 -> 1157 bytes .../site-packages/pip/_vendor/toml/common.py | 6 + .../site-packages/pip/_vendor/toml/decoder.py | 1052 +++ .../site-packages/pip/_vendor/toml/encoder.py | 304 + .../site-packages/pip/_vendor/toml/ordered.py | 15 + .../site-packages/pip/_vendor/toml/tz.py | 21 + .../pip/_vendor/urllib3/__init__.py | 86 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2157 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 0 -> 10793 bytes .../__pycache__/connection.cpython-39.pyc | Bin 0 -> 10390 bytes .../__pycache__/connectionpool.cpython-39.pyc | Bin 0 -> 24097 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 10807 bytes .../urllib3/__pycache__/fields.cpython-39.pyc | Bin 0 -> 8149 bytes .../__pycache__/filepost.cpython-39.pyc | Bin 0 -> 2798 bytes .../__pycache__/poolmanager.cpython-39.pyc | Bin 0 -> 13678 bytes .../__pycache__/request.cpython-39.pyc | Bin 0 -> 5693 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 20937 bytes .../pip/_vendor/urllib3/_collections.py | 336 + .../pip/_vendor/urllib3/connection.py | 423 + .../pip/_vendor/urllib3/connectionpool.py | 1033 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 235 bytes .../_appengine_environ.cpython-39.pyc | Bin 0 -> 1455 bytes .../__pycache__/appengine.cpython-39.pyc | Bin 0 -> 8308 bytes .../__pycache__/ntlmpool.cpython-39.pyc | Bin 0 -> 3300 bytes .../__pycache__/pyopenssl.cpython-39.pyc | Bin 0 -> 15143 bytes .../securetransport.cpython-39.pyc | Bin 0 -> 20167 bytes .../contrib/__pycache__/socks.cpython-39.pyc | Bin 0 -> 5617 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 252 bytes .../__pycache__/bindings.cpython-39.pyc | Bin 0 -> 10251 bytes .../__pycache__/low_level.cpython-39.pyc | Bin 0 -> 7648 bytes .../contrib/_securetransport/bindings.py | 493 + .../contrib/_securetransport/low_level.py | 328 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 121 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 501 + .../urllib3/contrib/securetransport.py | 864 ++ .../pip/_vendor/urllib3/contrib/socks.py | 210 + .../pip/_vendor/urllib3/exceptions.py | 272 + .../pip/_vendor/urllib3/fields.py | 273 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 349 bytes .../packages/__pycache__/six.cpython-39.pyc | Bin 0 -> 26553 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 246 bytes .../__pycache__/makefile.cpython-39.pyc | Bin 0 -> 1344 bytes .../urllib3/packages/backports/makefile.py | 52 + .../pip/_vendor/urllib3/packages/six.py | 1021 ++ .../packages/ssl_match_hostname/__init__.py | 19 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 586 bytes .../_implementation.cpython-39.pyc | Bin 0 -> 3363 bytes .../ssl_match_hostname/_implementation.py | 160 + .../pip/_vendor/urllib3/poolmanager.py | 492 + .../pip/_vendor/urllib3/request.py | 171 + .../pip/_vendor/urllib3/response.py | 821 ++ .../pip/_vendor/urllib3/util/__init__.py | 46 + .../util/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1055 bytes .../__pycache__/connection.cpython-39.pyc | Bin 0 -> 3226 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 0 -> 1100 bytes .../util/__pycache__/request.cpython-39.pyc | Bin 0 -> 3388 bytes .../util/__pycache__/response.cpython-39.pyc | Bin 0 -> 2015 bytes .../util/__pycache__/retry.cpython-39.pyc | Bin 0 -> 13065 bytes .../util/__pycache__/ssl_.cpython-39.pyc | Bin 0 -> 10125 bytes .../util/__pycache__/timeout.cpython-39.pyc | Bin 0 -> 8928 bytes .../util/__pycache__/url.cpython-39.pyc | Bin 0 -> 10659 bytes .../util/__pycache__/wait.cpython-39.pyc | Bin 0 -> 3170 bytes .../pip/_vendor/urllib3/util/connection.py | 138 + .../pip/_vendor/urllib3/util/queue.py | 21 + .../pip/_vendor/urllib3/util/request.py | 135 + .../pip/_vendor/urllib3/util/response.py | 86 + .../pip/_vendor/urllib3/util/retry.py | 453 + .../pip/_vendor/urllib3/util/ssl_.py | 414 + .../pip/_vendor/urllib3/util/timeout.py | 261 + .../pip/_vendor/urllib3/util/url.py | 430 + .../pip/_vendor/urllib3/util/wait.py | 153 + .../site-packages/pip/_vendor/vendor.txt | 24 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 9759 bytes .../__pycache__/labels.cpython-39.pyc | Bin 0 -> 3873 bytes .../__pycache__/mklabels.cpython-39.pyc | Bin 0 -> 1943 bytes .../__pycache__/tests.cpython-39.pyc | Bin 0 -> 5107 bytes .../__pycache__/x_user_defined.cpython-39.pyc | Bin 0 -> 2703 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../site-packages/pkg_resources/__init__.py | 3302 +++++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 100523 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 229 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 20546 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201382 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 24512 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 27 + .../_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 753 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 591 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1053 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2835 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 8977 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4072 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 19787 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 10865 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1500 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 12120 bytes .../_vendor/packaging/_compat.py | 31 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 296 + .../_vendor/packaging/requirements.py | 138 + .../_vendor/packaging/specifiers.py | 749 ++ .../pkg_resources/_vendor/packaging/tags.py | 404 + .../pkg_resources/_vendor/packaging/utils.py | 57 + .../_vendor/packaging/version.py | 420 + .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++ .../pkg_resources/_vendor/six.py | 868 ++ .../pkg_resources/extern/__init__.py | 66 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2428 bytes .../pluggy-1.0.0.dist-info/INSTALLER | 1 + .../pluggy-1.0.0.dist-info/LICENSE | 21 + .../pluggy-1.0.0.dist-info/METADATA | 143 + .../pluggy-1.0.0.dist-info/RECORD | 20 + .../pluggy-1.0.0.dist-info/WHEEL | 6 + .../pluggy-1.0.0.dist-info/top_level.txt | 1 + .../site-packages/pluggy/__init__.py | 18 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 563 bytes .../__pycache__/_callers.cpython-39.pyc | Bin 0 -> 1697 bytes .../pluggy/__pycache__/_hooks.cpython-39.pyc | Bin 0 -> 11072 bytes .../__pycache__/_manager.cpython-39.pyc | Bin 0 -> 13928 bytes .../pluggy/__pycache__/_result.cpython-39.pyc | Bin 0 -> 2233 bytes .../__pycache__/_tracing.cpython-39.pyc | Bin 0 -> 2367 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 269 bytes .../site-packages/pluggy/_callers.py | 60 + .../python3.9/site-packages/pluggy/_hooks.py | 325 + .../site-packages/pluggy/_manager.py | 373 + .../python3.9/site-packages/pluggy/_result.py | 60 + .../site-packages/pluggy/_tracing.py | 62 + .../site-packages/pluggy/_version.py | 5 + .../py-1.11.0.dist-info/INSTALLER | 1 + .../site-packages/py-1.11.0.dist-info/LICENSE | 19 + .../py-1.11.0.dist-info/METADATA | 69 + .../site-packages/py-1.11.0.dist-info/RECORD | 101 + .../site-packages/py-1.11.0.dist-info/WHEEL | 6 + .../py-1.11.0.dist-info/top_level.txt | 1 + .../python3.9/site-packages/py/__init__.py | 156 + .../python3.9/site-packages/py/__init__.pyi | 20 + .../python3.9/site-packages/py/__metainfo.py | 2 + .../py/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4167 bytes .../py/__pycache__/__metainfo.cpython-39.pyc | Bin 0 -> 285 bytes .../py/__pycache__/_builtin.cpython-39.pyc | Bin 0 -> 4123 bytes .../py/__pycache__/_error.cpython-39.pyc | Bin 0 -> 2763 bytes .../py/__pycache__/_std.cpython-39.pyc | Bin 0 -> 1218 bytes .../py/__pycache__/_version.cpython-39.pyc | Bin 0 -> 266 bytes .../py/__pycache__/_xmlgen.cpython-39.pyc | Bin 0 -> 10320 bytes .../py/__pycache__/test.cpython-39.pyc | Bin 0 -> 347 bytes .../python3.9/site-packages/py/_builtin.py | 149 + .../site-packages/py/_code/__init__.py | 1 + .../_code/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 267 bytes .../__pycache__/_assertionnew.cpython-39.pyc | Bin 0 -> 9474 bytes .../__pycache__/_assertionold.cpython-39.pyc | Bin 0 -> 16354 bytes .../__pycache__/_py2traceback.cpython-39.pyc | Bin 0 -> 2175 bytes .../__pycache__/assertion.cpython-39.pyc | Bin 0 -> 2590 bytes .../py/_code/__pycache__/code.cpython-39.pyc | Bin 0 -> 26478 bytes .../_code/__pycache__/source.cpython-39.pyc | Bin 0 -> 11907 bytes .../site-packages/py/_code/_assertionnew.py | 322 + .../site-packages/py/_code/_assertionold.py | 556 ++ .../site-packages/py/_code/_py2traceback.py | 79 + .../site-packages/py/_code/assertion.py | 90 + .../python3.9/site-packages/py/_code/code.py | 796 ++ .../site-packages/py/_code/source.py | 410 + .../lib/python3.9/site-packages/py/_error.py | 91 + .../site-packages/py/_io/__init__.py | 1 + .../_io/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 248 bytes .../py/_io/__pycache__/capture.cpython-39.pyc | Bin 0 -> 11608 bytes .../_io/__pycache__/saferepr.cpython-39.pyc | Bin 0 -> 2727 bytes .../__pycache__/terminalwriter.cpython-39.pyc | Bin 0 -> 11296 bytes .../python3.9/site-packages/py/_io/capture.py | 371 + .../site-packages/py/_io/saferepr.py | 71 + .../site-packages/py/_io/terminalwriter.py | 423 + .../site-packages/py/_log/__init__.py | 2 + .../_log/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 293 bytes .../py/_log/__pycache__/log.cpython-39.pyc | Bin 0 -> 7478 bytes .../_log/__pycache__/warning.cpython-39.pyc | Bin 0 -> 2316 bytes .../python3.9/site-packages/py/_log/log.py | 206 + .../site-packages/py/_log/warning.py | 79 + .../site-packages/py/_path/__init__.py | 1 + .../_path/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 253 bytes .../__pycache__/cacheutil.cpython-39.pyc | Bin 0 -> 4782 bytes .../_path/__pycache__/common.cpython-39.pyc | Bin 0 -> 15078 bytes .../py/_path/__pycache__/local.cpython-39.pyc | Bin 0 -> 31285 bytes .../_path/__pycache__/svnurl.cpython-39.pyc | Bin 0 -> 13405 bytes .../py/_path/__pycache__/svnwc.cpython-39.pyc | Bin 0 -> 36189 bytes .../site-packages/py/_path/cacheutil.py | 114 + .../site-packages/py/_path/common.py | 459 + .../python3.9/site-packages/py/_path/local.py | 1030 ++ .../site-packages/py/_path/svnurl.py | 380 + .../python3.9/site-packages/py/_path/svnwc.py | 1240 +++ .../site-packages/py/_process/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 264 bytes .../__pycache__/cmdexec.cpython-39.pyc | Bin 0 -> 1938 bytes .../__pycache__/forkedfunc.cpython-39.pyc | Bin 0 -> 3754 bytes .../__pycache__/killproc.cpython-39.pyc | Bin 0 -> 1048 bytes .../site-packages/py/_process/cmdexec.py | 49 + .../site-packages/py/_process/forkedfunc.py | 120 + .../site-packages/py/_process/killproc.py | 23 + .../lib/python3.9/site-packages/py/_std.py | 27 + .../py/_vendored_packages/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 229 bytes .../apipkg-2.0.0.dist-info/INSTALLER | 1 + .../apipkg-2.0.0.dist-info/LICENSE | 18 + .../apipkg-2.0.0.dist-info/METADATA | 125 + .../apipkg-2.0.0.dist-info/RECORD | 11 + .../apipkg-2.0.0.dist-info/REQUESTED | 0 .../apipkg-2.0.0.dist-info/WHEEL | 6 + .../apipkg-2.0.0.dist-info/top_level.txt | 1 + .../py/_vendored_packages/apipkg/__init__.py | 217 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6372 bytes .../apipkg/__pycache__/version.cpython-39.pyc | Bin 0 -> 290 bytes .../py/_vendored_packages/apipkg/version.py | 5 + .../iniconfig-1.1.1.dist-info/INSTALLER | 1 + .../iniconfig-1.1.1.dist-info/LICENSE | 19 + .../iniconfig-1.1.1.dist-info/METADATA | 78 + .../iniconfig-1.1.1.dist-info/RECORD | 11 + .../iniconfig-1.1.1.dist-info/REQUESTED | 0 .../iniconfig-1.1.1.dist-info/WHEEL | 6 + .../iniconfig-1.1.1.dist-info/top_level.txt | 1 + .../_vendored_packages/iniconfig/__init__.py | 165 + .../_vendored_packages/iniconfig/__init__.pyi | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5293 bytes .../py/_vendored_packages/iniconfig/py.typed | 0 .../python3.9/site-packages/py/_version.py | 5 + .../lib/python3.9/site-packages/py/_xmlgen.py | 255 + .../lib/python3.9/site-packages/py/error.pyi | 129 + .../python3.9/site-packages/py/iniconfig.pyi | 31 + .../lib/python3.9/site-packages/py/io.pyi | 130 + .../lib/python3.9/site-packages/py/path.pyi | 197 + .../lib/python3.9/site-packages/py/py.typed | 0 .../lib/python3.9/site-packages/py/test.py | 10 + .../lib/python3.9/site-packages/py/xml.pyi | 25 + .../pyparsing-3.0.7.dist-info/INSTALLER | 1 + .../pyparsing-3.0.7.dist-info/LICENSE | 18 + .../pyparsing-3.0.7.dist-info/METADATA | 109 + .../pyparsing-3.0.7.dist-info/RECORD | 29 + .../pyparsing-3.0.7.dist-info/WHEEL | 5 + .../pyparsing-3.0.7.dist-info/top_level.txt | 1 + .../site-packages/pyparsing/__init__.py | 328 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 7129 bytes .../__pycache__/actions.cpython-39.pyc | Bin 0 -> 7192 bytes .../__pycache__/common.cpython-39.pyc | Bin 0 -> 10083 bytes .../pyparsing/__pycache__/core.cpython-39.pyc | Bin 0 -> 176659 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 9180 bytes .../__pycache__/helpers.cpython-39.pyc | Bin 0 -> 34728 bytes .../__pycache__/results.cpython-39.pyc | Bin 0 -> 24804 bytes .../__pycache__/testing.cpython-39.pyc | Bin 0 -> 12133 bytes .../__pycache__/unicode.cpython-39.pyc | Bin 0 -> 10274 bytes .../pyparsing/__pycache__/util.cpython-39.pyc | Bin 0 -> 8643 bytes .../site-packages/pyparsing/actions.py | 207 + .../site-packages/pyparsing/common.py | 424 + .../python3.9/site-packages/pyparsing/core.py | 5789 ++++++++++++ .../pyparsing/diagram/__init__.py | 593 ++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 15575 bytes .../pyparsing/diagram/template.jinja2 | 26 + .../site-packages/pyparsing/exceptions.py | 267 + .../site-packages/pyparsing/helpers.py | 1069 +++ .../site-packages/pyparsing/results.py | 760 ++ .../site-packages/pyparsing/testing.py | 331 + .../site-packages/pyparsing/unicode.py | 332 + .../python3.9/site-packages/pyparsing/util.py | 235 + .../pytest-6.2.5.dist-info/INSTALLER | 1 + .../pytest-6.2.5.dist-info/LICENSE | 21 + .../pytest-6.2.5.dist-info/METADATA | 215 + .../pytest-6.2.5.dist-info/RECORD | 140 + .../pytest-6.2.5.dist-info/REQUESTED | 0 .../pytest-6.2.5.dist-info/WHEEL | 5 + .../pytest-6.2.5.dist-info/entry_points.txt | 4 + .../pytest-6.2.5.dist-info/top_level.txt | 2 + .../site-packages/pytest/__init__.py | 121 + .../site-packages/pytest/__main__.py | 5 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2996 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 342 bytes .../pytest/__pycache__/collect.cpython-39.pyc | Bin 0 -> 1505 bytes .../python3.9/site-packages/pytest/collect.py | 39 + .../python3.9/site-packages/pytest/py.typed | 0 .../setuptools-49.2.1.dist-info/INSTALLER | 1 + .../setuptools-49.2.1.dist-info/LICENSE | 19 + .../setuptools-49.2.1.dist-info/METADATA | 109 + .../setuptools-49.2.1.dist-info/RECORD | 297 + .../setuptools-49.2.1.dist-info/REQUESTED | 0 .../setuptools-49.2.1.dist-info/WHEEL | 5 + .../dependency_links.txt | 2 + .../entry_points.txt | 68 + .../setuptools-49.2.1.dist-info/top_level.txt | 3 + .../setuptools-49.2.1.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 253 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 8876 bytes .../_deprecation_warning.cpython-39.pyc | Bin 0 -> 588 bytes .../__pycache__/_imp.cpython-39.pyc | Bin 0 -> 2123 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 0 -> 5295 bytes .../__pycache__/build_meta.cpython-39.pyc | Bin 0 -> 8647 bytes .../__pycache__/config.cpython-39.pyc | Bin 0 -> 19459 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 0 -> 895 bytes .../__pycache__/depends.cpython-39.pyc | Bin 0 -> 5309 bytes .../__pycache__/dist.cpython-39.pyc | Bin 0 -> 33110 bytes .../distutils_patch.cpython-39.pyc | Bin 0 -> 1963 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 888 bytes .../__pycache__/extension.cpython-39.pyc | Bin 0 -> 2037 bytes .../__pycache__/glob.cpython-39.pyc | Bin 0 -> 3795 bytes .../__pycache__/installer.cpython-39.pyc | Bin 0 -> 4144 bytes .../__pycache__/launch.cpython-39.pyc | Bin 0 -> 939 bytes .../__pycache__/lib2to3_ex.cpython-39.pyc | Bin 0 -> 2790 bytes .../__pycache__/monkey.cpython-39.pyc | Bin 0 -> 4710 bytes .../__pycache__/msvc.cpython-39.pyc | Bin 0 -> 43274 bytes .../__pycache__/namespaces.cpython-39.pyc | Bin 0 -> 3708 bytes .../__pycache__/package_index.cpython-39.pyc | Bin 0 -> 33286 bytes .../__pycache__/py27compat.cpython-39.pyc | Bin 0 -> 1821 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 0 -> 1263 bytes .../__pycache__/py33compat.cpython-39.pyc | Bin 0 -> 1478 bytes .../__pycache__/py34compat.cpython-39.pyc | Bin 0 -> 518 bytes .../__pycache__/sandbox.cpython-39.pyc | Bin 0 -> 15910 bytes .../__pycache__/ssl_support.cpython-39.pyc | Bin 0 -> 6934 bytes .../__pycache__/unicode_utils.cpython-39.pyc | Bin 0 -> 1217 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 362 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 7411 bytes .../windows_support.cpython-39.pyc | Bin 0 -> 1061 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 15 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 496 bytes .../__pycache__/_msvccompiler.cpython-39.pyc | Bin 0 -> 12976 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 0 -> 6683 bytes .../__pycache__/bcppcompiler.cpython-39.pyc | Bin 0 -> 6594 bytes .../__pycache__/ccompiler.cpython-39.pyc | Bin 0 -> 33299 bytes .../_distutils/__pycache__/cmd.cpython-39.pyc | Bin 0 -> 14022 bytes .../__pycache__/config.cpython-39.pyc | Bin 0 -> 3625 bytes .../__pycache__/core.cpython-39.pyc | Bin 0 -> 6750 bytes .../cygwinccompiler.cpython-39.pyc | Bin 0 -> 8599 bytes .../__pycache__/debug.cpython-39.pyc | Bin 0 -> 292 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 0 -> 2812 bytes .../__pycache__/dir_util.cpython-39.pyc | Bin 0 -> 5913 bytes .../__pycache__/dist.cpython-39.pyc | Bin 0 -> 34483 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 5348 bytes .../__pycache__/extension.cpython-39.pyc | Bin 0 -> 7013 bytes .../__pycache__/fancy_getopt.cpython-39.pyc | Bin 0 -> 10721 bytes .../__pycache__/file_util.cpython-39.pyc | Bin 0 -> 6079 bytes .../__pycache__/filelist.cpython-39.pyc | Bin 0 -> 9931 bytes .../_distutils/__pycache__/log.cpython-39.pyc | Bin 0 -> 2411 bytes .../__pycache__/msvc9compiler.cpython-39.pyc | Bin 0 -> 17608 bytes .../__pycache__/msvccompiler.cpython-39.pyc | Bin 0 -> 14803 bytes .../__pycache__/spawn.cpython-39.pyc | Bin 0 -> 3464 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 0 -> 12445 bytes .../__pycache__/text_file.cpython-39.pyc | Bin 0 -> 8537 bytes .../__pycache__/unixccompiler.cpython-39.pyc | Bin 0 -> 6698 bytes .../__pycache__/util.cpython-39.pyc | Bin 0 -> 15686 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 7437 bytes .../versionpredicate.cpython-39.pyc | Bin 0 -> 5221 bytes .../setuptools/_distutils/_msvccompiler.py | 537 ++ .../setuptools/_distutils/archive_util.py | 256 + .../setuptools/_distutils/bcppcompiler.py | 393 + .../setuptools/_distutils/ccompiler.py | 1116 +++ .../setuptools/_distutils/cmd.py | 403 + .../setuptools/_distutils/command/__init__.py | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 571 bytes .../command/__pycache__/bdist.cpython-39.pyc | Bin 0 -> 3701 bytes .../__pycache__/bdist_dumb.cpython-39.pyc | Bin 0 -> 3684 bytes .../__pycache__/bdist_msi.cpython-39.pyc | Bin 0 -> 19866 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 0 -> 12321 bytes .../__pycache__/bdist_wininst.cpython-39.pyc | Bin 0 -> 8641 bytes .../command/__pycache__/build.cpython-39.pyc | Bin 0 -> 3977 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 0 -> 4896 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 0 -> 16287 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 0 -> 10529 bytes .../__pycache__/build_scripts.cpython-39.pyc | Bin 0 -> 4426 bytes .../command/__pycache__/check.cpython-39.pyc | Bin 0 -> 5005 bytes .../command/__pycache__/clean.cpython-39.pyc | Bin 0 -> 2178 bytes .../command/__pycache__/config.cpython-39.pyc | Bin 0 -> 10308 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 13895 bytes .../__pycache__/install_data.cpython-39.pyc | Bin 0 -> 2381 bytes .../install_egg_info.cpython-39.pyc | Bin 0 -> 3116 bytes .../install_headers.cpython-39.pyc | Bin 0 -> 1806 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 0 -> 5178 bytes .../install_scripts.cpython-39.pyc | Bin 0 -> 2229 bytes .../__pycache__/register.cpython-39.pyc | Bin 0 -> 8549 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 0 -> 14576 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 0 -> 5299 bytes .../setuptools/_distutils/command/bdist.py | 143 + .../_distutils/command/bdist_dumb.py | 123 + .../_distutils/command/bdist_msi.py | 749 ++ .../_distutils/command/bdist_rpm.py | 579 ++ .../_distutils/command/bdist_wininst.py | 377 + .../setuptools/_distutils/command/build.py | 157 + .../_distutils/command/build_clib.py | 209 + .../_distutils/command/build_ext.py | 754 ++ .../setuptools/_distutils/command/build_py.py | 416 + .../_distutils/command/build_scripts.py | 160 + .../setuptools/_distutils/command/check.py | 148 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 344 + .../setuptools/_distutils/command/install.py | 677 ++ .../_distutils/command/install_data.py | 79 + .../_distutils/command/install_egg_info.py | 77 + .../_distutils/command/install_headers.py | 47 + .../_distutils/command/install_lib.py | 217 + .../_distutils/command/install_scripts.py | 60 + .../setuptools/_distutils/command/register.py | 304 + .../setuptools/_distutils/command/sdist.py | 494 + .../setuptools/_distutils/command/upload.py | 214 + .../setuptools/_distutils/config.py | 130 + .../setuptools/_distutils/core.py | 234 + .../setuptools/_distutils/cygwinccompiler.py | 403 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 92 + .../setuptools/_distutils/dir_util.py | 210 + .../setuptools/_distutils/dist.py | 1257 +++ .../setuptools/_distutils/errors.py | 97 + .../setuptools/_distutils/extension.py | 240 + .../setuptools/_distutils/fancy_getopt.py | 457 + .../setuptools/_distutils/file_util.py | 238 + .../setuptools/_distutils/filelist.py | 327 + .../setuptools/_distutils/log.py | 77 + .../setuptools/_distutils/msvc9compiler.py | 788 ++ .../setuptools/_distutils/msvccompiler.py | 643 ++ .../setuptools/_distutils/spawn.py | 125 + .../setuptools/_distutils/sysconfig.py | 573 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 328 + .../setuptools/_distutils/util.py | 559 ++ .../setuptools/_distutils/version.py | 347 + .../setuptools/_distutils/versionpredicate.py | 166 + .../site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 226 bytes .../__pycache__/ordered_set.cpython-39.pyc | Bin 0 -> 16420 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201379 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 24509 bytes .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 750 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 588 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1050 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2832 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 8971 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4063 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 19784 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 10862 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1497 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 12117 bytes .../setuptools/_vendor/packaging/_compat.py | 31 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 296 + .../_vendor/packaging/requirements.py | 138 + .../_vendor/packaging/specifiers.py | 749 ++ .../setuptools/_vendor/packaging/tags.py | 404 + .../setuptools/_vendor/packaging/utils.py | 57 + .../setuptools/_vendor/packaging/version.py | 420 + .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 ++ .../site-packages/setuptools/archive_util.py | 175 + .../site-packages/setuptools/build_meta.py | 271 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 17 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 706 bytes .../command/__pycache__/alias.cpython-39.pyc | Bin 0 -> 2464 bytes .../__pycache__/bdist_egg.cpython-39.pyc | Bin 0 -> 14493 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 0 -> 1854 bytes .../__pycache__/bdist_wininst.cpython-39.pyc | Bin 0 -> 1261 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 0 -> 2506 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 0 -> 10005 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 0 -> 8932 bytes .../__pycache__/develop.cpython-39.pyc | Bin 0 -> 6582 bytes .../__pycache__/dist_info.cpython-39.pyc | Bin 0 -> 1433 bytes .../__pycache__/easy_install.cpython-39.pyc | Bin 0 -> 65584 bytes .../__pycache__/egg_info.cpython-39.pyc | Bin 0 -> 21834 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 4074 bytes .../install_egg_info.cpython-39.pyc | Bin 0 -> 2465 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 0 -> 4172 bytes .../install_scripts.cpython-39.pyc | Bin 0 -> 2388 bytes .../__pycache__/py36compat.cpython-39.pyc | Bin 0 -> 4682 bytes .../__pycache__/register.cpython-39.pyc | Bin 0 -> 883 bytes .../command/__pycache__/rotate.cpython-39.pyc | Bin 0 -> 2603 bytes .../__pycache__/saveopts.cpython-39.pyc | Bin 0 -> 961 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 0 -> 7971 bytes .../command/__pycache__/setopt.cpython-39.pyc | Bin 0 -> 4613 bytes .../command/__pycache__/test.cpython-39.pyc | Bin 0 -> 8715 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 0 -> 856 bytes .../__pycache__/upload_docs.cpython-39.pyc | Bin 0 -> 6247 bytes .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 510 + .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 30 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 332 + .../setuptools/command/build_py.py | 276 + .../setuptools/command/develop.py | 220 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2339 +++++ .../setuptools/command/egg_info.py | 721 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 68 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 252 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 280 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 701 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/dist.py | 1035 ++ .../setuptools/distutils_patch.py | 61 + .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 66 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2451 bytes .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 150 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/lib2to3_ex.py | 71 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1831 ++++ .../site-packages/setuptools/namespaces.py | 111 + .../site-packages/setuptools/package_index.py | 1140 +++ .../site-packages/setuptools/py27compat.py | 60 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 59 + .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 492 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/ssl_support.py | 265 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 217 + .../setuptools/windows_support.py | 29 + .../toml-0.10.2.dist-info/INSTALLER | 1 + .../toml-0.10.2.dist-info/LICENSE | 27 + .../toml-0.10.2.dist-info/METADATA | 255 + .../toml-0.10.2.dist-info/RECORD | 16 + .../site-packages/toml-0.10.2.dist-info/WHEEL | 6 + .../toml-0.10.2.dist-info/top_level.txt | 1 + .../python3.9/site-packages/toml/__init__.py | 25 + .../toml/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 744 bytes .../toml/__pycache__/decoder.cpython-39.pyc | Bin 0 -> 23236 bytes .../toml/__pycache__/encoder.cpython-39.pyc | Bin 0 -> 9419 bytes .../toml/__pycache__/ordered.cpython-39.pyc | Bin 0 -> 983 bytes .../toml/__pycache__/tz.cpython-39.pyc | Bin 0 -> 1297 bytes .../python3.9/site-packages/toml/decoder.py | 1057 +++ .../python3.9/site-packages/toml/encoder.py | 304 + .../python3.9/site-packages/toml/ordered.py | 15 + .../lib/python3.9/site-packages/toml/tz.py | 24 + pytest_project/pytest-env/pyvenv.cfg | 3 + pytest_project/pytest-env/test_capitalize.py | 15 + pytest_project/pytest-env/test_wallet.py | 42 + pytest_project/pytest-env/wallet.py | 15 + 1466 files changed, 234688 insertions(+) create mode 100644 pytest_project/pytest-env/__pycache__/test_capitalize.cpython-39-pytest-6.2.5.pyc create mode 100644 pytest_project/pytest-env/__pycache__/test_wallet.cpython-39-pytest-6.2.5.pyc create mode 100644 pytest_project/pytest-env/__pycache__/wallet.cpython-39.pyc create mode 100644 pytest_project/pytest-env/bin/Activate.ps1 create mode 100644 pytest_project/pytest-env/bin/activate create mode 100644 pytest_project/pytest-env/bin/activate.csh create mode 100644 pytest_project/pytest-env/bin/activate.fish create mode 100755 pytest_project/pytest-env/bin/easy_install create mode 100755 pytest_project/pytest-env/bin/easy_install-3.9 create mode 100755 pytest_project/pytest-env/bin/pip create mode 100755 pytest_project/pytest-env/bin/pip3 create mode 100755 pytest_project/pytest-env/bin/pip3.9 create mode 100755 pytest_project/pytest-env/bin/py.test create mode 100755 pytest_project/pytest-env/bin/pytest create mode 120000 pytest_project/pytest-env/bin/python create mode 120000 pytest_project/pytest-env/bin/python3 create mode 120000 pytest_project/pytest-env/bin/python3.9 create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_argcomplete.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/cacheprovider.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/capture.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/debugging.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/deprecated.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/doctest.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/faulthandler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/fixtures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/freeze_support.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/helpconfig.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/hookspec.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/junitxml.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/logging.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/main.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nose.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/outcomes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pastebin.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pathlib.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester_assertions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/python.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/python_api.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/reports.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/runner.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/setupplan.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/skipping.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/store.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/threadexception.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/timing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/tmpdir.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unittest.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unraisableexception.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warning_types.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warnings.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_argcomplete.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/source.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/code.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/source.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/saferepr.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/saferepr.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/terminalwriter.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/wcwidth.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/truncate.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/rewrite.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/truncate.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/cacheprovider.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/capture.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/argparsing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/argparsing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/findpaths.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/debugging.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/deprecated.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/doctest.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/faulthandler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/fixtures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/freeze_support.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/helpconfig.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/hookspec.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/junitxml.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/logging.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/main.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/expression.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/monkeypatch.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nodes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nose.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/outcomes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pastebin.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pathlib.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester_assertions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python_api.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/recwarn.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/reports.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/runner.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setuponly.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setupplan.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/skipping.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/stepwise.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/store.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/terminal.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/threadexception.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/timing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/tmpdir.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unittest.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unraisableexception.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warning_types.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warnings.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_cmp.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_config.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_funcs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_make.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_next_gen.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_version_info.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/converters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/filters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/setters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/validators.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_config.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_funcs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_make.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_next_gen.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/AUTHORS.rst create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/converters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/filters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/setters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/validators.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/converters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/filters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/setters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/attrs/validators.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/easy_install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.APACHE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.BSD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__about__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_manylinux.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_musllinux.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_manylinux.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_musllinux.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/markers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/requirements.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/specifiers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/tags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/packaging/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/LICENSE.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/REQUESTED create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/entry_points.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/__main__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/locations.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/build_env.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/base_command.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/command_context.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/parser.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/req_command.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/spinners.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/check.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/completion.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/configuration.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/debug.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/download.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/freeze.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/hash.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/help.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/list.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/search.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/show.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/configuration.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/installed.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/collector.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/package_finder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/locations.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/main.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/candidate.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/direct_url.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/format_control.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/index.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/link.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/scheme.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/search_scope.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/target_python.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/auth.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/download.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/session.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/check.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/freeze.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/prepare.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/pyproject.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/constructors.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_file.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_set.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/typing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/datetime.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/encoding.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/glibc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/hashes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/logging.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/misc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/models.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/packaging.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/parallel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/typing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/urls.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/git.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/appdirs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/contextlib2.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/distro.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/ipaddress.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/retrying.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/appdirs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/core.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/enums.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/contextlib2.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/database.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/index.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/markers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distro.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/core.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/codec.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/core.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/intranges.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/package_data.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/ipaddress.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/_typing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/_typing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/markers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/tags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/packaging/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/meta.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/build.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/check.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/meta.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/bar.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/counter.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/progress/spinner.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/pyparsing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/api.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/help.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/models.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/__version__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/adapters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/api.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/auth.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/certs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/cookies.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/help.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/hooks.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/models.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/packages.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/sessions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/requests/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/retrying.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/six.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/common.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/decoder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/encoder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/ordered.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/__pycache__/tz.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/common.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/decoder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/encoder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/ordered.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/toml/tz.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/request.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/response.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/vendor.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/_vendor/six.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/extern/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy-1.0.0.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_callers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_hooks.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_manager.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_result.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_tracing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/__pycache__/_version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_callers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_hooks.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_manager.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_result.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_tracing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pluggy/_version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py-1.11.0.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__init__.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__metainfo.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/__metainfo.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/_builtin.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/_error.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/_std.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/_version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/_xmlgen.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/__pycache__/test.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_builtin.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/_assertionnew.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/_assertionold.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/_py2traceback.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/assertion.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/code.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/__pycache__/source.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/_assertionnew.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/_assertionold.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/_py2traceback.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/assertion.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/code.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_code/source.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_error.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/__pycache__/capture.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/__pycache__/saferepr.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/__pycache__/terminalwriter.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/capture.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/saferepr.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_io/terminalwriter.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/__pycache__/log.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/__pycache__/warning.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/log.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_log/warning.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/cacheutil.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/common.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/local.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/svnurl.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/__pycache__/svnwc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/cacheutil.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/common.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/local.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/svnurl.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_path/svnwc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/__pycache__/cmdexec.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/__pycache__/forkedfunc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/__pycache__/killproc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/cmdexec.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/forkedfunc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_process/killproc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_std.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/REQUESTED create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg-2.0.0.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/apipkg/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/REQUESTED create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig/__init__.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_vendored_packages/iniconfig/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/_xmlgen.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/error.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/iniconfig.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/io.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/path.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/test.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/py/xml.pyi create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing-3.0.7.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/actions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/common.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/core.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/exceptions.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/helpers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/results.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/testing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/unicode.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/__pycache__/util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/actions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/common.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/core.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/diagram/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/diagram/template.jinja2 create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/exceptions.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/helpers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/results.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/testing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/unicode.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pyparsing/util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/REQUESTED create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/entry_points.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest-6.2.5.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/__main__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/__pycache__/__main__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/__pycache__/collect.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/collect.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/pytest/py.typed create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/REQUESTED create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/dependency_links.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/entry_points.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools-49.2.1.dist-info/zip-safe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/config.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/depends.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/dist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/distutils_patch.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/errors.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/extension.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/glob.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/installer.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/launch.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/py27compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/py31compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/py33compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/ssl_support.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_deprecation_warning.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/config.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/core.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/debug.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/dist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/errors.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/extension.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/log.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/util.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/archive_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/cmd.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_msi.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/build.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/check.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/clean.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/config.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/register.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/command/upload.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/config.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/core.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/debug.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/dep_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/dir_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/dist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/errors.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/extension.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/file_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/filelist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/log.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/spawn.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/text_file.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_imp.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/_vendor/six.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/archive_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/build_meta.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/cli-32.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/cli-64.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/cli.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/alias.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/bdist_egg.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/build_clib.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/build_ext.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/build_py.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/develop.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/dist_info.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/easy_install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/egg_info.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/install.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/install_egg_info.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/install_lib.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/install_scripts.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/py36compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/register.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/rotate.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/saveopts.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/sdist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/setopt.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/test.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/upload.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/command/upload_docs.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/config.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/dep_util.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/depends.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/dist.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/distutils_patch.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/errors.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/extension.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/extern/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/glob.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/gui-32.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/gui-64.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/gui.exe create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/installer.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/launch.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/lib2to3_ex.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/monkey.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/msvc.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/namespaces.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/package_index.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/py27compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/py31compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/py33compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/py34compat.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/sandbox.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/script (dev).tmpl create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/script.tmpl create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/ssl_support.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/unicode_utils.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/version.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/wheel.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/setuptools/windows_support.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/INSTALLER create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/LICENSE create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/METADATA create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/RECORD create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/WHEEL create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml-0.10.2.dist-info/top_level.txt create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__init__.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__pycache__/__init__.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__pycache__/decoder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__pycache__/encoder.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__pycache__/ordered.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/__pycache__/tz.cpython-39.pyc create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/decoder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/encoder.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/ordered.py create mode 100644 pytest_project/pytest-env/lib/python3.9/site-packages/toml/tz.py create mode 100644 pytest_project/pytest-env/pyvenv.cfg create mode 100644 pytest_project/pytest-env/test_capitalize.py create mode 100644 pytest_project/pytest-env/test_wallet.py create mode 100644 pytest_project/pytest-env/wallet.py diff --git a/pytest_project/pytest-env/__pycache__/test_capitalize.cpython-39-pytest-6.2.5.pyc b/pytest_project/pytest-env/__pycache__/test_capitalize.cpython-39-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30e6b1d23b83c4e25ae57c8a199c7f7d5f6d0a8b GIT binary patch literal 1297 zcmZt`%Wl&^aA)l}PG}KO@jOLHNcEvj3*{wI1mbb5kN_ckv9{iAFgUi^-B4QW3+0H! z4{%71{L&ub#4o^w*>!**vB|u5W+(FqTVC!HJYV;JvIYqGW`)H=QMiMkZX@D^Lr!9F zl#V@0fQ915Ua3fy7? zXgH$kl%^bx;62o2>dgrH)1AW;`?4%XgRH0~dY}&~5&5Q{sjN_%6{#?u($e&vpy7d( zr8NDNRhedab|@k+9q0Fj$SywgBN;wbLaJfi6gjE3Un%KO=95dWiOpCNq$a zkP9qVP_!doBkw58DV+fiKKo}#LYd-_zJGzUv!n!kaJ>bMAjRipP8p>@ydONYt zc2gevcM(VMgKlp}aJX@4wK~{}RR7(a8Eg+Mw-KpTOD{(%>NkV!R(m~Ct`?CWdw9p9 z%NA`}bp7x+Q`jW!0^E$0Ro;wx&cpQY%-g}FIf+Hi^F)eDrscR|QkW$)C%cm@*Ek*~ z$VA}UaauH5 zq98tIUuYBDTokS>3fC5etF6#RyAit-TVZpW%YIOs5*nXZPxUSNj?{+qEv4Xa| zCXg&sLP;OhuJQ|++KH7e-8GkPdll=Hipo|g!Eb?pdzlrZgvn96V9V&IY(N4_AkQL= z`X>C{Eosf>e9|Vc-j*1ntxK^lGmX9aqcYvOP98%adDXa&g=K N0lB@rNW*UE{Q}iRRd@gZ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/__pycache__/test_wallet.cpython-39-pytest-6.2.5.pyc b/pytest_project/pytest-env/__pycache__/test_wallet.cpython-39-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e1ff04ed9a32600ac81758093917e5c180de4f1 GIT binary patch literal 3253 zcmdT`OLH4V5T4nWR`Mf$#d+~q0)cFZE$87R2b@X@IFN&jB2X#BVQZsV+3QHVVrG=Y zV#NXbzzMD#I7pn(onNz8PWlU>Dn<8>wLOo_nta{r$& zvk3Vcg~`hX;U3)L4q;z$oq}7mB=cNnY<_n zpTaF1Ad>8nhon#2t6xR3#S`Iqp{FPF;zY1Gh6&A&9230X4fb)P+v287Bi*Yb$a9}5nqD6kdYqc72sUt5(DhW zTRqCG@yNCsX~M{TWd#mKTFn04TqYfdIjoR5F$KOLi!h>;6?(9UKf!jiNXP2f3}EZK zn|=Di*x2xCyL{<#bN9M0NP!s{w(2R&79?RK%8P*rYZ1VQ5HSf`lN7s0`&^;^J-5@ z9ws8J;`hWN%&h7NF-o*V`9+Yci1*Qj)nSaYUqHyn4(U)vJ4Oa516-MzDSni+YXG85 zz?F%5g)0lz-BP%^2XH%=h5LcGs0ki4}h#jKGQs}W1dTdz$tDD&q=%IihvttYq z#L-DQY?*j21H8=NwpIok+{JpV8uh}ha*20~mYj#1c8H;LRt1g2$iDeAn3RP=BFLdRi z{A1w%Z!3poPOTh&A30U}K0xjgk`Ix51f=h1lZT4SNHB%mM{)(pxP@^AbDc5~N!X-Y zM^c6>HA>kE@F%D2H?U$+Wy7MnLjm^2A=v*cjjjoRN7!3q*xSPuKX)l5m3@)-;o>&S^{gj_D8 z{Xge&c`}y=Z3uV4K{l^}bSRrx=&)X?o__}z37uX7fPOu%y1uEQ`UnoV7X0>1N|L#2 zL|j>eqc8rxg6~HFk~O;LXvua>_*t&?FTo(`kN^Mx literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/__pycache__/wallet.cpython-39.pyc b/pytest_project/pytest-env/__pycache__/wallet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d497e8af217e80e13dcf1bcb567a1c59c669f3da GIT binary patch literal 999 zcmZuwv2xQu5WSORSr)NFQl(VEFsN`;(lAUiz%VpwGQf^I)%iThU7z_ zV*E>LsVHfoVfSP^0q16Rb$h$FyYJnK_xnD9^=t8mf2V}}K;!Omz&HY_9)WsI1QGNG zM3QTJM8UR%4Yn;Uux;U-kg$_LkwvJL+pgd~qEqpfk zjxgvks13fJ;f5kBqH{w=j&Ma6z8&EKa=&rkn<_uy4oNWmdf^`B1xSUjv!DyYfcg`% zr16qA-7(L2o=Bk2sbn@84bnW-DbE-;1m5zZT)2*8VgO@k86zu;-c$C(Ut5mcqj zMeyl!XsZX1(~#M?lS0k7URnyLqjB$t;G4TF;nlPUVHF}F)TaYF-&+B>jo5;M4`iV1 z!)8Go)AUmyxouiuN`*|TT~wQ~TG7XaFqH@3{GYzHFAk~lz#qB_-%epkg%qkjsy$R# z{Z4XI27#z9D2z3*Jj=Jejya1%r|G@AOk}CkB46=UjB#9z|Gs36_pJS2o^R&%q5T)2 CchDLD literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/bin/Activate.ps1 b/pytest_project/pytest-env/bin/Activate.ps1 new file mode 100644 index 0000000..2fb3852 --- /dev/null +++ b/pytest_project/pytest-env/bin/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/pytest_project/pytest-env/bin/activate b/pytest_project/pytest-env/bin/activate new file mode 100644 index 0000000..4700cfa --- /dev/null +++ b/pytest_project/pytest-env/bin/activate @@ -0,0 +1,66 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(pytest-env) ${PS1:-}" + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/pytest_project/pytest-env/bin/activate.csh b/pytest_project/pytest-env/bin/activate.csh new file mode 100644 index 0000000..f8f2a10 --- /dev/null +++ b/pytest_project/pytest-env/bin/activate.csh @@ -0,0 +1,25 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(pytest-env) $prompt" +endif + +alias pydoc python -m pydoc + +rehash diff --git a/pytest_project/pytest-env/bin/activate.fish b/pytest_project/pytest-env/bin/activate.fish new file mode 100644 index 0000000..845398a --- /dev/null +++ b/pytest_project/pytest-env/bin/activate.fish @@ -0,0 +1,64 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(pytest-env) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/pytest_project/pytest-env/bin/easy_install b/pytest_project/pytest-env/bin/easy_install new file mode 100755 index 0000000..6d4e08f --- /dev/null +++ b/pytest_project/pytest-env/bin/easy_install @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/pytest_project/pytest-env/bin/easy_install-3.9 b/pytest_project/pytest-env/bin/easy_install-3.9 new file mode 100755 index 0000000..6d4e08f --- /dev/null +++ b/pytest_project/pytest-env/bin/easy_install-3.9 @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/pytest_project/pytest-env/bin/pip b/pytest_project/pytest-env/bin/pip new file mode 100755 index 0000000..772e8e1 --- /dev/null +++ b/pytest_project/pytest-env/bin/pip @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/pytest_project/pytest-env/bin/pip3 b/pytest_project/pytest-env/bin/pip3 new file mode 100755 index 0000000..772e8e1 --- /dev/null +++ b/pytest_project/pytest-env/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/pytest_project/pytest-env/bin/pip3.9 b/pytest_project/pytest-env/bin/pip3.9 new file mode 100755 index 0000000..772e8e1 --- /dev/null +++ b/pytest_project/pytest-env/bin/pip3.9 @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/pytest_project/pytest-env/bin/py.test b/pytest_project/pytest-env/bin/py.test new file mode 100755 index 0000000..e442b41 --- /dev/null +++ b/pytest_project/pytest-env/bin/py.test @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/pytest_project/pytest-env/bin/pytest b/pytest_project/pytest-env/bin/pytest new file mode 100755 index 0000000..e442b41 --- /dev/null +++ b/pytest_project/pytest-env/bin/pytest @@ -0,0 +1,8 @@ +#!/Users/chanagonda/Documents/coe/qa-coe-experiments/pytest_project/pytest-env/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/pytest_project/pytest-env/bin/python b/pytest_project/pytest-env/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/pytest_project/pytest-env/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/pytest_project/pytest-env/bin/python3 b/pytest_project/pytest-env/bin/python3 new file mode 120000 index 0000000..9556d1c --- /dev/null +++ b/pytest_project/pytest-env/bin/python3 @@ -0,0 +1 @@ +/Library/Frameworks/Python.framework/Versions/3.9/bin/python3 \ No newline at end of file diff --git a/pytest_project/pytest-env/bin/python3.9 b/pytest_project/pytest-env/bin/python3.9 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/pytest_project/pytest-env/bin/python3.9 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fce3d89b04b8bc67e1b8e95e2ee8115d10a50a34 GIT binary patch literal 353 zcmYk0Jx;_h5Jv4JzzVS^K!S)aMdE@A2_Xt7Xee0Gq_Kv0vFLWZdIin2Pi1 ziw1?9m&Xo$-+5!CEB*xq1}Sf5tBr%>gV@~=f9re0?j2gksv0z`jTXSJOw}vYJ{%3Q zC&)wOxarU~^SK*5I?p?69Cje`1tk4SJ9fvP-?DJp7=e=4f{irU2FbAMg0{G(3VA3Er z(c>rQZKsVpGe(%Qg61yXbJK+AAS{?u_JM!Fju zw+=d^?|JRW2urTkM2fozms%Yx4v7_xmmRHFwBeS(s3QPE2?wy~245Vtn)Q+&$B;Ir L+DsBW!`taMQI}zn literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_argcomplete.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_argcomplete.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46186681f05e2580209b88c619b2ee47478ffa0a GIT binary patch literal 4094 zcmaJ^TW{OQ73M{vWZH33Z@P}l~0{agN6zGrWBOlwR{Dtj9`<Qt{LI-ld#|G?v!C9JL`Y~jRq*EVmb>zKFOb@6uN+OXEG*|eq> z*N0x$GwnLsjc&uVJ+zzMrfog6L_;*+TB7;J>CQ>#z<)O{=HA=tudf!2%!TfS-&o0K zHA^mz|7=_IlkU5V!hdhu)^jUyZdh{ejn%y*n{w`oCBG-;?^x14SQ3s{_{tF%#Nyjp z_oL+F%GkwnOgP<3!g*@7F7dy?#Em#kkJvufgS9XnX0gnpG-16|F|PU-F&eSb$vp{on-uHR_?wEZ(9B^&1h2)xz)N#|Dk7F$VUAr*TmmBv1r z^>`HfdCGcGB3M3gj$aJovq`%5^%7TqAA8zjc`pM(1y)7n_mu>cvD;8;%NJwE? z=Btb+!p{d76RE^RnqyK5rZX8vJZ51kWDANu7$u=GFIVHBV$h1QQ?-~Lq@!4{eaS*& z921d}VIGe$1eZu`Qcf|byrKG|p-l28o(Uhy&Q->H zImXSvoxP1O?q2mNRFVsp_KbWXdwdk1{)b(??fZxa;tq}-uhWiXw*FJ5TpE9zX8f-}>U&!-v~D4`TB||BHMSwjG#Ddn zw~3bD=}-prBN38Q^E8_oKIBkf3WbVaVAUGiE`wC1nc)+~vzooieS0b$vTBxO(a>0U zE_tq=OLI_q6Pb;jTgDDXI-jP|GrO7WP1(ofJY+=h9Wu^l6N0mW&q;D5GL^}3nf`H_ zu!x1=H-eJ%=|C_HJxbLfQ&E4AGk(O!#??=8u7v|iyA?)(H2b5zfw(#LR(K5L4rHA5 zMlm}Y$OIzEVIj;(hfs9JXr)`F!-5Yc&lZ(m&l<>m{Z~GD|_tH@!R@qHj18^ENJO!=uXo%bq z<;Fmh(+sJAE5regkxHe=&?60tOXaJyUcX6)^NEo&9Z4;t^Yk>$2MQGiXOHA~i{#yA z%jP`a-q~7aQ}aL|B(6--zF-LDCD;N<{W#sn30{p*Nf(6q1L`tsr)3v4T7|c}k&LPP zIMO-Z&D|-eLRwh8YUq59TmJ$NX`!IAsF*_`=gXRKMGYlXU3hQn-MaK-14Wj11tr^8 zu4sxm6jpP>MFBNmEIr^le?X`F=IlIs^(l=R_?|+8g)!HeHiutmpNL3_t;VGm8 zm4puTm|fJSdlR>&5_MuvtN``rQ|oIR=Bft9AgHEg+%ui8Ej5p^Z+_o-hDtDttsC1^?HPl(1INF~cIk(!s?4T%- zYZ(s@d0*;IQ2wn`%@0UwMKcJZB+7%}F)44XvS0n++g@tV*0tQi%arUz$Ax!zgfMGG zb@v&Eki8!6MKSEe!#9hFV$@9MdSQI8~0!Hu^2bL+J|n<+>0#-#qn zzHj~Z`fJbZ-JEzR;r}x6L<6h6N$uU-#GAOKjwI*BpC&cnrn&RMh82n)g?;@S``(BD z;Ckh>(eAYT!bx>e%XmI0TrIQ0jb&0arX^nC$>UrmLXmR|PvgvyqNqn2^M!{rfw@*g z5pEQ|YAkgTkFH<(n5wtJKRw&K4Pwqd1lY$0LgizDL?e`R)&{{YdisZWSk7hpQ>SLr z?-JTecGF(87w~JM_Z?v_Wwh))*aeZ4#DbvkgJ7775lYISSO|g_BOXs@8bKh^5W}_o zG>z2-QpLsRfO64NL|nz_L4dD3NM%JJ*~YD@(X?yMl557g3)QrxKEiCVc=mt+pS-gC zE%8UOnO)STjSV4}(4c5f%*Ws#9emh@6YVQ0tXSZq zJe?gJU)yR0LeCjfTs(Jx##l3Dlv+hQJg*}4jf$izDW&GnD8>Hxq-FeOyhTD3?y1n$ zWpxd`q6tMyPkf)ct{&?t*79+NbD$E4I!Pd37Y!tgNTo^P;&Y_?amCitQe3MREuG&s zWrR5jikdu*aQU literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_version.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/_version.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffb7e650c4d4e33a45bc5269d7bc1abec4b44f2d GIT binary patch literal 270 zcmYjLF^`pN}2-=J=E&lkK^ybWecw~k&rb8rH!i1N2!k@6Pacp(t@Ax-t+&}1d_Z_u} z#-V@x++k^QC6nO2q_)xpURdc8FM6C{W+(;Ol1si&Qg9fltlFTobW{ji9+5=2*I4HD zSsyGKo9a@nFtbqG2;~lneD3U0$)C6fCbt-Ao~`);4WxAr(s!9yAFf~hjS^LXgC-R+ Iu6)yb0>w&9eRucHY~1MWYFV07!xq)#4(uDWXBGq&T!94VRI|8b}c#Y1FpdYV>^o8tetU z?|~FrjW{!uEy-ifWbAlJM&khSq+&W%ab)G$Qtd5RmE&Rt?JDm=*@zlNe5^bs@qA-)C-L#d{^oRfTH<>UKTtj(@d?BamJdpNvhhIkQ2CI=ry377 z3*~~u_ck7C9xfl2_&&ssl#fV!f8%KLSoxU5ryCD9kC%^2`~c!7$|od#uu*KDET2UD zf$}5jQ2CU<&mUcxQ4gxZE&FqpdPp69*HVXXIydc`cDaO{BkCw}j^1?2vq&9N4s=SpA}xL2dt2OX_fs}8;HACd}bwH zZ6W6)8(ktbrv0GZxb9aLb-P&!LUxqU&5B+`Wb{J2(eP{GEBWRZ%T*mnqdb;byIQFStLvdJ z6HYDG-wxL_o8&K>=qvuUwYcz#9|V{@`}jgzt;teTFIQ_;Cc7C}Y6t;6}Z5%9pmy3lNEzff->+7y^8WrJSanxx(KC;oIs5eD>8H+~ed_G#rxt2YKAAmIklkCXH_%g1sNS`|VRflc(|$GdRbgR0 zK7|t6UB(0k#x7JB+H2vxnq%7n-#?cLHXLR!5LJsFI*P$D+gqQkly|C-TDeRX_5yzp5i! zMfUaEAT52YoGW@!TA&E~9VG#ZKyQ5n`xzy*50GiqTqO8MqT=BLJG3s~3-%!ht*(31 zwymz+wJLVkLFg#!ignwbE4q3TH85De|5U{cfI+>$Z!GGgsGyHA;E=a1{Rn>Ud};Po zkdR=uwp?vhm)b2=oxRwut&x%-ub? zzQE*iyY+bK>Dd4zZD!R_m0-4F>KTaZljPO)C|#)#+A5Wcm{33%v)r_uv^#m(*ix?; zi|kKdKa5&6ZV&%@+eJA-wu?PsyTIG9UDk@-6-nZjv*C8_O-H#~wtlMXZn|3*eytU+ z>jG{7zpULw9Z9)3U9a=}1;Ia|I5HpL9VkMp<_i_57B0v+f}#}SzjI9=q;FN%uLEz- z^|R;oVazJZ)(9=NhF{giggLcI$*KS<4EHbx7iE_G(9pgpSr1qojYvCl$T(QH)(8gn zL{6BeDo3kN$#6jR7!tl8=|BXrWZKT#C2X>ELl`le>>}3t;|4P=oBR|a zLz`@^IE&6j>&=7Q=B|6d+VHxbZ1O~i7!TvxgtD=%PG|7~cR+SESX-FE;)JEt{h+WG zfZBm(FSi@YkTlh2Ff1o)xL$35mwoq5pX=@Mc;&)#7oNXVdHIz~mtXi|<>CvkT!|)z z^Im)I#aAy~(T`$TI)?yFA7{M3A5n5u``|XZ!0h3T){i0Yb$tCD9<$T<*V%1$c;Mh_ z1Xup?mmuunlO3H#1Um|W%(g;cIY8Ck!taWElVXn3dF69Hc^X6-$S>TZZ6T+(gX{mF zU>Rj<1W1Bc(Gz&oBbY{%1oQzdqqJ_fLk?GT20(4c;%CvKhA-e!EPz+%K)mc;--D1% z$X#rPhV*$=y%gMy&Lji;1ZZDGqz7q4&4#_<;n!_AO?M*!r1gNpiLMJ|wpS91Rvk$E zE&Ho>%i&&P|B@>y<*CFi7veB?GTAxvLJ+L^g&xbjQPr(_Yw5cKl;)vPgdBfaL@in7 z>!$TbF3M1p1%0SpZTQ#yMlo$rJRJ`ZlqX|4Sc-Ehalq|}7 z#iTxog#HYJBMi5J0(nZ|MxF2tKwqj~_ge+dao}uTFw*^YQ`H7tfZ$hH z2Y&c(LuNl!fb@5x4ry@#YJd@;N`*JtYoI7Cl9)bSrj*z&G|B!z(jkh8!04BjN`(MI zu0IRjtm`;T5os5y0U~S7RnVcDZU?9gS-Ia?Un+G{>;Y3L^@ow>)vJCXfS@V+#2pgpR)p{M_L?rNijZA^ zq*z^DZPaTbr4DrgrKlCw7wZrJxCfwa*ldWAjcTxrZIDA~;}mo(Sz_BXmd4Tfr^!q@ zB^esxEBC+JXtZw_@*S_lkgEO~g8AD{l$Il^w68`fERCW@aYU06MsCAzMG0hvL6m)s zME;W2ZLLqRFul^Qw+JQrNhTA`tAK#Wfe7I-H%c_xHK@mBuX{0 zeQ5p;Dg>nVR*pi|pKtN!}kNWv-iWsV6XfB@Dek--VU0&m(|AU66q z3NR@yL6e+mGEP}8NUGrVE6f-ssnKW!1X$P{*Y;J^2soT&clMac4VB-vH9P4GkgUm> zW$<0V7bpZ^C}1vI_J-Rz2k8eWyk&pw;D#4En{LElw z@G1U#1K;abYud9yFH9^t>px`jyG{~iSFF>9w7S&~x6!-Lo??zIylES)v8lsY-GZjZ zLoeKbjz?Usk$)({Fk6{2ATU7 z2cDYsKn}(BfErmW#?8e+QjSbB=5-rt$X)^p$}R{#-_*Ios}c`HF1VWVAD z`t!_Ay>vzJ4kAz=K%-PVGlNw2;!cQl0kB^utQmC5+K!#GOF*bj#y~8}Azl(rql9ed z-2!F`g=u^Ov;Q8E0hld;sX%&%%zjWo0BB7E&)iLKi()29R;YuF6^xMH0&Itwm2B66hG(nPEr=DJJaA-0rGYO=^mkbL`^AC& zGK2lKPNTQv*I?)CCO324#FkBLQHdTlwZ?!;-yXyz&Y+mB&H-DlRKZ;gaD(>?Y#F{c zj39r6fmS9z46;4;M(}K{(;%||%T~`i2R>S_(Vzicu^=MQsXl-blj^NnV@(NH1b-z* zp_o)?)WP%`;O_wQMaT{{vC4rx53r?qeF=cmIb3V6L4hEtF#JDeVj^a|!*B!imkdHg zs=1FyUP0=U^%mJ;lnG#~tKbBJ$iC5eNGK6j(d+XD4R(gP#hr~Blu8Ydm~{w_GPVp|YU@LigUZLx=bv+z|pJFbX<2ghU6LIYdMV=AwCn zce*NxQmT#MF>Iq!z>;+5rC1B!-l|ws0}&*#HKzl9j#L$52&5t&Jhp+vn)1YuNx0O> zymINei(j}@YN}4Qv{VmawS%nJId-IAMjEiU6{>4tn>IcO7Y#71li5z<)%nXaXCdmR z{Z_3_Fzw_-zl2^{nt$$dq_1-HSvj~$=)WCCc{60?!b@}Wm*(b07Klbng#{`T(L}G0 z3i{~Pi)nCqglF3HD}>x~zCUG2M{YA%D({wEz$R2+H3%?2v@aJQpsj{{tx}1yl}b}g$B5@Dm1|&&afw$ALx^Sd1(9)#+_M|Q zi9#cyn@B*Bx6p1k^bAU9*Qf>o>?X@?rJrD3s?$*piWIgz&Zm+Yxsa8kq|p)tjr12- zUC{U?#{M`1@;3d043-&i*#lc4xNwSj4I!cbDTv!+Z<**sLz6TDI!W~FrC8^S| zC1zAs?mJSVn2tnyUcCIW2=2TycwIwM@O6~#WzJE0ZY&zn{@wy9$(W!al|Qi+0ugC3 zq^+>%%)wBpH&7Hk2)mG6OZci%4J)w61YxD!!WBlf-T+;?9ZRRIJg|GRaXhu(!eg88DeQF) zV~u~{yl1@!ij? zTbTS54QT>1?wlAUL>q&A#q-y1BY+_xAhEz^+i#OrH5P>^#JUjAF-o6rRGSN``V3{Q zKfq^op1s#W8ryA!3Q-SgDll_{Yn9H6))hR1i0Mktqnn}Ca7tXw8vBWGB# zxE5~0H8Ew2u5=r>QoJ9lD*(GwI<0S_8Rnk_jR*;wzB1q_E6AK*!rk55tNNMWLpA-Q z2s+RGp_kC>tFya@WUziW8o|~2>ik_xBAm(rkytGOBHlc?y{O>i`(S)nBG_GUlImdS ztt0@8Uf2G*bIpTdNIC>Y@jiaa*?K@DIL}Le85xme7(=rrwT?1GEHRSm57^MrUtxAC zHaQGzN%lM?BnVuq&+tftEP|v14)5fg+?~#6|IoYB@3y!{-$JLF*AnF{? zNt_&9#TA^75uLt<<~^x z)3~32X!J?i^^l%oj^rj0_aI1RKk?iogr=P1e4NjCcBeEr*_f!uw~Y6~DOe3xIjw69 zGy~FGO;{6H&|hWj&oJP61_B|@9prr*UqAyYEE-p?&s3Di%)`+ zrBqrtRz|2#M*j@@6g$eC?jkxw&UwQ8F5nY5j2RM$pRtSB0y6rCQND8-Tr(uVzTM-{ z;3oD@yw78R5IpdEGez!%%z=>5XDKgA%(>|VFNQ9zD4;{e3<3t@>$ZLo6gTNvD`{n) z!7?f_69GpDx`WXGiG_H3b3%M^KL9DlS4F3sZnRrVyayDhgLO;LMSz~vd?(}!i~TwZ zqI`w4W)*m2;CYm_Ng=R2QN?AA+ovuEPDp@fOf1%=CIE(G#P?5Ii)p3Mceht3XOP zL#72Cp%}X|lpw{KyOf8q*dcszkwL8FZ$KvgKG;eH0vrwRB8p%$Ac{Cf6yX){`w%{Y zQ=hlK47q^z5ardaEnW^M6x%`&LYPu8&R5d!TbmhbSUQB&BfG_t5wyx}j)wUzgd3F- z5eC>E1{NqT%Jt}77M|+^#$MFGSqsOW5jMb>O%nk?#=)lt7M9`TS;^IBwbC((RPoqYZ ztiptF-H#H|i_sar!h(Ic-}bN9+iO8%y~oO+{lhSjk0l>?`M}X5(BI&2Tw63rPIwpZ z(?o3jQw)BZ0ZB3BG{I)BzdT~*Ahrgx8GnxTWJgE@;YenLKnq;Pv-g3+j3I`9)ZoZ@ zKI!bfYp--2P47A|0+s+5hX;AX<@g7d02UM#02V|{g4PCZ*O10RG6it(I?wj6Y+wU0 zcLUIe3onzspD2nwhM>Q_YXt*1P{b+A1*N;@LZVEb792vbOz@f701(4q0J6FTd49ij z!1@W1m2mgzoI59SE+oN3fcuQF2TG)l0Ht&I*#@O3a~{(a8R(HdKt0;uq;%e6-;IRu7g-{WXG_W$n)K{?UDUI~wpzm-iL~cz&|>XAq$Rf_qFyk0E9U zQA39fuogaJSyG`iL}@{<~_hAcrmVYIiRI~RGh3C$N%xVl~TTl&wUQ%K~7gB$tW zU2U%l1BW^$x=ZhL{}Sr%fQx8fZ*ibAFnd-SZ3+>PoFC_Qq|w5{c8mk%I}hEpAv@dL zld;I!X@bW(u=eYsQR~klCF&&<%Q>u)7edI1jdl*03fT_ZBuEdK-^wZK%D7=)vE%(n zfMv)Q=)a5shQN+8po=fyx6Lci7^uGJ#Jqxp#+jef@1fRh!zkd17W&N{`=rSyp23zF zb+0et^hD~|v|zSw0IPdEW4@S-QqT)P9(*|)qYwNNdX186tFW5-MsAC#v>X-BcE>!T ziTJ#R27H=ll|yX{fa}42nzyH&&I3EoYp4xwMfIOVizprHbz!x)-MKmXb&)hg32s8{ zCBbd%{qcH_RJ4XKxQ=m+MFHo6yZ_iqVgD&xPcOk@g3qJi@aDrCiEaWX2b{-wKTAg_ zb767qDE)PMaR|h)YCuGQg(YYy=&x*X6PA`K(CSLE1#y2RwV9?tSN}BJrXUs@+Xjx6 zmnj}ZWJ1aq%DlaB$Lg0`xUBJjcYj?aTDWrANX9*-(9>_KH1aYVDV1FrL4Kl}>L%65 zEl{Ki>*ZALs)sdkbig2bxuZtWBEJFd1z|m@c1gQjH?78QIU=|xTVGVL(nx#GcpP+Y z&s7J#sJP99sTa>Yo+;!(SpjtW{@hy-lw_1i(?S%0skzSaW2$hq)xOaZwg-x6F2rG< zhW8!Rc3xosr2`R6KokECj!d+Ma*A{T4a(^_66l{pVJ8U_H!c_>t8+~IlqGwtweXGz zA{&KwS`bJ!-U-$g;gz&ebRorvgpxEl@a;fS2zKOAouG3u{2MwC7EZ)8s_>5Fpey;U zNQXtB@!!V`qU?5N=OFH8dlP_3J?8DQq*H}x@1WGvYWGZt0jJRNJ5%s>mR;b54y6Wx z=Tc{iM*6`rlV2P)jN~iaBeHjhY0!yUNpJzbUGQeprAX(lWqhqf}7>Yj2YLw85 znQe(nHXpTj3V1_VMwionjUD_w27i~qKVTqSOak)c=z(D>03JrucztnQR`XesgUhIA zd9(?5;MpGj!6gUwfgFr7us1`3%X&ydZ5wmO!P8OxNg6rhldOT{1u>~p~{A^|)yc2!7RuvJZmu(86c$I*v`f;cO#ves;)V&KY#DuzoH zgI+$w&#SJUm;9ti4T-)A0ja?_mj%o18@TCc>#Oh~ZLPu}vx#wWoh5RQh|a0yYQTE~ z{pV4#EryHzzUR>)>tU1fSb!T-C8D+g#GOadBh0aJ%*YfZkmlwC3>16pz}`>U;x5uW z)T+2|hdvyoDiyeiS1Nip=Sm4(lQkQqo&uT{?U?GD{zV1{82l9mqA~w6V|y5ob{p%S z_X61*&qe<_1M*1yj~HwNTOZ;?1D)h*rceX$#GepWJ2kh6*wpv;Gk%Bm4jkfCVDWpN!Tjg@YQ40+sd>{4S; zouPn(x9^V9bMfs)=j^BJ&#hR0ZuvK6gbZ#g&zdHJA>oG7MFJm*fSBowm5HJW*<6Lf z3=Ho8;zQJvab8U(eB>v^cCKD{WsbD-$nq07M^hz zLUZN(c-R3YqirETLkZk)@L>mp@#AJS7W8cWL>J-!)90;g`w$0{j^nt8#Wjn!1tko2 z0-(8xgN5S~DQ58$5)*LtBZHp;89<76T|Jdi?rv#EaEC88cvSpE5S%~+T3^ms&370K zL<-b@6;*a_9@qGD*gPZsJ>B25PbI_m5CdU*-(vUgGx#=wXp&A<@J6S@0POKg6}*#x ztvM;~4T1g}sM9kp^DN34z(Yl-e0fA1o7(NWlMh@c&euFFmvVu)}3+HC6vyQooNHjyZ z{zGUHX1HTrKZ15!{nI$zpZPtva^Mu=+oE~EHT97wMejqrXTmT)fi$uOdj#fp4EZln zzR9g5Bw0{^KYh@9TSAC3LNGYG@IY}@=<`4TbDsy|CPy9(oDz;`#~kq-i3v!F7~Mcr zAbUbL;vFCXKQX6&13l}1h@dqvRykknf}M@f18U9^CbOGSh~%UxM7-Ge}BI8Pf_|M z&TR}4;Y-OZW5AAJ^mA11|G9`!qmfhLxzqWJ=OOd~=Rr7D31Lh0 z`UZLD-750Hkie>Xd0;@ugROTU57u5a&Zj_XvR`iswP4vrEf~+$emJE-_E~YE6MYrG z@MmU9P49>F4{3+`Qc@j&Y=q}Qx7>LALwp*<$CP|NsgB)B#i#%9TscK`Q&$@ukLKXc zD8imG7#Cr15{JV{CDd!au~j~AgnUAX*pNlEHtsC3unor+TG-f2f%?D+y7p3-UWARU z1?OdBUw|GUR!zF}K@tulZ`p6Gau$n=ojgLRX1J=+G}hT0dy2*R1-vC=R(~`$5tT-m zU&%cBYgX6cU^62m(zKMP8R7C8{IuY!LYHEw;O6yO{74VVP`I`_8{+MUg058JtXhkm z5rjQPum1&3zVnidPld@mB1CcQ@gq|ZuTL{UXj*;e9X8V z2jp=`p?Gz3N(G!hOwO?@6IYErD%~0FTRmt8FOST>^u@}hx!3r_MW1DllcpQ^1d18<;e&!<1ihPg)G84eBf%pxt(gi=f;p5p0@rf3b_$cAy zDn7E(1#SH&;GGIWWjx)B17)DHrc4CaqG|U1aAhqt<3}eu-P_F*6p%gGvEe*P$}fc0 z_)fx!er?F_mq(g&K?l1DLc8ICszFH;qK zen`(bE&8fsp8%2zc!SNy>&Ati#%BR#LIU9Nzu{*GM2n2|zj)MyODi{KOU@9~678sz z$-yon(mU9zUI34CT#`WNQ7()g3xrnX;lNMC2M&j{4ZNwu1(pbd^mB#h_!Kr>?XBrA^+H*kjVB^9+{XYhJzqe&-7i!RQB3u8X zI3}TXRufw$qyaWqZ1%LM@a|12wPK4B$=jjqy%&mzskxHeOl?`g%TmJ=WfYeSI53zp zc&|toWAq09P?f*~m@InZk)apZqLbmsN)E2eDYPD?wAuBhOStgBf!P z55;?Pky$=6UTGBt6ngLm7vE z;^z34g&xN@Mx->?9WkrAb_ex2vptwuuAAffV88w{F79(?HW+s_p7CgJ#@$i$b#5cy z&2LUf{mYnV9`l3~fXov+*v)*yQmiq#Wvxu9UElD&iI;jddcSsAUh9q-2Sad{)N1@T z@%L~{9b@m{3md<=8m>q7$vgH;XODoddCmag4bsvvJ%nK-o5V6P0ZbzT6Tmn{e7Qu$ zh0+c~0LR7qXUcN-yiEOZbnyr}HRgbWGCYxGK-cVD{B&lI;lH5h{TCImjRR`=pF8wq zERetW$)NEESDoBIgL56HQ0N$U`~JH{zDPEU>`a>f#6g5kyiUMVwk5n`)CJ71xEE>< zo`mCnpZK3h8Z;!lo^+E8D6Dd34jmTq9^4_4iUQXd<1|PBR?mypKuomdR4gcoakf0t6Pqgaw&tmo^SvB; z7~rig_1(ct`L7*Bu_iSXV{du?b{iI#pbStG=A3B=E06ce70%elb4^@dOtN6CR}d>v zfu`A4?`7~`7*PJ$9vxB}9@XgmEFpT8clFx$mMHe<2FqIrB0De;)?v=iFxZx!D4#|y z;g)iq$VCQt-)53D-B>xcxr4+`v03`O;m^FOwSpf);K6ZHE7{(Pb`212!h~45_LsU5+RWQMag>56itZ_i?RgLBqdtg*o)=f3u3{= zE;RRo6jos3&s1VNEn|O)>$r`afc{!HY!bIk(ubQiY2!9=+k9!7xQTmPw|O?7)4FNx zCT*nH?Ejycd-n-Zk{@5bE^udO=FXjaX3m*2=bSk+TOJL~Xv=r9n3V5SF(u#WVjADcT5>T{%p~|ss+L{M6>}0# z*9I2z#k|Bbhz}MAC7wlms5m6?9OA>pVTlhQUMLnMo=1G7I3n>u#7B#x5+6c*Lve$| zhY=quj!C?L_{QQ!iI3FA7dI6*Nqn?6vADUoS>hWI-%{Kn@v++0#cjoH65m+6W%1VH ztr8!v-L|;BxLx9#5Wl^6yTm7I&f*=#J0!jt@g2n-65oROoy9v7mN)j%gqlGQY_08F zysLN@a_(}sZMBMbd)r*wz2z0#z16+#uquGcJH~G@HV@<@XVxp@5`wxiQ;|lwA}mL-7j12ZZEm8`%1F72RVD(DdbF< zoGIi?yL*wd*PB9#X{7IW_aVLSie236KHxs+zRi8;m1OaL+>h4Es zKT;1M^>+6FQU|;TP|JfzKjt1p`XKJU4XJmyhmbnty$yFC@-}()!o$dY+gmzEkT@Z1wio+<~`Mtw374xjNBky}vW(H8+fNzXZZ#bSxHnYh~|z5adz&+66-71_~aSy#o+h}#L{P$m!!_&GtZwqS~~pX z(ZlaLKJ!lAdA6=!pPs9lrw%vjbJg>#cfQfMP+eS-x=xmr@A1Wj%Dh*(P^$V%D{MM) zvUAlJ(O$xwKfQeJl=s52zAb$BtJWGg(Y3_o#A;#zT+B_nDK~xI zaxeDz-NMy0MzaN6j?5IZ=cRXR&4E38Gy=Ua?E`jC-Gb`F z%%T^}H(a$1iRObqoW^`Ru6cjI)2Mq+W6qg#SJ8^qdSlfzXlgPYX37NdiVHGnm@`ST#4%5vb$HIx$r z{8+s)HJMU(vTRzO3sWv|q3%J>q+QIN_k#1PvAk5wm&&f&5L%hE)$Q_t+_`S4JxIO! z()6>yw?9q#EuU}H-SYI2MrC=?s|Uzzc+)SG_uy-f_aeHYs-8({eh`Rr+r|4&r-Q^p?u!+r%Srrv}xPa(n>gLYKmdJ3n2Lp0xOYEq!PCI z%OvbqGr3mUeoalI%=Ls~*$S@sL)wYyWiJwewUAiD{B|v&b~lGeFSX=l>#S8DOIbl` z<=%wFzvr#Gz1MP+ppjG=bC-1#qgQ!OrBTCJT`W6Q-@y=((d_sQCzvk>PBn1KK~Tm( z@tuHqEUy&$gp8uv$UFF;U)>rCH0sXTYTa#I@~850Bd&Vc=ks_N<7APelV$bGoB&-u z<(zIf)j7PDq)B$;HuMux&sOnjs6sLnJRxu8m*WFvX)KC}7pxCVnTn~WKs5VmA5}-MC^yJf1j&poY z>Wl8`k?0VKQum%cEUP&OGEnuRSFUS1?f+h>uIt_9InZ&=bVRz3ekA384PFm z2K<*DG!VV9C=73DC3@9vbXT=9&ms-fS!bWR9CUagN`qTDQBi>->g3j_+wI`Z7_l;T z*iCxqb`RWWxRT%M*qC}0v}!G+7Si<; zzb_|(%xWUYF62=9IhOvQHJ5NRZuaHeWqZ}eI|f|b3#`(Br1I{7r1Fv)G^s(ETJq&w z%CgSJyiRaoW(5ZlTBYG~3K6{MiGyqaG^`oG!N(q$ddT2Q=Qzk{4FOQ!x|NWgqxnb3 z&>;zGVg$(&1$fu=B~=ahPBWdU@}*9AJPbVC7n+-$ed!b)+kF<#c$XnglAO-Y&c;I; z{KgDs3|pblOJI1QG5tkbv%K0&IKiPM?G%q8!DR8vvaC5UI3kjNih zd31I*9$$DEgA>o<9hWedIkU5eYYpFfQs!OUMXgd*G_r+rpvk5O(NZ=Vvu`wm>tTUT zYAl}@W(bH;xesQFafC@&s=MS>m0S0G@GNbGDBoB_ z7M}wnlG=b{wqDQWl`>w02fJFb+W`(2%PXh~d<-I<541IwAkMlm-&s1 zZQ0XZhtBtxeUx`wnoAq5+E@~Job+d(xMLV!8qv z4ZM;hb{M$#LvtuE0xWWb{Zau1mg99$9wnWMhfaieK82^i`$%Z2l>;4ac6PUO4ovYp zgdz<9pVu?9??~+)r!WqmWq?9eh;T10t0iDFlI7kA_nfZ!J`|?u`}aQ3#yZ0dODjt& zC6K=lg`rff&oySk%(-QDZJ5OPt55@qK4fpS|A4BjV%b!?<6^AJ4mj9jjg4xS=XR1t=d<>_!cs_vbb`%2Ck(k^Ueqh4-=QZd;_1!d}vo2 zR8^^1o>k=~2>)S=s5K}2&8xC4+v`r{e2WkzBEC1kBN424?&V}}+bCW|N)wa!DSx71n5tMp6-l?d0 zhWRRZALN#L#Z&J8=J{_c5e@WvGdiao6R<#vs znb7(x?eiuH3e%ER!R3D(0m#BK>U9D{wT)!70K-(m3es1gECm^cBqESmj>`6x#O36w zy*99#bnR<4@2%xmlM92_@XTs*ZRi@xyVi5o*_YFo zGknk5=xUnJA-)0e4CA7_Cug*VUN9QuA~janD#nWA7wU~m^-@`#_e*s!KOagbZlEdB z8ydBznWQG6C`g3EOSR?m)w-!9EEsX^8C@wwm-AI0L%%o(?X(1DRIY>QH$hilDxI%2 z&XsE=4F@TILZsP#`I_N&-toe$_hN-ZJ>1BNxBG5dCM0zRU`}S$Swd^qef2ye8N3uQ zhgpyi1T-&9lFX8zv2MBQHZ9VZY7M-BY_v*{fqwEZ|A$eT*0k*mbnSd1BY*kCP;w}l zLp+B(QNGTjBuS&=LwY%c%coxFSuiA9G$I$1Udl_mHjJ4t%ZL#z6X&GejO1k9ELcu1 z96UF~7E)fRfQL#eEm$D`b-sW-xTd^WrX_xo(H)24q7R*->qW2*gp$ zi=8h50mK@sWCe*U)|##7nbVf4;x43(Oi9=B5|Yvf*KJi}{CgQlV_U<7ee^51OxI=- z%}wn<#dH9+lkockMxX>R&d{XoHAr1D30e!bj>o_$0ji9G>iZDDJS&iWh#6gwvCyyK zGLQ`c=^c>CIG7yhM*1MDiZLH1tMx$1`#!)M((o>%U%`!k6TK*x7*8qYRB&~C*u+D) z{BsB*q?7y8y!x_LOnIqd8W(tfF-!A2@^apQo58#Q6FnAz^4_4NV5CQC2n%bx2Xj4A z!(M?#`&Dvh`tI3wYPBaM1MZSxrHU&rf9%fU>?a3n?W61%|=BGC3RO?&|Jk_^1& zQ+hVsA8ESUxF*6Hbr)FAFpr{IYI>Mrxz6F<;W!lRsu+q|?R94N__I@7)Hreas2Dai z(!vx)kG38aVGeUs+5IA5@yYTm+OlsnCwdw(#bOU_UppwHY=CzIP}$$?=8Cxssg*Aa?$`P%6q-B zYf`bczD(>0Zu~y32+l3NaBAh^^}9uA!=5>|ey?Pzl;KzY@b%xN3*F>hH%CEQ6#PGG zd)MaPc1fAnf7Q*gEmOx=;uqP?J{_nvs>#uArobswU1jhvf^dM;rruI?OzT%io2dm+L?RN#RQ4%HYZtb3z}faA2lkW|oqB*a23?Y`diz3TYdX+yzc+vGLv#Dp_tRrDx@v)n?tz# zzl)%wr?ykTdL^M{a!kibv1De!sk0t9GnUvjN6sNN2#$>9Q*h(~+|47WFlWnR`UsvI zbvNM7AUHnDjkz0zkB_^XUbc$ECU?T!jNF1rZE?3EHDXfR+*^>MLj<4K&J!DC!Tt7d z^z@2vQ1%2C#MFxV1R64P48mIytE>nx6k_h-AO4GQQK8(wM4gn-H*hem> z0?3)GF#1AVNUp*vQcA7bP(dwqY!&`4P;~cEy@mZ`d^PDGppsk91+dF5xiFjzEtP}HJZw&GwSskDh-6|#xRpgyrAE!w!n%wAN>nn#hS^|o zi7XEi3y31{u*xRVfVA^(ve+7fMFy=wC*zG1JXB=MA49}~-ZmuKTP~5a#}R98?e5HW zK_;&yVJaB?C7LYil%Cv;h?`hLUsA3`Pik}ecAC;@99VK(#?rVlv?*Or%`|r%cVFueoNaPd+82!*)a{4#6k5pPBK2BxQ%{eG7P%J%dpL94e%_V#qxB1D{X#MT z4at(ijzC(CKG-RJAhtF3f%oz$4WmJe zRo9a<&HJ$6#Kklq7e-@wg+ui2cjz!63u4Ak;-$+?d$)5vsnN$C2~!I&<|9Wa{U?#5 zeu_aSCZVf9SA(jni3k2#mOq4ImYusnD`4S<{f*}K9*lN8)Pq%Wdui|8h-i-zZMYI1 z0o38n#*ojbpVsNNk*?`h#;s6mABjyWyPK^c zx3(~9M-$D7o_5GvI^gFt7{h-MiFWt_H@zK_M(0Q@!2WxU#=NflwX_)FXh%uSz;IG8 zFF`f>S=1H|n289+SU_i8*X@%5M+=L0K~McxSvD77{TQZv&*)XKSbN=au=%&c(U=Ny-v?TXKlMFFI z3|sL@Hb7cIVvJfL5O+02@X!whtm1LDB990MmPSEyb5BdOq}5|yi0Q3gL$r~B12QEb13j6TSFeL zTT|#asYkDygk5y3Tj>95cvK7YKg1YC25k+JS@mx-NuH`>X@PQ4#- z^>YmV34?#ipbI19XQx^ip_v@KY@`L+6xr$eRMkDKNlTbpBj6GmI1Ux{i+B|vi%CbD zZ~g^=`j>qmBZA&T$fEHJOVP$QxFQ;v!MN%&5DJB`WkPUmO>SZNmvQ$*RJ$m48O6LF ztaAJu!sUMqK_^yW8cK@_V!PUO48_CELIKFR15gK`c6d-W+`K3nNvH-%qk`n|6w4Ld z5tJJg^}~YUNat$xW7wpqgi!6!_p}l+;!^#vpm5N~Z(BGLI}m6Mqj}aS9M16*SbLuq z->b4-7j{mZ-c$1~dNna^I4z$9vkFag@0z5ugS!mZPG|NU4bJd`aPZNp3v7ltjBRKG zb8de@I~Kuy9-XFbn$1EG69ya>_rYN?xpFK4%EjtfVhsw-LK;`*8kLePa=4(I1tDPt z4i1euI5dLV<<0=f3-SCa7K_1&n}Ixig?WR)&?;y`wo4tx@ zv@{4Wn9+-t`bn0?dj>_38itCr;R@d}?BeaEF%gvp^l~yZ=y#zpucnuSIT-Pm5I2_m zlvwiBG3Kl=Xfn8lU~;4SSByV_0F06nrTWXvc$^vVr@dH*15%hdQ95Qq?# zVLY$8(5B&0qR0cn;rONxPc1#ZNwDx(T=9*OPZmKGDOP=o0rhE_`y=H-py}cO}@+*bwq^H2LxGwOlrr7`1aS)O3+utc&%x$Hm&mcD$!k z`v3|=(!sarTUcFaP8xj+>ek%rD_mG1=u%3goxg_HwDhaK0uCpOkFf2LVnr=P{$A_X zI-`WBpTJ|WnvVEeS3A^a@U;3n4E`R2F5Hk;euD7zC<(Xq^nJTx(J__a`Igk#Ed|J+ zgpNST8oi(9a3dCsoJox#=cd4A(#;1?g9L+?yA6H0BBC@VMG&n3NF@vThQN z2`+M|$)_(Q)q6~dG)tIx)|Aez4TvydE#%iSjJaf`V4t9XRI_v%4g)O$*p2lEarZYq zOFlEW=>d*ORk$O-Q`0aenQL~(E=^RtNFxmgen(i#2O%{*)tJ&U+ApBFGn0dwPJ{?r z#3Ex_=tPv3UuEA5#ZXnzq|}dMc=t0z#TG=YWF$rNJ3>l7w7kAXa}ZVape5D}(AlyO zj4n>CiRwla5J4>+LH#pS(V?SBTd29noS_FutIhqI#=<|}PGwXO^3=aK_fXl*bRG8d zn`sf51X`8rYSlkytIRkT{7I`s>TY4OYuNW|8lU*3-lmPiw9wtOn<+n>a@t!Ku{|wc zX!3q_$JxktGNxaeG23M%El^UWn~6kOX;K+EN)WZ4DEq5y2&ZNX46Ao?iKN-d4PC-) zm@SpuMg=yrmfpiDTdO?Zw6^nkRP{cBHh~D_;B?9!ofK^+3vFk#kSpXA%k;>_No4XP zVnerF3~4n5`4p}ca|w}+bH$9TkJDKOZWS^5nrFUsl4^kU9wVmG#iC;lw2{Hirzqtg zK+sMMAmI)>6Lpq8Rwm4lTFnx^A1T2?$D(6hMYXT5Xnm=jm^JeO+bP7o(O=g}qh9v5 zpeS00*9suHwk$Iw)vxpI{~EyzRuo=h;&TY##wdPCTEqHoX18=87We~##NM&&=2#1m zm>hFmKBy+2VF)Ulcm?~&Ol*j7CBj+#S2G{*_8 zg^@_25937ZVO;(w0?4Rm3&P^x^#Ed2q)qmA)QRC+w@;@01c9qnvMI!{bJ4KPFDab2k0#B3GIe z{RY~)uMZ5eZp*7bYaEWQWu?qO|a3kSPA`Y;}&fC4Ne94wWfY4a7OQnC%6t=;_*m+I=Ozoa+gzBleF0>y5M8R9M^xUeV-h@)hO zb(nC>f}+FjLmC#Rt8^KJ;}@w0WCf4f4A+yW}n-P!4gWUDXX4)>8_znC#as^39Y7hvjlkvB}3a6#*BZtR3d zp6{`Gz2Voxb=!MU{a&B<{yy?X*MF~m@MRS0d3lVj9k@$uad`)14F|n|yMSOfxx%GP zSU)ZRPeSTg6?iv+5KKwW6lNL$HZ$sNW>^*ZAa8deEmJCaFO*6f38tV727wf`md-BH zg<_&YJ(wQb#-FD60Qv)W)JGJL(E`t!c|lV@m7J@CEKn+KY3rqKYB5E=sXmGZq51=M z!8-j=g9phiH|qx$>gk6xYCVigW^HOw3)YX=TybAW;5+$Atf)?6RXK9a)RV$4aQerZ zncWV!(_^nDCS|1c>n26P&w0Hb(zc4p&3)(ZfK4W9we|eNjQs}&0}OtE!GB?pXYkt$ zewP7>H_SB7ai_HUA?ExcgD*4qBL){45VSUu65^71gjD!da`YXDMt4&m}Yg`itbfUz$L8GufDblS^EivF2&-P89X z+w1_GK#XNJmKp}#%^uzNBDoU%y_1Md~;JfT=5*S$CF&+-!TdJqxnKC(%LoYA6$XNB09>$iiKSdWW1&tDGdpo;BTSKu;2t0#V^Y)I;=zR*3 zfRK-a*Y!*qF~X1FuGqV7j_`!uSgS*WbS_+I?+UERe-?F}0TB!C1>>(EM>kbK`JYHe z0*YjGO~M<@fM_B02%An(zMT@d+oid^qxn4#c2EQ7=(xp)ka&YF263wcpp0}|Yg4j7 zg4UAIk9D+GtUYtcR&1#5{eI*=sx3FIR?hTmC278STSqIqpIg6?PXhn|j)r2bXe_{b zkU0I&u*rN|?P6$FQQ6E#kWR(vw0aWhv{}g1hS2*u4WY986##kd)^WX$)1RVqO#^!l z-R8zi@F_0WI0jZQDkPJum%`NY&vA3>Q95Glp&=Gc=|Lfnew|lfC9SS|54&y*t+nht z)@wT0#VsA3*ZJJ~U6*g`Ix_g?!)NDlYzN%y^wAsG_*;q34cfCuPHc!7Ik%^pR!(VF zn9#$aUsJFhkuw6FkX$x{Bnp$n<_@r1Wja9aU~mr^kkuHlW>(CxiIhj|v~bOJbZ#vL z1B9Cmw~`vorh%t1qsoAl1!O^hh|hf(l=>=)&lB8;K@Y)+i>qc3mpegkF}}&fF{}%| zfG69ww2%b!Na0M~CP)1*Cdu!#xb`*1MDqGm#{Lh3R~fv?E)g zh`<;FZ)P|%qzeNR17rC@zA#isDV7NfB_pgnBc}&}OYLBt4uXz;Co74igC9f!E`%Io zI|8iea+D#2gB?P+RJ!S_7&|DDL5U+M0b!W>ij=TBN@P)@h&>PbFM2MA-1jq=&(O2? zs*PAm$8_xh7TH%JW`9zTaL7!PLwcX3cz>yn0Y+_k_#cc>ppi3*6wSkD)Q=%-Zd**qz>Wo|2ulSQ`|)_q+pQJt_O5Q$U^kg;w?R((_T&-516ZsVgp`I z%4k36ZYKooV4yvryIm0u5RA(f96t_w91`&c1gKxR1%*opSe}cp(rQJcf=?-qn@QgXoxlw-^kMsv_`S{w%15 zmO?{Bj^62OoHg_YUvnM>__Bk|?bu7Hs_8~1ADOl8k^JWlT*&)aw?7#Z|GBa zaAFo7Sb{rjfE7VPWng#Pwk0*r1!ZifcjaaJ^cuY)i1J5V?YB=K2!sB$JckS2bQr+w5?agrE)otgIHs%~Nd36P6%-{?P9iYg+OY~MZ(nYPw^y))=3yRJEMY4)UA_Q z%wax`Q};YiUNz*WyYWHsSQ6v9&B?IE5*?ZWBDT}q^6J)~~yi<$O&CQ4P z4mq<~K30VLJF$=-iy}3SNVhh8jnW~s;HM6B*6A4djpp0k29phhai?C?4xto| zuTwOzDJW(Ay>)4K`DCkcP>gQ$Y23}U-kqpLD~JlF5YE`peI0GMTEK?j3CyZ`y%a}# z=$16|rt6)%{A1AaHbG9)cX&oaBp(Xk+*~dvErx^U8QHDTPa+&bE%C#jV!J4fa_*#v z1-H>#Iy-r)y?l?Hg6B2f+C*B7Es+2)iAxXn0YoLy;XlFmvksVwclN%wx3%j7Kw~>a zQySa-0{2$aM!Eoz#V91kp))r5Kq3aV{OtsMNRcS1iuhNNL9H9=Y3D^SRXiH z$ri0d#B;RoyR8>trZQEUVJtwXe$c%F=sp)i2Yj%Ru8uKeH z7{JJObd6Bzik%ak7q3qp)6xRh^xlibvXM&Xq@k5G)E<9mTqZXVqI(Dt!YejD5Zr=I z3!r>M4TgCWXB}W40FJ-87Fqwy$mXG{! zd|@e^RVxPtRpIg_rVSc(Ba`(YZ;lv?Vl?d)H<%g1bEB*-ddC^F(5wuaf1N!!hDte` zYpVwo+Z*SPIQPa95W^-#6i}5 zgYp~>sL?aHZ0b}&KeKx{8oet*&u_CsNP<=pHtXBFx}>}GTk4e~N1%M@778J*(+`mx zOdUa_VD{c_r#c5ZUYUwor}u>S8Mfh=8^0!M+<%Q52T$qd9bJvW-(r9;7@FI=0P5n{-y}3ytU<}v?mnHR@&M+KJAk>Z3z$x3jXg#)oo8Th zo}^?@!R0p*a5OXu(U{r5pno0Can54+?j8?6GV5g=KvBg%$`r<=nT4-;lx5Yri;oE$VZMoufhmw6l$Yv zYQ@qllo&o`^dg+E1zOALku-<{$s?nu;7`xo|@pO4Wn3W#m)`j2PYF(uMe8Vj%ZsfYG68Zx?n@xu*ZwhW}tX8~VvjK`#Db3@73 zRuN2VM1kU_W!+AX$vFmHroSO^)~V>3IfI@Mi!1i{qJ!DdIqdVr1wYV!S_~b1%(G|I zbEqCiv`Zzp;=O07f$FF}_>OZ66?lz7a$c!xvREY=fV(VmfwLwXD_HRqa6W^`| zq-YmZ_$`wD&~NO5o~7^Y0KQuW?(@L63HD+xvSzLi|K=`mq9>+1+Hm*lBOrQc5=Vpv zYDJF6t)R!^591`N!?BbOzro&EWpI%J zO<;;^ae9p`Lzpn|EJeeJ?mFs^OQn_z%n?6DM&dr)^U*uh{YgS!~C=8t&&NR0pA z3#9o+@H{xy7@VTg69YJPC|$@DGGnQ+bRjjC868AU26?GM23{_ssl=8MOzMSPVj|VL zMsW(J)L21f9iKE(hj96yK+wIuWX{%sI~CSX=thN~PQr>?4r>_tWF4M|#*{wm45y-r z3lwuAw`+Z0yH5?7(%O9r4%hN*F++{zx0z?R00IlT&zR^>(C=o3NLQ?ADcO9*8sue~M@K48*@CLr-i` zcJC!F9rR-?{UAdo=y+7`Lk1>ktcpTPp}`Mp&)V9Jkls`YZiFmxzmSe$(v1uYS+x;Q zheH)DSjb^F@?vNPCQlsm;U8s^E}duElbn@}zu0{Qwgq;J1jVGpA@w-ND3_isIYU*u z6T}u}gNU1$%VW#KgQ$kWySNzd#gZ_^H=CQ-wg}RHO}t>xE=tr8 z1|Q}&7zlv)W;y(hfI*Os$*&1Sz7x&gIt8tzHx*)&qCKX9A^gGi(4oX()%iUQIno*gyHmI)PrI)I%Vj!rSQs@6x+t| z1--1VNc4(#SB{9KUjR=n28ts`{|Ln`SCS5j1p&3BhuBO>%(ffCwLW|QX>GmLmXJAZ zQ@jW3rMEdo5UU%X|o_ns1y!&`uQ zJ$t_z1Jp|$P)EMzG1S^r@n(U>Dj9u>lFhf>++`|K&gn6we^_B^<{0d4(&1tb=-1)n z0DYODlgZNM3tb+WJY(S7EDZR?!A}1j&PuTY^8^wY<6;!18Cd@)8P!EmW^ApWyLN{% z^I(TEquYD%OdrFDo{%`i^!cPXdLAc{8hagof&pXm91gX=Fs0rDK={Kn418T7AZy6=lpV`c5LeOkK&~ zTuy=c&Ldrm-d(5{oExvEZv*P}fj4g&_hDKdl(V7wVPEa*gK>%rz_@Pa53hsww|$54 z-u(I-80cL)<8e^X?EM%pdcfmXklxxjpL4r#{TFzce}Tie&oJ+c=rj;L=l=LQ7=GwG zjp1*+kzxJ+2Z}i=-vTFpkfZhUZwAGKf?_?CKf4a1A4aW{hxI0UN(bS9c>3W;YF~ce zK_B};F+iaPo=FK|ig=Jyox)78vIHSYQK!=m;UZX2h|^p5sOYLe22|YDFaypc#e(_8 z(kcAT3J&#EBVC#NAst2aL(yne+}bM6J5xCAi@$5jcaQP@Sactri0}o(>ar`=iQ;H< z!zdNL8fT>xpo+uMy*hpn!qH&!zeD(;`f4j5{c{G>3?5?e2!m%C zoM&)|!90UY46ZQveg>an@Not|%0T91K_`c8SO^*cenF<8a2!4xC34(XSnwi)Z!nyN&!0YiD>=Hc zFjCku`UtFg+r~!621fG*XNXq7Q==n=i=*3l7Vwt6<0r>A6u!rLbo{uTa7J6#{{u1x Bs_Xy& literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/compat.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b36afaf1c452bebe770975481b6377cfb29851c GIT binary patch literal 10853 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/debugging.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/debugging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5361f873cc86b2dc409837de72b89e1edccc442d GIT binary patch literal 11462 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 MlH8Wt4Z2tV2ZDjxumAu6 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/fixtures.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/fixtures.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e44cfbec1cded4c7a6535ab88693d5859dfef0a GIT binary patch literal 45664 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 xb0YgcYWE+%d(Ih3&5=jVzENf^Qb1~-v7;zrZ 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
  • DTLR<(YA4-7a;XOGR%ADW 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 JqnMc-|KD8<;z9rb literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/logging.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/logging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6438c67ea412f7266950b620daa55100509f4f17 GIT binary patch literal 26875 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf9e285c4d7fbe0c2638b93351327e1963efd280 GIT binary patch literal 11246 zcmb_iTW=gkcJA9;I2>L@QCHh-S(Z6oYAD!-;xI0*7;Z$bgdgORrc2=W{9oQI(EkcZ^OKOyUdmG7MD?zu?H+Q5#) z>gu}JsdM?xsdi>&DjI$_{jZ&Wenr#%n_fnL1-!h0zsS`!&D9#3tGkA;H*|g)4TGO% z!{n#cu<$hfLZ{d$>eSZqOPz9~%=v;p)u}WpoG&6j-I(Tl3HfTH%K0+#GmRO}Pa!|s znB{y0`MJg%=ckdMZ_IPPiu^)jf%7xSFE$oAKkFaqEH#!mKj$CqEH{=pKkpyw9B&-g zHDMf{a2Foxjgw-*T|Cemr|>+2=V`Glrro6zntSvM-CcH%eQKff8S$)m&OQEx;hu0$ zerh#r@f=F1?9>;Qd)j>lWzXaNjF_j=Ed%|Z{lavgb8XbT@V4eY@1FTobI%+YjkDtH zf!26YETQxT_bf`!9vGP8oNIib)n4q^u7$x}vDbCt_O=(?v7NxRJ7N1S<+pl4JNClB z@x6Gj{uLe7Momv<+hGuk2XV&vp3eweuT*UOZf=XbRab_)o+~0d-WHLtJ0ji=UA*xoysbSu+}siEI9jz` zPo0PK&^nYnQW(`muEgdfc#a_QD zd!ojpP4;z#KiT(pcfy2TEIKP4b<|^%K6M)IF|x#OH4S#YLPwnMs4GQR1n%o^Iezr? zEe*SUi&^#}ka4@2j=VxIPu@g(+x28x&&0g!`^1Odme+QO(k<`xEp&$VB}cnk`k7`HL5i{3vk1QqqXpk2`760HL3H22MW_P~VdDsCitUD9s9 zdW2tp-s^Nj8QX$b#r6WbZ>-z3a#D!8Fae3#4ZBID*;K8X&7=edAoY;Uj`LG8{SFVr z%$1lCS}Zt05Tdbgn`x(7NuI>aNda6NN8lWA9@KjX4oWO4OAPTKDHBVy(Ty2NC8Um% zk`z(s?~2#(5}iY$85N_Vm+`Nv8~QBLqG6Z@p0oNqN-bj*Ir%-btXYW(RioxNu`+e+ zCXJH*EIbLV@Uh=O2FHk_ALuwnI;2ZDBz^Ba9FmDcIu`?@92z)i?LglFkq7!i6Nh2u zhhhHQt17;_RWl?37_?jW*Z} z`V-bBjP0+8scSlup;k;PWapyT3ED!wfSP2ApoL=3qGax7#!!1NNT5?qzDf;?3P>at z^LbGzy$Px&3of+Qu1E!g7V|Uojb@Q(`eMd^#C<6tCET-HS zMMX@Ds+d8a$`=K9+O2*H9om>h-8s}yZB{pv*Uh=JTLvICY6(&!hnAchww|Nb7$<9+ zwV+;AZcF3N1EvG$qve8F6i37o*Wih|BdA+)k75r;-IvB{spm3kkGaRubJ<LAd}Y(>HPhtj4MIjo`o4IL*NSWj5E& z--5|1;7hN z0;>izX(x9tPTlYQaoRLZDsav#Cqg8ae>-NR8$m+?mP9n;(ej1rO(6~*NLUkZ-aB8|RGGFQ&uy*ApQ~+x*&e0(zR{u_18Smw{iV_EXU*gyT8t?tS}gS=S&vqbz)EWS zdVg`ClQ(r6R+Zq5t_77WcOmnweud6&Du2^X9ZsAJET!_cxKgpwKiN6K>JqDCm;xr-OGHKZ6RTJV;jN& zuN9K#lJX$-wWF{j6j?#yO#z&u2+BSU{IX~@-$c~IVUVDt!G=~Vid`UkP(6z~kVlM@ z?s#{$VfCeO?h;YSJ@R1E{4MfRs5=(0Law$@XAxEZe`Thm_;&vQ& zqxH45I}qRACP>!FTnkt1rr9+wir|i|QIAWlrjkS=9>O1zkQ3Ymv+YNT-kFq^QY!PG z60ftc94!K1m5u(95#G#Bst%L7j+QABX1APihr(HetFj;E$1OahTmBHGyo4_c@)G6q z(d4`IN*Ly`C8oOI?@*^~y*0kLw7&fvrkC$MK9RhJvVRy(a&v4zq76;JTI`>YarONI zSt=hsP7LTA8^Dkkk<(-Iz=&32fFQDobe%s@|$^JBJ@N4Q_1-s9LWa)h9k3(DzsulVJN7$ig->ZlwO}*dsv*%cU7Aa zW%3r77ks0_M{Qy6g+16ENF7`s3II7Nwq^6yQUgOySfS<2p2ZeC1m`5874Vs;y<@v! zO!*P0ysA_O89=DbnBoS?juVe>ifpOFO%c4S+qBG7#)fj-8a3s)ZaIUn6#P6`7^oEA z>1?{rx_xHtOs&-#E;#Ig7GSni=c@K2@^7>JLUdbUgkps`N62=Aq7~6(EF%qG3>WW) zXhhZ>Dm;o|%L4$Fp^_L4ps=pMdP186NJGKD$zJkCzYQQjOQz9RvJ)oiq!FkZS^CL( z7VBC{bB>y%M4mhw<-zm^zjG4S{4{Mokm~yTn0@ha541Lylwcmo5tn}eQDx+=z&^R( z?A5S;niQ3{q5Ra67qM9;7nMSna2HG*vLumDiU4$1>KSKfixxC*MXx{u8;`7lrO)Gy^TjFx1B+$_PT8Uk&v~df zS!HqMJ%_+SP)HIAY5K5A3-$DIwBI8)9i<`I7I0 z^W=3({+N;*l#n>d50E6KbSVg}O@IMr>YLP%ghzfz2|MOP@BB8({u6(ZjYOLpmz-J1 zjipzNDxM@Ua|N>JV8 z9KqOY5sSZZa$>J(3ye1$>d7i&Y_FtWC@hG0*fK=_HAKmj=-Z$G?VNCieu&stY~>U{ z+3VT<|Kf_0>s&}#nu#DIa-7OWtChw3A@#6_kWUha;H3#02=b+IJ!(fdnQUJ!mJ>D* z@t>re4gBqB%!HYwF}Q_jj5}K`;tSy-g0tYQNlK^fKCJ!(US9HRGdj+7@?ny~^i%*&)KQSJjHFi2;3a!#N8n!35m-RuIi?-}S|=@HcAHN{7CHs-%2h zT?6ArfpT&Vg|bcwbyopQBaD(#x)1qN6ldBYXEgZ{HJ;$yBIEsA;oGL5zxH#(UsczX;hRVXEG14EgZ;mf$@EB?V7fRX=SdiG()hQ&-!8^16 z*%OrkvB}8kj4;{%uNNK%$KOpcz$vCpZD5BEo^q3tKS44^R^B&PO_CLYj%C+!om)g` zX^eoXL7R>~`;F17fl~e^MC(9=>3)6(jwIZ@bFsN&LB!2pAfQWuF?v5NxQxRN^oP`z z6aoUr2hjQ1HzahYGcXU}Z?l_M=&xT__sS$Li0(s9!KsK?>_zqT-G^&I(vf;uh#RSJ ziMn0`!yZSdvDf9R8eAy11!6qv%91{*SnvW!hv(vctl4(j_zs{pmCSVS-a%MC3VX6G zA}O#*`4$pfsO^EfY2u^^ST1CoUTUbx5k!$&XqQo+V*08#l}%dL^})E7-vFmcTk~sLd0}JP`74(|4oh!k159cq2#YtR4h0IIKUZ^NDQGY zk!(aNuH%*cY`Rg(Tx|Gn4#G@)u5~&_KaC>}mElHG;JbdfEmw)6#Pmr~xv%mTm2@a! zPl;rlPX{;T3_$oa#wCE8|0t(>fgBe8XUrS@g;Bcmv~9G^e?{b`@W<5t4`k^2Z3mRY zMfA%bnfs6jXb}Wcdb)g>s7@x00vPuyxOgL@2N}3mp%C&8eP^Zr!nml$Ko|vMtYZvo zzmSfh$v^K`a79;m9Z}nRzr-DAac_`aW#9WHuCn3RQ2j;ZcudHe8WZD}zy-SeSIUtI z&1&eHuu0*T3fKR8Og<0Z)On*0RJhe4;Z9 z%hEu@&p0>;3YPK5R>bkr$iBm0Mftd;fUgw@W$vi4-J*VcNr7uh#D6%rny-VJkRR5I zL)`k}TM`<=?a^f?qQ#Cp?}qn-kqL2aM^RTqPMP~DN-!Fu$*)@o@xigd$ZZ)$aO?6J z>7yWgJT?9$3RG08)NnZh&@w^<29G?{W(bkm+{;Iz6-*4HLbv2q(kC!!h)1J7t<5B5un?}9@Ld5(j`FK0UMX)= za)lDIDy(qjHsx4wb9rZ(tre9_53k+H>q#m@HzhIg@z;dPp2XV39R){J6n(1HjOjx_Th3FmKnX!8 zHu#c!JK3hhrNpO%l_5pVN9e!`kgk{b9_+4LhMx?I3$a)t`$ zv%>VsF_L6t8%ML^6t*@A6i19&6Wk?qx}Gl@OE<1I-??`E{Tnx%S8v>S|3-7;^0liY z^rtVFR5&iC-1jIsOG%Fs`sP4I?8p&jf;{OYlJZ3rVZB85C8EoE!~oKN6fJw~A9xo3 YjEad?_|RtQ`26Xm>dDG%p@_Ht15{>nPyhe` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a21b0a9ecb8a3e176e6accbeffeb3354abce255 GIT binary patch literal 18158 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 N{d_(<{rq(9{{kMsgQEZd literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nose.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/nose.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2df776d8bb22c565aeae73a97236283b7d23855a GIT binary patch literal 1452 zcmZ`(F>m8G6ecNIk{vsjyQamVlhNS-4~d4$&?|zVz!e3$xImLGS_E386Zs@kC8@h4 z@J$ZTEd2)^+VTF9)=r(fwL|+Ji zu=oSP4h4mmCcs!`0I?iHBHFeTD+m>*dzA>e9QcaiIOqI6s z@ONNDX0(f%D|ew4prlo}$2!CpTE8mmrUD#(<79m{qgm|CRmH6n+ zj@$xK^M#|gWW@yCF#DOuLBFDKe-*9ZJ9?rXgm9A|HIO|`7 z!RjNJHi6K;<&?ambD%hJk{hAls2pp12iY!!vTHMht$$vo-&<*HT3m3&&$SXf{Yn>Y zEtP|$mgx`t04@jevXQ3j)1V3{kvB%KWZ}|&c_7v6v?>?41kIiwzDO;+=AhxlD}F9* zn)mxsuW)nKCC3%77lNNW0v>S{j2L>vieT z7?$1she)B!UTeY_g^1=A-d1aqBY(j;zsqFns;Me8BI`_a?J>mfTr33k|Ac z_Aorg@C3s>P4v(pO@oZ^ri~yF3aqHl&6wGNL>C!o9Hai9;-|0+DLT4h8>XLn9IV0( z8$<7z7;eu7-Keys0_jvC4K`hteBG)7^)^^+ws!_TyyRBCPk)Fk7WEWnPd3=4(ciw% z0*|~KApo3Sz|R37qcs&|6TD?0qW^y025==UHAm~f9yzvVAn_4I0^bZf?Z2B~o_(@y zzh!v|GRWnY^w})v;&TbD;7k`^aO**WZNwLq1pQIyghT~teGZIKO2yFI4wm3tG= zrzuQ}MH8@KME8S;djICLNf+n2&_$kS@s^I)uvcz(n>sACT9)SqOV1{ENu`Cf2LDWa rr+I6;aG`bephG;7`{3I*Ij*&6D|v!{Mix6H1YZ6ZBy==PqP^rFBRN>S literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/outcomes.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/outcomes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e37526359d057d0428ad4397c04316dbf4a0de9 GIT binary patch literal 7458 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 a)~6+Z0H5^^Im1H(X7;nv<9iCbO8*yb{+!(a literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50dc21cf75ffffbfa801295f84c9e08a6d72a693 GIT binary patch literal 67648 zcmeFa3wT`Dbsjo%=F9_w0SJO1_!KoFDUqNEkfNT3Ng5PLij*jjCLzfhNgfPv4!{8i zGvGc0k{AyY#Z+uMDILdowTWDlzD{a3b(%V9lAAQWNmI9plcu?8+M7=ABe~7XdELZG zoS3qu`~Pd7=U_ltPTKGG`|gnEV4rjL*=Oy&*IsMwwbov{)YFrV;Wt@%qx7+KJoaCC z5&orcF^0pLH)Am?R)|?~%c#T)hWt$w67tt9nDRGSNXp+-Atis)g|z(56f*L+tI#EX zvxThu?Jji7-&`SwzlloELQkP5&Tp8N-i0-VHIhzN`WDs})=D~6>0ek^SSRT;(gTG7 zNoSB=Usx~cE~EzwgObi7y`iu{(%ndJENql?4(UyWO_J_GdUIj3q?rJzdhRUj#NYLm z`xhQ4JRr{ok=|97-3vp7AxUqn>{)oQ@SvnORrW3n7ltLhxiYe_udq+jTPph( z9x6N}>8(f~C>)UVJxD)Xcv#Zgkbb1_h@|gD`q9FplFlRjSm7~A-&c8j;fcZ%lHOi< z*TQIFRMI;t?_M}qI4J3zNFOR3lJxzR!wc^zyhqXxRK^yL6pl!GSLMlt@xr*IcO!kY za8%Mmm17IX3&$nB2kEB@Pf7Ygq)!x1NP2JOw)qFk!i)An`;c|eI)vwkt@pfU zUW*rYBgdF^5IGJaN5S4;zqBc4Z{*oJf_zU}<0$84TGCzqrg)zErimiFCBlwGw|sa{jeaI%iHo8eU1VTR1<#d^6`EmfGVU9wfBRl{p>Y04;%(3G5iDTo>oSb+HHg5Ggj;V9CB_$tUTb#4&#RW8K zd8V{jUsC*Ra<;5N?kS!@6V5I3T;^v}e({-OSIYJAn#KH?^R?QA^1>o(X)cr`=eje} z9s12@OV!exhT%*}Ig;v~Id9KgD3%@FZ+JC5TfX8#(0g1b&$5}MCDZ3@$I*4~ezsP< zU@xP4q|ds>Yn2M>DxF}W6BY=@Y^h9easUg6Qd7=_@*?lMf7dRQF4)ED(!#W@Y+!tu z4bYrXUt^2+=vux|QdQlu=~HE5L8Y)xSIbt#l7kAf zlbpujzDp&wuTq}g=Qz&3g_(VeczD?wGFqucX_rUTe(_0~9fa5!E-*NDU8HoZ4(F>!1>L zv96ZvmdQKE8{5@VHGlkQUb@D~muK_5pD#Q4(j~N~Ah+2{q57y!%e2rst+YERTjqJY zvUsO^co>D~%D1}8)kRgCLC>|i{JTSjVkPkXrD^wR<3)a0zl5T`?o zp?oi3a<=0Xi<@!7`}amF<6jxZom96OH{Vi6kn1)Obw&t`I)KXvmKwML=}=?oBSNnJooH7Xir zj3cj;7BK>M@8P-V*mKfGL!%NmPOT^h{XC@^$ax?*dB@05 zs+9pfyi}@SOsqY{F=3qop&ciwvQ-OI12hN7SX``>>R>(#tsc!}7Bq!xWt2S!(r>Fm zpGE*Ft5_*lZQwmeFp*G76O7QOG2ZujM>jAi@jUK2eK^J9*?20RiRTRImp9cjxHrQG z;U61+42Ls`lN~F>A*dU6!Zz)sot!l`#lZgK5Dn1W5K>Y$gmPSgfZ!@)cUdX?OW!c; ztle#8khcrM1xSgA3OOqW;?bjyqv+f5m7Sl#eV;`A5H9Mm`8b7LD^WM<=)-->!&P2oBhx=uIK_3WjXx_H!j-XeMsvnyF@b)*#~H zNdh(M)#%np4j-Sb?!RLUFHR1Dz&UlRwp5qDwo|cT0 zBNPPqQc)<|$iYf&rc`kbkLaRW@$8*=OtD&DfSGf`rI+4YO2*w zk<_-%XcjB?FgWL!k=!PrE$S&;ejg4;=J}{$KmKKm#*USBTtVU{!PvSf;a%2}WFb>m z%d=(W)NdyyCID{B1m{`MLx5bs`%!DaV+RiBS8(DGxD~r1Q*ug%md!BF;#=?LdK6zgoo zX^wCXcyL~ifs}$@f2Y8!9Awb&)sea#FQh>Xi?fy!pj#_@umUD10Dp$rIrJKbz7c@^ zavEFr@Rz^ep@3QeQ-+v>31J;Fk!`K04@oZ}bp~-lZF<3IVomfPG4CQUkS#xf#(zJa z-!{||{-|?2MMn;rehzcID~syzKjBlU$v4#WUNMbsR^v$<(t|zvjliuwKtP&2U0n&V z`X=1_5#AS2v;5eRSqLQmt-kf6{55RmkQu9W~?Z)M1b^nBwordetqUot7~4W%@# zyU05!;l~fTe-9LLL=nvFgqgLHVBBpy1X;V}bD6WkyJTDg@x z!^nb#=DG}~h2O^YKdfMGtsv%huF2_kULA$d9!)^^aPkMoIr-NZf;BlA@o3D z;+D!!KxnUFu7?R)FvZT7rXe-tA-RAtN_qT@GXh5XQGkm9|1tatwayqWTWNQ;^fS0{ zgoP#5$B~}F5&0!>HHJeZx6s{Xd3gT%-kTbXb6+)dyub?C@wCj??S4T!^}rC zNaY6ROnpd}o%5&!`<+^ngj56i0`yEF$FqkM?ftT*4QKu>igzeda7NS{Z)^%G*p?Ht z;oFf-{XL#;@U(-c@8;QZ4xHMG zUq}R;8t6jFq7^SnWnCuMieCxUeGC~?1E+t_gm8?Vl$sZprYq$cp-WCkeDJxk6oE1I z{d_HG$}wEF`q~Tlcetxj(op{p=@}f6UjkQSI2_8jtBoo%EUmF75s|#W17b}4p&5&kKK$nWo{@#P(%cc;0cmw z3yM~d<aO%+eo*6ySqrWG^K)K@&CkgkDv_(l|sw;S%xz0gI&0!40e$}*YX?x zIQpTWhdzl1zs|2{w%BL(CQU=}?hKB|FO91)9L`bH&cjqIUVxT`6BY0Ptxus+D+S!t zJOH{D8yXfHS{3SHs9_1~R|+}1$L>XrjH{(FtQ<;$;ud(l2KT$IHMs7x*5bO)zwWoz z;d-szZw=u6b#964twGV)4B-9-++XkB-x#`opbefLT(7g&yI-f?XESmqVU!uL9)8=r zTbLI)9|2_1dJeM2LD|-}*G=nQE05e8+MvGA+AdIUY=e47=>DT^rLTl~Cvp?&jn-pt zoA-X^Eo^jKj(zojwF~XJ2|XdLhI6P@qE}){<2$>pA-uC0J-P+$%sYFm2XSYs^|-x- z|9}U>sP%}o4_~{-#ftsbL&&pDOk7V0_iTJd3p#m_t-MGqt_VXVcqy-SjF5H2i~@TW z1*J)ka#;-$^nTz3!2`|pj1X|QV8ZwE; zczG>%)xW1lj_CfV@qNjTCF5Zu6(nVq7yzoJjPeK2fhBSc5vn^1mA3+ukjzAKClPqyw_vd)*L9_F1 zpph4bQiWdVuW8Y95lVd)Wk8K**JXN9*Z={p$hKfRVM+qQvPQFLtG5Wrq*fF~5G6TN z3#5V%|CC_f~^WVLpX_#z3Mk(OBOR+?A}5twl^5$04O?MUqy; zxMH&UF+2=)nBPo_dap0@O+nUpf;eg7E=b2KpJGTeb}Qnx0+h>7~VTh!Ys=GUEuo^%HzFhLaddq|~qA z%9jr^@wQ&|zKs!_hLuZ|VSN*EEKEA6IHg!G9sH|ESeS2E#?^$@*+6##+Yzip@%aSw zeR1^#Xyw3gZ^d6Wt|qTidk3RJ6U2q)fNOD>4HTZFuV$K=`E)aR!BC%u4$o{Ro0)3~ zXdb&@2r%(1do7_}#gk;cyP1NnF@@)5JXRgRy|jA|W$@m`v1X!~wh|j+k3&O=>#fKG zouHNVp4W3$cQXk|3}%KOFg^ioBy$M?>zSC;TE3T1w?fy%k!O!Rdt`F_L?I`({bOh4 z3L4+Th2DwN;d^)D!-ejN)BYPRle2}!nwi>C#R7ST3q%XQUOs6i4nd1-fL5Q2UF?OlO({F^iNu^3pl*uoo%6kUlyFV- z!gbS1-Gp(9dSX{goSv9$477C?M@_zw+zZ~mQYl9;g)V__4X9V0Wac(D(d&CrY5Taw+xmERv&NBSi|{DX_{25~vU1wx@Qx z#Vnua>EH0gE^Z}73)covEA4(24tt(CiWV5^)i{kGj5h{40U7B+kMK$t?!mXn1877> zM`GTF!HbS_wm*$~O=A|O=B}QdgTDsh=W{GFXBUlf{>GVa`rMBqY>q zY=|^okO$Ts?Tq3B7`CX z5iDPjv;z`IZQ+^e_I|oWCjD!GQBj2hZVsOt(ECLgh($yI!#HnOHOpl~ct4Uvip(;e zs1t?V9d_t>2+UUe28rSc7{3zEo@Nr3d;^LlrVO>`VqY_%MtQfHz}-NBJIwR zzh8}lnTivL`wWvRXyhuD`-F5fhG%OKee(zCcC-M!oMhFdI6*NVxKCXj&41vkrZipB zSL#0k68d++4a_Q}s&JVrTgt*snSivi&(qsPjJHCRVB6-TtL~B6-(XG=z*vxwDT?Zg z?Zly5Dx*OfCw`Df#po`=O{cpt^_jRg7BzFWHQZt{_zlsY` zv^Wm!QRIRKy7qn%^y2C$#XV3qL)GhvrdcUA!{s+;V%D-w=xM@ zHhV!5QgAFIX=rSUG z(1OZW{R*#w<-Y}3JECGwliM(-rOue#R))!5Bry39ZE+>G zfT@JIy+C2Bw@cbxgWbM^J$tJaqXCJ_9UB&I3KJ3 zo3;NfoQBdJ(dhGh&P?!B*ZQXwwhTVK$=1=T921B`cd5)4D|j>G z-?thErOKW{=d*+-(7**^aB>EAGIcxDOa0Ma;$w(V9DClMg!*+B_aAVA%YWM=l%yvI zx=ZhEvnIeiT(oL4u!r2sjNi@E_whvdwZ?=B3~i+UBvT^1^)R)Dr(fjhmwEaXp4fPb zh@i*54EAnb{uiFeyA+Kc(pQ{5*VS+HQu^`lYyHGA*$c6+H@H|8T|>?_REYl1fXO)l@}_ulXHURQq- zrGt|6zG=K=yqVkBpBU^Ccv_iL@QoaY-;&np0kX)C?JlE&!=oO%#<^O&VIRql&QyRx zqxmWC{i(qI&m|w?kEwOkl+vnrx(EX*C!Zx%Sr$dLr&h?9;0LT1Tfi3~4>=MqibKF3 z;H#b55-ukwh~7(|^>NHpRAIKd3DC2Y8f_q;XgOA(w_hvbd0p4(SB^legdYT#e27~) zbnB5yx@AsoL^zWw_EQND6Ej-$PZa?ZsGt z0+$?ncBv9sH&cPtlJe}8y0aI~_pl0-;8_6xK?T^V_!Qa#s$vT(X<&EAWBmLfo9;WK z?Kg(Y*1Avw!S_bibNsm9ifQ%7%#_h)dn^M88kVH#nN;FW-pqlxzB;qEdE)-LTjU4a^Gc)E5)KFYp8 z3%Z4CwL4cNu^~?acQK7wSgZ@A`m=x)ZLeo3SQNnp2KXLmIhN$)b@O0uf=P$lQ;RjM zivfD<#k|BS&o1MGQaDSQSyDi`dj4`vT_7Nlr43V4Z1JfnsUwyXlvS-tZ+f^TC_y+U zZ!K}Kph?i0rAoOWjS>J4eBwN_JhKEGf~!LZUgB0%Dr5Gi7l;r|nJueJ>Jy)7p&Z9GzN7$buqJTxO z)~2~!M3WG>m7v+MECL?Z@arHoaeHyXzKqdr*?9*pD9|XEF4fSmdcjQqA+b@GOQ6MP zkwp=ugU5aa+=f^G4_W|J;!FCEG+C5gwbE{p|ITmfjQR^?SBp5!;P8JO_&j*iwYp$6 z3GFTr@3ki}1a=5eo*)%A3nf~8=a!u_Xcy;P6_(QTwR)=`cb*kDyWk!?-efftT%Efd zWMoS^g$$DzRqb+6tEUKG#Uh`w#NT8cIRA+mV%&mSQ~k9IG4IMRQjMLsMeGm>Pyp8p zWaymgRYs&gXfBCb5dNt9qpF;X`w}q3={6tHchuh?Q)^w(t~!`x!${{5IxaflGg*1k zj_-YLc}1|)BKzaFS&hpx@9Sx6jnxOcd!Ja^`&(USm#SxN2a6pW`ALvRbUM<+Whf=N zfG=P2Sl!43CR?9FHAEN|R@=(JiPTMjFbXZylz@E6`!UTX9LM3D#i`M&=Qwcdi(?JI zMiXlQma*)c{~+z-?u)Uiu^)Ojq!TbQ#fl~jT9{WNMblP7QqY*;I^)}lVUO90(gjBK zn@oL;CmK;Scb;JCG*55gBeg~d$)!lkk z&WLou(4qS+?bkpmaL-&sym~{?AjV5)z#+pi-e1R#yVLHt))Mt>T7bDgk3`Tq# z^kvaXQv+8lt`PnbcvHHbOh8N&un&nwBwl(Bjp}PO=}!g;0}FSFf9`|}I!Lhm;lsc} z%w{$32ZxpAk6J2FSixJ;E*q2}ToJ8)9%8UBHh_U}$t5|bwKQW}E~#gJjrCk%3gsz3 z26kG8HL6s(Tv~SW({R(q+BsgA^pog1U5j8)Lx{tW|JxUXJZsG zDgM?hZ2!nZ4?FT+P|^$hq~>8@Tp!r3%i2Dge`s%hyDrYXa7%RDODR<@u1-Nz)-XXJ zHCpS~Om@*(f}pCFi&8a>=QVbB8~5pv+}ma8--AH0~UCoLfU2W2q}D4gLs52VF; zz`U9ARVkSg0$0S2U7^KQ_p(}PgYQ`Xd-tQ%g7_-g0J+$xJ&nC8hl}x{Vu(Xg%|-5<@fmq z3)t`nZb2M%1(U7OjxpJT9I%`d9v%j&XbsduZO9TuzmO8jf48yw%qrcb75CbrkMCny zn}_?Yltz1cP`LNhM*#1Ns&f{-4u`1vr0RpZ>hMk>tBK!2BSfm-NgH2- z5OIM&Ry)?%E+*luEa$%@j0n(6e6AbO#AdjWlPc?#}yB!lQ&Emco|RpyxP^wG`qAO4?G(>J!tdN5K8Hun`(Uf5eWwXYVDG22UE#1mNQx&6Ts{a z8edG3II|b34mpMMO)Nh3NuG{efa(8!ObSX|EMLd$&Z_Q$I5UH3-;hj>WFmP5xAaWj zr5F_TDDjMFrxXa6o-2k&?Z7mnxd3SoDkIl~F7dFFWIuCHlt z0eMF{IZBnKuqW8R8&OgWtIoA{(O;JiO{4LZ#-^Z;wVKgGF9%Qql8`2^?Q&>sn_^G> z9<%rJL^_}aDt5Cd^$-RF(K_l{-$#i+5gVkot!`0J>FJmvUh0+zDkP~f=Km^Ae}){+ zBS=6oXL2ofigme@+tQmF%o*{Gn^1)JPZV&WI{%H7FoTyPKhnA}9L{c>0$CgDP!K{% z)}aX3!4(2Tz}pm|nSg)kR`>X2%bN;Kbe7rB1z|M{ea<0zh8^Nn5m{uVFBT+Y-VrLZ zSaeF4V6W)FcBLiHv4i=A7~CjA@A-0MgsibYS<`VGvXHq>FMWuQ-ZD(rLtg`gb=sJv zD#UZ@s5n+%Mk&vSzP~Q~eRl?UJK2sTqVMIaA)XL)FM0D>mPvsi6YotZ-dWLIWJ_Z> zBz}kAT^YSRe=e>kM{psApaA!4c3@JXW(7+g5IYMU*romorO+QXvj~W71u9Hh6p>HZ zyga1UD81A=#5#BRmuX+Gy2wmb-OhF5R*F4-0 zv;aj!k?Yhg3^7T;v6wQsYhX!4EmbUBsgDw8B+=vs$B~i z(g`me3}RE!AxtB~cBXdl6yQNXuZRbg5M&!yK;~kfLioGz01k>^d!J_!KFQ;q861%x zx#buR$3Q-xw_uFA_RLK(N!-5(o}xaD^Av$uI&5)_Gyw1YB%iOX>A7 zRu;U>*Y?<9dUechK5^tBuyH zcAve;-i);xTlM-E`ySls6J1rG-D_=vermI|1>|@e@8e&uwH1)vV{OCx_saFXC_Qi8 zhwHq5z1`Y@>-%uM-Qx3|*8RBN;Xl`_O?IL*Lax`5-0xoLH6#yM-(v3qWPR3NcV$ir zYe@`CuO-Db|18Cy%uvZJ?x$zH9fg&cN;W<2i%ur3**w5!`1l z`)z8KP~x;TgReYkkIx^KBQ={qo@4fLDZx)!7D{3ezGUGkD`lswxk&r%vQOZ9@3PLL z#IiMyHav--dzIH>g{RREeb$2C2UyjC?>&RO#KuMIBCbxsOU*&vXE9>xsDW47625uK zx{T5$tZxPMC#+?wfiHZ(Y66ba)`#4CS8?x~tZ&A>GgbvvyX5Th^xl|W(Y z8qklg;FVytnpaXf3wR|)jnUYkRYq7|Hi?LEn4%xMvWO6M*2L{ZyJ#jnIyjO0wtAy) zT_@wlfw_u;6(T0Kx(Tf03e2K^hNpT`kwA(+f=71l%O*M8fjSLBlxM`K8`n5X#9HKe zQ$4`*eDbPUg=6o03SqvmwgOKxh#t_@lqkVBD12q<_E_w$=j7dAEEVp><`2X^VQ>Z|WpAu+x&}!e&o?t**v&0g zvc45>ZoCFP7|KY_-$Nn8$}l*STy=R@l5ZPg9>66F?`4}Q{RYxlC*-Gd2rPzo#ZcEf zfmlt2j2BQ+y$h|?Fk#gmF0A*0j~;vSxu=Sg$DTcN_Vnq=Lign9_Z5##JYPI|^6YWE zn}+8G75#-?H~*2NM^B%bJl8T$oPPFLtNXcg$Ijx-<0nrYD|DTCu{eJE)akS5TJilu z_p1BwHHp2TsQ+vAF^q`keowDTtp0JsvIxdtRXj|->K5PnG+#(9Tp$&|s&)~3)9z4} zsVt10nB@D$PLZ}-{bB%RX8%EJXVF7ctPWyyO$RnocfuItTHo=ulJzRzOIW(#HUbj| zV@FU7E_6Xfsbea_4@-|WfJ7Zu5zVV=wYs{W@8!5q5o^!^)Xez;^pDVDUy>;b!cZfU z8ZIaqd(Lld=+3ri84HVrGzvY>wU^OhBr}|E=h)$FJC;UVn1uPJNgqmy5z~hm6{5sI zAqgD-=2nA7F5YkS;vZKp<&4H!uW@|Izw`7KAZqcSGc2GhO=9uJ_+$qn4U0Y;(RCck z5>lE@!-gM3zy)1&-CHb%Kp8>l9cKk>nIH2gLoCs7H&89EEg&o#W6E$D%?e zhP6?~C&UqFqLDr5RW^Soe)x7=8ouLqsaE?b|GV)>E3IN-BkhuJ??r-NrhZCjg5jqU zeoFKJ7^C8oT;iPEV+HwfxCvJCON;rrN^KhUE_hmi%h8{OUN)YO4texB;Dhgb0gQ&1 zjj#pxMR@4h3hEM^y+Y6f!k1*dBN&1IQMxXIfs$t}EsoGD#;U=$g$wzVy?_Y8QgA1h ze7<&>DtE9KzcA>0!P{uHt6;G%xEh_|xF*;wM5}r82(Sb!VGtJD0$CycCZH~TnE1dM zp+fxdnBpQD^*Bxilk0pt_ShqM{4?ya3|@;Neh~k8k!H9A+8ezQq+zG|NJE9IwutGV zTvp-+yT^}H5wU%? z`0cPVZv?kIL=mFUhxa2oVj1|2(m1`vTxqVu=n5H1Jx@uiRs}U!s@LHDN1rYFw=KeJ z(09qC`93_H5~76GoQ(9eZ1noI6y5cm-^eR(UT(pwY zw9B?2DbO2E#0HG&ar740*OTl`Au3}i6TWLT$RAhgf9UsPys^XY$0E^{T^3LcH@_^f zji4?HkWLe2^B^vCq{8cBsBTj)YXDwHCP4I51R_x?|0xR7X6gIy28$ogP>f@U?TOX7 z5FQYJ8W?s3)1e~5KA?2}_l{u%8C=5KIxa2tbqF<`0Q%P+pbDEo#D@B{)>X9EVv6G= zLmlON?`P^mJZUKvLl2SEMVe@37y(eE)o$8JVS1m1L#4z~M`c>cg$pzVZQ#o^Ew{4b z2Q2-D0JPxCi$gh0q{jFu=21tOlFlYHuwhG-{8nbVY^n!i;aE0^XooRlkIs-Hg8sHHbwxZqPQeQV)|bJ0p-S zQ{qegAVH-R8lnH>A8fhPI7BQXyVO?bTNKOUB@$Sd;FH`FMRz+PJ29jMUjY!JhoeA` z5@xk^hCZ+R^LmjXIB&*HxE+EPiqPSe>bjF+qR(aSGnFNYYsLvPR&GLTFfuoipTaVg zkQa~pA=WYal_PXe!d;vo9tq2Wr~_wW?aD9mF0laukak@4eBT4=OlS!#bldd!gY-po z0N^YKS_h=+;)6{HW$^0!TuMu&0B-w&6U$;2mO{*c;a=BW?CRb`3!jp$3+PoS;-2g7 z8^Gk&S4Rzt*7z7>^I$Fmzisd>?W*H}Qj(AFr5iM0AiRQ)T6H1bToFhi9CoUzu9L26 z;Fxtx$HDhhMXc$_i1-H&Ps6YH!ssa7jSyu&c!haz57RMVtcS3>w~ruP^>%o3Q8IhT zOB~of^!6`8P(^-SBK89SD}u)_$G%Wj#z9kH0$u5-Zs=(7O`TO@eNgU}?tFgiQy2K!hh4^ceg>mW&&X$6W_Yjth-CSpA~|(u(&B z?|XG(+mz?@7$~H4Zb+_E1bYA<(o%pc%1R&5gR{Lx)K3uR*|}}3lgROy^oQuUpCMAO zx9^1ad!2wV(|YrV5lEwO%;dmd9iM_BhOz4grxv9rM(<-I`+e%rNJ?=)xi0?$s<>Lm z$%ZL@KEkns)s+#HY;2BnVI*5bm|#b);8Mgq_(_sW8KrY5@!WYV&c~V@(VPZEWk^@L zl;@!G*-nU69o5?d`Gi~uvd~Rwtx|p$-rNHh}4uG3v?9FqV9+FH@ZojQW zG9oEw+lN&H-yp@ro+*Uw3%wZr$}@2%f5kuN3_<0Diz; z_#%H^xzdiBuI%2fs|zZs^91J|`AJ{lFS`O^TNGVs&}8^qL%JSA|&9wHcvhgH@6_F1!7V_!vn=PR~W_{TO+!>-PjpxD45N8 zJ&0WsefoC`MXL@1jbf&v4=_X-#Pkc)R5Gt#0VClY$KiU+Q6!GSi12;Dftv{*Vx#e? z2*hAh+`Y@)zbZujq?0wjL-*yQHFG5b&-6Pr4A~BWpTRvmgkbw9>`Mtv00K_ToL7uA(=HXJ7cML=vjfmF zT2-FXrlCei+SOSijY$V{3P{qJG}mw_aTu}r*y)ym0k#c+RAuo1FGAoKeC=KP!FX3<2 zqJj`_I$x@rnShec)z46pH|lALI8sP$P;*|v)YV3C3fHFYGu>#g> z%PiwrxnquOM$I#Oms@Cld*lA?{H(;8K@$J0HagIYy>~f=f1eQzDCTNO_7rhgyY9H%4c7)eciyZKy_;M`GJ*6_R1T)fMuULt>g&Wm; zWCDm~`Buuz8iVFm+|iMEOx(@B6`I0_OUBfc?b}K~7p8eJKIQ+&U3^|VjSI1YkZ2?< z_!Uk#feAy-#3 zQP0l9GgCWEfa|17J?Mwfj5`SA1e5`ugT-ofRS3o10Tx}rJTk85;(AIRnE@T~=JEPj zFyug_xvxy%Ak^+c)cFw>NNbap@D$GJ$|1JzLKj&~Pvv6-B<(%rT9b$;0Z{5cq3Lg9 zFH?6#)N!P-r670+);fvG9Z~}WY8A4O?dr5SFFDXe@5F1ZZW1haQMOioUqntVhVWhW z_nFJxS=3u|g(6fPHcmvKz=BQB>%$Mi=)$4xzp}!mWt@HnwqKb95uwy&7EPE$9AI#{ z6${?e7}M<0TR=I{7{$7|TN>lCZiMPO#MIo3iXjRkoGpT&7QV~v(XdpKC<;*#JBu)7 z)EPKX7EJ;fQ#aFc7o2H6LjA5c7^Tz+QLf4v0EcBa#%wiPP=HYZHhBqPzH)09TAaK zX!LH_cLl9}Ts7F2IIv3;&O^N_ibk#8D8Pbz>MF7#(42_5h3;vvAB8=3$U=y9>YG^R zH}mu=Pc*KnZ$Zk#BoTrIodlCU!RH@j&NjiA!2nq*vFXQ&O|;m?GI}we#Q7yY1v8US z_S;zW0t%yt(u&Clr6%GSVQ|DZM6`mKh`3uyLJ}H)j03|cWE2oNEpuG#myIBe#t%QG z_i~2O7xo5GBxqMachy`QZ3|H}!5D~?OYmT1ybYHyw7no^3N0m2&>$oeO>-P5n!#u=)VI!VM31oy{(fmXMO|v{;k2o{(2Q&mN$2MGWF8 zV=klZ15YK7Zbim1+Zk`^c9uzB>?Xv0j$O!WSgAm1lY+kZ{DJL=@o5(z!NNUJ(rjlP zi{!oA;Q(VVM4A$AOlT64W!)0qwokjoH9^$sXcKwv>3g(O7`BpvipiJrA@(tB3|bm1 z#eqcd9)*v{?jg(yT$fE9N5(Z&>5f5eolggu@e8Au4ITt4CX_B)>xqW4gs?W%Im|TJ z%G$Q&ze(=InvPC+3{QVx1bj4a02N5yrm^iSNLFX;|-#ZHD9aKjX6n;iq#um(_5maFWh2v$G_1w=ZQlat@B z%_tB}Io4RP=fNzJDgED&+wnVLMcK62l_5v#UMDh@gq(MDfAN~rFOCc4Nzk=47=qKA zJBZm>&m1(QKwB*h`8rD73@hKASrBI?F@ol?@jNwxiq<3vzy+#7J%^gpJ4SP!0ag?) z#U(JKZ(kkPtm)T+Oou;^6>31mDvzxJs{CXT%^AixtbYyQ)JlZ!2U4^nT3_(CZin9@ z#*s+JdMNOE`**=|*q$?@V5MBkLlAES2@o3CtL9)g_*DFCj8j>P0*J5xorooqUU-}^ zmI_wsf&3&(_qj+I#e|o34P&=$2~u6dR$aSZA$2HMhh5AJwd+|%%3$XO<2i(`kSAUD zTJ{JLl~f(Vug-%aIqUQB$43Hh1V9c%)GBmtMB)gV1c9OG&+apiprj?L=UrQ_TNAJg z4sP%mQLz!=yP|aAD$ct3HHBX5qr_FlTZDSX*XvV;ozV*-+r*DGZQuHHA0yNKh>zUZ zxEfG`+mPM&;d`yVY7KsCblA3wSQ}fJ!Jv^j>~|9y5lxVgAKQ%#mUK3a!!hxMb2Dnf zFxP_8MxxZ3jo)mVHxt+6+$(z?TL?hR#dSS(&Apn#_99PV`&;;GK^eqlt`@eo;oJE|dy4s7?cif#SW@S(c{JWaz zA{^c8-Sau*`$E%jzCc9`cj4wX8n~0OvYTS_z4L48eaunsyT(|#=@(?H-B+^k1?#$o zI$iwrbFt9XZ&mx7CPc5``IoTUaW_U~u5>>dA0rDKIy9JsMF)d0N~FV%iT|C4=ZLFs z_QNfRP~dKP>5s^_9d2&;I_^?W9Sl5L^wJOvTg^`bw&fbEp|U1PLBNPA)i`wb6?OOY zau9f2heq0mKZq3~D{~?cQQ{e$LMghP!@SkyS;BDqck=j+ugY@74hTZmnHC20#GAtC zWjN6p^f6&;dYu93qwMXeNo*%<(9gXfb;9!qBY*B!+DBycbCb)9t`FyB@@*Npbg-GO zmFQtZ2X3{*FpO@SLkfj^jffF&(w>bp%%oa^Qxl2!Gj0JVz4yVbXY54@E(wWcrnHDi zOOg0Xu9^^`O%deu>!-{!ARLh&xgmcdhI#9Supc7m0(aLE8o{--IXCV@GUQeq@Rfkj zj%zMBp`Jvc5uuOtiz2_cRMJI}!DK+CKwTJxfmA10hMdrQY8@sOr0*~2vrGiXkp}Jj zPTm(HEmV07chxgE?L~F8CqcaNXgdK0)~|_*cw*sN zDES_K+W%@R?N<0f@5@*X^}Wclg5|NHh2_y9L|7iSW6`eFp`g`zH)i@~wuwLzmVG}8 z(JXJ*T`L!GyP>%78QFSZMc)I=*cc93$?x^OUf>XNL+#YdTv1`qOsM<)#)xu9an*h_ z)FgD>E^Kle+Z!&hh`7V$i*MmKF|-zYPEY`^SIX0^wdE@O(aPK;vp56u8gxmrs(zM@ zbATWIGE-bJuD0=Xji;aE>2o|$u~6tfIWc+c?AhneOddNb%pH42U76tq>n)S(^jd3I z3_ULZ4f(SYHcW2Wlp`IiKO!#apT(D;0`?Xgh45sIfKagbY68vt2(DsVGMFKm zaUjm{8(kHWfGy1fAgt3O~qCL<9wX zg$`xiIK+wbJOF?|3qcnG(&nh7CanubU7JmOm2*<42ySVNro?2wJmDqaS-=xmPGBgS`F7n_Ye_j?&^W9hwlL z0)xoF(gRTe<-G{e;cEJu($)R??|w+<_3q_g)$i1EwBN)btiT1_e@mAHVIg_5_f`zf z<6T#?NUveedf)t7*mfXlNO~R8S*EKa?<6-uD5mBItn^a+V{zxOD7x44PP6Og;H_9Q z%X1w&7$a?Nj9oLYVl7{@8>RrI;&bXFhiRFipw-A*8gf*8~9 z1VXf5KE|gvfjOvm<2pRzj(o_-ZDdl$TtEeU3Mr)Er8ZJ9u>w4DhcO%yk}Ckuf>aC6 z!?{;NF=GE0F^~|oW(AezXxBq>H5f_(3_}|=onV_OvLX-ztcX<%7gbmzdT@{&@?3O` zjNexF+%gd+0No`KS)-}*q+E?Kl?$D zFz&I|`!)Bk(T5zH-!ieLv9Z->R2@DPVUqZ$%*};loyA3<6rwtiyKpv*+YE5*c8(BWaP`geMQG(x)SWfg1}EXfrSL6&>G zfDdaaQY;D+Y%*GgBhB$oc~>OMfNT(AMP=hvza1m#6Rx`!Pn@G?HYoE{T@QI{F8_|?t6o|OVp=ib?dZ^7Q-fkh(pg^!2sK`H&?`L*_~x- zm1{7)25fk!>^TNJ#WYR(5^-J^sdF%&6%HL;q{{I_botv(5xy6N=qZ95AQX5SGSUST zpwJv4qrQSK3(D)2@(2nJK95B*x~ugR;=7pVdwIHplc&TL3h;5>m7z4wRCFk{xRn3}xR4R)!=o*jdkUrpSOWU`ISltg z5B7`+GC>ZKiLW$#hvrrgxMQDWGmCm$oW(+sKSDsEIUulb_DiKR$~}uWb$|!DDXsJj zAxVqS^$>?n2xUj;4D9Adehh~$f5X$ba}c{+(Nqd8CHCVM2QQ0W`euoCX1B*UU87B1B!OaqGMGw*Kd6>z`#?6f+IcnQ+RKNP0b(D zVs2m=()5>*pg{83p*{RDFS8xF~Sd@r_6HY7)siauxrb8vtQIc$bU{OyGLNfv#J zK+1H>8=WkLp!0ehhs-ppb?bmt+e1 zL3D=nmp9!EI`AXR6vd0y8n^1jX>2(Goc|r-h)lxa9@#HS7m)A3rreetCK1_@gu0U% zifP`D#$v7np#8%6HJr4sBsYmf3foFBln0E+Dd;lcBS~s}%b0^X81c|Rn}ILz+qX>o z<$N>)5C4gJ7ySN{R`Ou1x~<-g?M`!uieZKBW*DrV&vCDZTQx)MOK@Wfm-fpBG*Z@Q zpfT>nLZ7(vx~wR`mDb}{clss&il67_{5;qQTIR%Q@Pu^Z7f#9yHl)89IfH;c@N)Ah z$GFJcfr3P5fYnnrGviiALTy$LpI8oNdk~FCnGjp;#OaAMMJWseh_KwC(CIDbczd>Xo`INlC0`ykVpNTyoBj8_63h3wE87H`ZAGtCrXK}VW|3z zh!V|7A3ZeTy$)T9_End&5n34}1;R%&O%)dOrt(G1Pkxc#{AHem1|lIp&I>6`(m5iH z!|B5v3tOYsG3lT)&vj5cu4(y5?7AT&$;;Zn3g?YJ4je*`VWJ0 z8jwwrkW&zZ#>(C>zT*pYaR^<1Kh~3Ag$zbi7fS3x#1(^UOT@vw`$EFX-AH~Uemy>q z7=KG~^(m&bRl`Ea;n(9dv3}V2aAMY!pu@SV+3N}9#0H-*jrO9{?&_XKFVokd61&~7|oJ-Sz#U9`}EPOL34b_(>O!e_J- zoL`_05!@*3{xE?&Yy0&Z)znty_$RyOn^c<70jqG_knV1G7^e(B@-3`7=`4t zI7a9|d)Nj>{4TJ0Cj4riWT_I}Mb{nCcCfP)sTx`$0t!j^VtEn%7g!vQW%>Zsl_T}` zEuitLwlpUzu;52R633YYYvKLEK?0_xLUeYD-6MNTLWf#)s7l0SfsBCt0XY@ljg+(- z-Qp|nN6wr)a}0~Q00At15CbtcBv}sczBQ_7ydKR1Pqj_KFK%+iBa5JfqB5g+=gih*x|y~>Q4n23HWs#%IRjkdN?!G?_yVSi5}N5Usc{?W|0aCr z2?!zLh=Gj>?)Cc`v!;*&5?*@P{!|?fM3+VeIcO;zZDgdC%hl0V{Bp}gEVCyh$P=t& zj?x3!^)4VpwL>2 zh`*4Rg$~I&5^SqQ?_+afvhnZ-T%^D1Vt?Mlc2t9LbUiySG$pRm{VL|}SimDgh3@l0 zb^tt;CN1*c%!t*soVxH-o<9l!{@| z=mnDD><#KUN3Q}Dt1AtoSc@WRP-zOm1(6QefGuP3Ash!<#w`BM9#H>?0Q@JOK1V2M z5LN$}mx8$ek*P7>dj+ShY`mjL#5S5HY@U!=xMLxlHHm2Gzyml zF9sk6GCQ}`{~RLEz_gup|Ak_twWNm%O$Q@rsq=q>5D)&@E=i2YXgqyxkp2;k%D zR`K2d>XOL?Gm+IPHzxT+K&h+xT{I3Rbs~bN^oQEWY^w`nSVOYp*EG4pP78JTib^88 z#yb)TuabEv&c9(8#|GKKtFotK;?+4(kUq`wf_HG~c;!HCHeSkHg z42S~7Q4Tba!5)$QabHXltkP3BoN1gu-8qQFHGnqO8;Pq}h2F%jpqz_hU5UYX-LSmE znSe6`dOfM*SOcE4x;DkInTzB>!_dRWP0wTHAmVpJm-$gDnY&A$1-iKHL?y1Q{tuYA z7SFV*!WZg7?MG#sYj}*tk?&xalm%Y0xhAZPI_SKXBp|qCg-zl#H8s2lxz6n>y}U#F z5I`(CVp)hnggrtTdlWf+OFG_w<_si30@Oo>^2;j$K$_HbJb%&@$W>Se;OXBO0s~?o zY-LLe(_-}Wo}gmbp}u@2WEv4!b-V6TG}iET(j%(oK^qj(e{)Fg}?U}BrzjD)za_Iq+9)IV7L9dcsDNENwJ+%R58 z{EXqixl{#}uil zx2W`&dE*71KF^%b`8nIz*=OYG?k;evWwiy=_p8A4YlhPZ~Kw8?Ndb?D@@ zjslti*|h*vE&{+|7fqBKjc zA^{N(Jq-_8I7-#g;}~GOEt8u7--=y|sol`oCMo*Sjf$SM&M5ArWd{vd)S$)9+=?S+ z3m4qL&W6?QacE@0y}$=)hsz?svN@j<4Jx-1?QT3vtf2Z`t3_5vjIXQ1pp(?S+DJS+ zvR^6Gs9-$yNCWQQi_620J(8b>!6ssuQFqWfjV&H3O4CV>(k1M5LV>T*B{xT)Woa3g zr#Y>v)aJ;RGzI8UL->sPW}aT<=|7`H>@o(jzND|@&wJbbYkv>rw=xL+4hC8*wo+Q*qdrdnKgH7nJndjbM|hgyi54#I?}+&t z_(I6YC8YT?%<>3d`zxjnFm*3e-@}uvz1YlqJ-lQE712~jR8BMXJf9YLdOuH7Jgw)0 z5>wAge4MU}_gn*ZAN=JaK@D#arFr<;Qqh!+RfR zioW%-t*LlJD*7*KKc>&|ftc;+aiC}l7vr1yS*Bz*T4H7rZAD>UPh!{esfgj6*eFs9 z^@lwDF;CZ?2n@&vp?Qe!OTPjFfUzKF3UW0?J>VH$lI;|)e+F?ig`?Ka|YdrI{7 z^yd0fy}8&Mys!Og@${a7jeREmCHvC71HJ2GZ}FM&X0E3%)z>|^&%}JR_s!h8!L|8Z z-dlHmU9p0p>O88H*VON9T*tMvLxdz7#iQo-D~p6dMF3* zj*-AT_N%!ew;V>4c_Vi^lO9a;rZefyTlgOGy_xGHPDOqRT#eyyh;p9SHi_5k{@$@R zcId+nX;?w{x)!@l?4m*uI-~voFRilOB1MjI7EBvhJtn*Ph$cpFkZCA>8Oi}+tL4rc zOc4417miKNwPP5QjkTvh48|cr_*^Qt-pM<$8W91?F?Ev@zxiz%deO~T!P0et){H?zS^bhav4w9X}@(9}CP zOJMm4@Xr+5LLEc3TNwsCfY5EXOxR8@C<`~$AM#00q%6+|NSgEWe4YT9>ch+)43sfG zSnqDP?AF-P!2ANcO8XsS*$Wsd?L>`j1YWwV<4XXwZ5;x8T~jQfaXD5b_m%Y3h8GFsaVQjs57^%xFkCvYRccW)=k`BWigC+E|JG|m~>hcbcv z?vge(l>+{QS!3FnLcpGG8^L8@`pjUQz`*hvFr~02a@X6RvJbobac@dYQwwX6=FXH% z_uKvR>#Vi@RkuALrS)6uq%`hT$ODzO75uDrb}_0>je6L#m@6oQ5OQ<{a%p*vfx zdvIq<=*~9lUfkJ=-7U9Sd5r1%tnIkIH*-MF*es-a~F z*^spd`}^$(<#^EAiyS*ccZRJI+_~Rc4Bgvj?Z~Devtc9fZjX=$PQQ! zBmZ9O5$jRxJ~)gW5<@v~wFmhgL%zqYCy;NS^)71^`S!~Wm9fG@)*3qPo}UxJ zb&3&wcORZ&ABaVFstj5+hdZId0~$BR2PA{P<&igGetnaW95EyFd0*#k!+fSk<)9l` z3!Wm}=UQ~2!;`*ND45>q4a6v#YBm&X6=t@Dj8eTM$Tfl(g}9Z%erYK2cL+2a1)GQU zdLZjZ5v7FJe!`&JU^NM=mK2g5zyBCzxK2P|t_ktl-45=HJ}5?Gzy~FiVFf@0N&w0r zK^bA*2LvwBthsp6clfgGn)oN753cb(=z1EzgKt5ok#Fr=rIw;2`SVWSa#fN(lCm4v z7d)h1l@EQFqFEuKT^BN!%8U* zJzr5RAJ?T?BHU426FlE7Apy0|jz^><@_8|`+0@jC2O{T7UL%}9GqvHj%%{EH>cCw+oeY8FQ!MXcqys6prE#3b;c(`ti4`|x;u^9hqxy(5F`SMQbUMD~ zb?vrJ&{E+=eFA_@wbKldBn1y;vil;Du?@xfpsN$?Ubzw2*`LU)#}rqchG9q8LO|+6 zyL~lisXVWP8r>aYnVG-R0b)Mr2|?^rI6~}|{S8BX&uUOZuD(0KqDANgFdwW90qj#q z0*qqSOUSSS;Jc0Sj%W)bA_~JU90A&jI-?Uve6T(Q(pql-`Gu^2=j-kEU8^-(DmX_~4!p`2A7vIgJ07aNoCD>$|b-?+&==yE*~R2ls^l54aTC-7R_r#@r2qcXWa? z5(+-RVp;7z*P%K2;QkO)>%HcT6tn^^yzzAru#PNBqQBq06{)_z?MfB5@U3<%;Pasn z*gp4K8Cm|F(A&JYg7oxs|WH-tky-yL94zjgwc z4;~5u?DK|j**e4zvD5o8O}0Fa!`}{ytDO;tS9?%&Y+MjaI@Jf5dW}8n&Tv?k4lLY8 z1eod35R87Ky2Y))S3)O{b&&Qtj&~G92g@jIqLl75iumANA$a_;9V$kB7@6J?U>#)@ zFTP#SVjp0Oxnt!z((M??=Z8YT`J$Cq)H?#IV=qEY$Yp^6Oro#v#5MWgNC+6e=iDM9 zu&~#7JcUDogXn2hAl~Wdpp6GQQNkcxj=LDlm)2=V@KEe58|*uHYC|z*Iu-(SqemFq z%Z+dw4qzSvv4?9|7K_!&BC_9q7f8MC2wIV54vz|o0lN)GK0g_PaYGx7UWvMH--X<; zG<`Sz_%IVzj#|u3R3GU??muLMGXngiavyh>i|4BEw)6KB5-^ z2R$iv(oQs+59UI^gcyh}V}?V~`+4L7w7rX2YlDZN8@5c)ATGsi|Ma!dwZ1AQzYkDiMS63S8H4Z+5q))f! z45Xk(riusQB-jzfJht~{-^^)eDG}{hdwIKsDecZ2s85kYH1*@+i*E@!{<0M>) zZ3FZUO7{SLgD`dil$rj%4`@hnEG7Dg0lbJIXTX@~Xaj_Rru_%tJn;##fy6|k>!90H zdG#)SJ+9VJmgt}-R-ZuuulpayJ_BAK7ZFB#Q5B0TzD+tKY>29J?G=74WRwxr?rNWF zrHe)QT@;I-41LmP!|t=FGnXNKdc4h>>zQ^hh2H}(D#2RPVMu#%J;2u9$;%m@#E~J% zEEoAeyw?;{0xj3Lip@v_YdDzN*o0iE`}t-+pSAgLt>e7{Uk_Zhr0%QwYn^9i>R3Xq zTH>W8j$9n#+EwcTrmiucOpQDFYkizgJGp96KKur}w8TM6NUBgV4tyn^TO2^zH;ag( z23hG~j7e&Y559!cuYq4W*OAC1%(TfV2jjb$h;hgxf&H`OfG-OE_9c2v{_B^2eUZNd z=2kOf=5g@fU`prgHM6+Nrp42$E8=O@J&2Mr=0MW-tV#{QAB?>o`K56+hQr~sDeP_H z`c%O+6c&GYmcZH$zY{F6k4q$W6aFU|dY#bo%FfOvaF(>yh5T8o8~MB4JBHlD;$+;* zMeg-lYjCf}Eu|ObCFcx#4Sf#a577s|fVEaXa`bs$veuz=?QgOcIS7sRH|e+6xzLHn z3A|SZEO?s0=RiZa2D!JQynC!|@I6@H@!7pr9?u3N&mu4Z3YK%<>q%?7wF7xK;G2fE z6EJD-la2nh_C48zH`+=-{-M`RYmfCH>a|(&@BM#!JGub>+#QOkINgBm#0%QGvohhP&~--tW{NsY{D zP{=r$>~Q^uMd}rv&k5tq-X99CjC#nFsVOUR3vtwWPtljpFpqr*DW-K8c#u9Gw#ibu z3uYpaI3o7RvG_x6chc30XSOq7`L;&saeYS2@B3o9wyI!g)`z7C7HP3XfH9k>FlZfA z?Yb*r*q!}l>o*gaCV1|{Y!k zvwDBPgZ9b~r<5Rl(jUi^3w&-pNVY(y=<6CoD>-Wn9Sye{1NH=(g*Av%Cq4b(4FWKy zRb4HSDu-&Xa+uMN)33o)TmXKQAeNwLpBW-GvtS7pETi|cwzO^L*g9`-tm4sy>1!9O z)5m^J13BZLr<08n&UWaBqydmijq-fBi}Hmd3eudTxwT^^AEbI@&!!(m0#=tdap5Vt z5DcGVeWKOqNAt7(tgGg^kl`UFInXT{z>KTsGZ)SpkgKi^VtfHG&ed#fj77P$gR#CR zHi9rZ3uu6Gf1!0fr z3oW@HzQ#2zSrU(Lu-e=XxA-P9iifi*$-!|0EMdcd79YviYkm$80-xFqXB_}fErBdx zNK=6`+q4#?5j71brgAZIO}vRnngW=IrM(@@Ei7F`X>p-CgVJG=@4rMnaF{>?vv>6q zZXEaLK4Yfs(T>&ub-~Z{)ViRJx(8vPo=X|>#;?=~MPg?P6`CIev5{oMx5Z8^j`U?x zyDk5K3oXm=dL>+DV>F^)sFGheu#PVBXTndI5Z2|QF;UZ>5CK4#C`XPh+VCVcc5d-C z68?_DIG(UCSq0H+f(3A#fpJlIi~+hsEp>^MZZhq((fcS-eyb)@Ww8SvYR@@U7wXnp zs;-wE&ZOpU`+A4Do9R#@jwt4%_{-V0X)uZ*r>=bx;bhnjTC-<6;bB#6jN-&BS!L6h zAWO$@w8`iAjey-&u(^QxbGP%6t-^07T?zIV<277XO`m2nY zB_|D=*>EH!X?Vo4P-~b3xW>^yyMaR@V`0YG0yDKoHXaqD!rOs#gBi3~2ih2nGje`%b1dg3aDo+U<8Sp-&OyB-*Nh%>_JQyuG znQm$-&M;xZa-SrWE=Wu={|gdAUf0}mpCn{O+JuDS4prZa$!#vOg%69l1PaA5Q$)fb*z|Cwevo!LFnt~LfU1NkoO%vkKn60N># zTkt34|vc`>Nw0hB0K3wW(#zP!_QVgG80yS_%@ zPFMKlM&T}g&XETD8>NQ);oTwEe!%4hfNgVYo7`&e@{5Zw5SSdYb=}bPGDPylMb>N!H#AIe z0ktrDjm5b`dVpI`O{KWllmp}y7fABV6w%({+Di<<3vMipr4b-b0V<#wVZ$+M z(%$sU%FFB!2-yfwF6SMjmP8SkSU5H1NKvbWBv_a)OY3F21kc%|CM)Mu4rT?2gCIBW z$f2mSve3MwnULu+o=Wuf$Vr^l(m`B1Sh~oWpJqp&n@=BaSEVxYan{Xmw2(^>kF|6? zBo#-a(4clHp%(#SY;?mki?B?Pg616Zh%0ES6jW9=>DFk$(u{m!uPaevVoew2^yaf~ zeZ>103k0QRiDr&uV1OITwYyW z1CtD|h=_7r@T9(NQSi&LQ0Ef{YsJMStIqgZp1v5bZwY7RqZj}MXOyXx94Mc{&X80X zVd2)Ax#^2DX4Ba0#sA=}d37-ivG8pQ=*|}6C0@c0n#5mQ>xGZ$>Jk~7)Et3l#Y{9D z(|Z#!zD*=76y;UX+QXKE)J#1Kme~pd)NIw(CKF{}AA^{htxn)pMxb5tj;-w4W*5x| zWms@c>zd5=GH?k({uIye=TBb4y9tX1x1sxCo>cH6L=4chw3+1Yg(Vuj*#(lQ-T=pV3$rsqW+N1KMb>lV$IC-}3q3j&a^m_`;$=VWh@b zEiJ5y+TEhi51>Qd3OD9n@vaj>OHAvYIcRW^&iQ(dMrkB!I>yx^e|SuF{Cg zR)wiOE30$|iLp6Inj}MgLA_#=WdZ3iwSa7knG!cr&F7aRzWJCe*{Q1LtqFxO(OrM@ z!J04CoJg=n=w@#NaTT~rzu*}Nl&9mYBZOkiEdgiKZQs5 zy!N$@iBg}nfbzI8bet>iDL9eIm(*)+>8~+V`e%LjzxyWp3q}yO#bd+W#zQ0{@d*Qp zCQ~;ae!vCe0pDw@1!N^jIyodA0Qu*&n{^9#SOMK?@8wbqI>V7JK(Xv;HGm+&j8VH= zg?a3r0h8`QYRNZxoYcabf{U}ft5K-kienbzn6iM;-mf_i@cG~hAf3t0jo6 zul}8lh>SfRA&%I>blO&b|F5BX(q@u+c0355R=0je!DkhGPQfz-GPr-8=5{+m#oY&g*Cj!W8lQ3HH}5$%|OXT{-B$sd>VD z*Y2*g5T52WuvKI8>)5JDiVrw4(ia?Cod?6nItxr2k(1w4%APsTuE!H9uWhM3PJ(DJ z%<{Ji7NZ@L_E|*}TRk2L6k6xF4(K7C3$%N%ZlfnIp@xt)7`3Mj#$T}60g>Cq>ag=; zzyYtREXq5Rtq&&0BTVYR?v82zIxunIM0VtFht?H6Gu#zBhq1|hx7ui*0&N00B>~$8 zms5<;$i~zKtSz-G5I&{A)?vqW^_&7@FeVPP=4ZP)u1r5Bcuxy5ZMy<@*~`Inac|FX zVYqu(5FmrYrLvC0^8yk99urWI_-Z7(rek!#N(4o6um8kQaj37NINOK14=jftk!1{Y z4_~g0O04<33iWG=F<(~!-qO{iu7uzdeoAj^x;m<7uPJy>w>;<60pt(OLJkETxKKm5 z!IBs!%{Xo>D7LkgxLT-=4h4qqFl9`QLA!jeowY!8z%n}Q2p>|QUTF4QpPjx6K3cd% zcX`C|&@?h8dSxbN-lRfpgFED#H+7|bP~0{-rK3@NH?g)+KTZrr&mvEa=5Z2tTV~ch z%K0u+f4Eb5+*6=g)j{KE9EA@n&`yoP^i1q*r8AFb93V@9RaFm&G*OA(vtGfMmpqz< zd-P%`dI3zI981^2y~=c--t|ZCB$|f%^=M1{XlW7Oy%cfgvT2&M>L?qDTKK4Pc}z*- zFK`4wt$3$#?Le9iDni^6abqLjpS^>4diHIYg95!{Dm7l zDOtW2KB)u`IV!*d=L&v8L~r2{<@OKAXQJ#C!o0zAIIB0$DUb$bcv8U|3QR}lySn!FukAO>*|DF{7F|D zeF+8^56fopsMCo(M$}C0mjnW*?er_yludr+TUc=rs_*-k@eOo8o{Aaug`u581Lfy& z2HI0D?I@Io$~QBo;0@iyu5G=j&;U$SDe=FEGtk~r77xc4%f)iJ%>B^KOzZbtxgv<8 J%FWE#e*s1k>)`+Z literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester_assertions.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/pytester_assertions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc032c0118af16bbc82bb5969eee938cdcd662bf GIT binary patch literal 1605 zcmZuxOK;pZ5T@Q*t=Epz2a+2vMY{*@7U-#H5VTED6h8RSY%W3pK^AAdinXLdQi&aC zFLi1(R+W5u08b^GB&&l}(cAP*KzU0L#!mx7~b@<>z_6#5&eg*4&G0>9H+N7JB-MWYL`2Gxait`x8? zQm18IBMFyzS(&E<-AG>e=EK`zR511<{Hd=Hn6vR)@~yWCHsL1Hqzx_{#L!}Nff`+U zo49paxAiO^wQ)N-b=8-Lq$L;5cMc<18}Dc__2dXQl;#t1jDUysD&W&Z#>Vyk#cn^# zw9E@Q&WqDk$so(pL_%Km*`#B`w;A;d+pJ=7tn-p%eleWYSRr6P6S-K00S67AvH)Exwp_-^8c4$i;AV8@Z~pl=%A zTAmQ36jF83*|of9eF5YSzEt218-^*kDX!IYg#8$%Ckd)swb%W_|@w#Y~oa}a*Y5~p(N$9G_AjFP?}>`=90cF^~#1w zFhRO}0SU#yoAoiOMnC$Pt|8|NFAQZR;~#1&ilUKIidW5St>Fw^jjkI_mr|^0jxMFC zZ1`-^x0i$2|JQ)>U1Ppy;C+N?c%uhztRI<%Jpp}t!`=v%yJlM>tu)fw2JGQ3n%!RC z0oipCK0v3U2xRPgi8IWraO66}yb5D^4`c8u*-Qq&qYwNxHl1`)mN8LeSr_W_y5uL) z#f_wAz+8A4Af$WS#+^y~x1wZdmP|8sUdgp>$j(z*+GD(xl(}EhW)?qK3)WQd&>Tn| tBKX+KcDjfo;^2H^QVN}2XZLUSA2--0`1{U1yJ{jF@`sCM7*aRv&A2!c;h(wLG(LMB03@*^(c$6~}R$L{7l0o2}fmpEOOAY?8K4;&{_+vh}7-Hl1yqZj)x?Zj!o5 z)7Y{j_51sud*=a?w)6PxegYB~=bn4+x##ge|M&l#+Q>*T!OzUXH*0GzClbG>oBp36 zZf@f@{Bkal@Di1Tm-JE#$x71xrYb4>o35noZ>Ey5zu8LG{^lw<`md=wNtBhH?Kzeg!v!#bfk5|SmT_nAw zvc=NFq$es9mM)RrTG?vp5z^Z#+blgwdV6KNr8kk@QQ2YXG15CLJ1xDL^sdS-OOKPj zqH=|$w~*dl*=^|w(pOflv~+6Ws>)T?&#Nm}^LOjQ)r;jy*`949{hG>aEWLeU&*C+e zYb?Ek^tF|1ExmK$wTstPuCw$m(t9g=Eq%qpzQxJPq@{N+T)+6b%IhqB<--2O1C;}o zzKZlzWy;c5lRj8EXz4QP8!9(g`Zc6qUwOTy_mIA^a-*fMA$?QjCQDyS`VEygSo*c3 z-&lF0rLQCXrplWvy_fWxD{r>+KGHW=ZnpF!>9WZ~j`S^+TP(ew z^xGh#y4=&uXc(`)d(l;y|Sv*=fYU$T699z7za;K$l zBz;%qE=%9EaQEUnD(|rL8%W<%xyRCPBzBaji_gngA(hpP~ zu=HCN-nsZ-2-?s3`;-i&EEqyELcU9h%O!&KB zNC$T?`)^-(_hO||v8T7uqsJ^&URq{@Eze5ZBS+7Rv6W*+SW#P_*=Xw6K3C};5sw{Fh=bhy4 zBzFyeX?@vWI+yUz`1$p~J9Qy>E_o{9)&Hj?IgNAfnmCudkgbG|HF$U4JI=eu{YAC< zT&B__@3i*_d5@5@;y1~Ag1iN9k-SC!iF4`7S;nOCV$xgkmTBvg{;Y4k^3KryzzfM+ z<&CDd!W*g~U1@Rugm;$vvv&U!@6^2~DZlEqDBtehKjpQ#U-PGUW6gV-H=aJ1Vr-wb zocDQml5?l!ywBfZW4VnH+ToYypV55mOlXdo?YeuaJMS6q{V!&`4|wOO`Tabr`5&-S z+x@pvw&p#{=zh@q5Kqt1jvA1%-5>KeolLqpc2GlFGZ(+}+>3eddGEu#^DO1>awBk= z_qO_1dMUr=>ajZdYx>BG1@EKY$EfLp-g;Ee7XL0Qcd1rfT$_5~TDMhg^v=_^k9(hZ zHe30S_k#C5&n7C*u?CJZYoDa2-|KyfZ#_>=cTE_?f1meh@;}^H^83Bdkn<7mf0~i`4v4=I68CgUrT*20I@EPR^6}!`_dO_apv!X8q${>Y>EskG9_Yz-sf< zQls4T!)Dp5hqFsh_`&M_a_Bc#mdmwx!TG?)2q)m^<=pgH0!kmlIhzUtNbY(t}QIoPAvGOGe=yhp*#JCAJm#l zs-bWIizCc^5&7d~xpQz2A z=Hc*3zd08!)Ej2_Wqd}dz$SH)iIX`eu zM{410yg=d@VVYL>nHfF7GlvyJ4WZ!`2rthD5=;#c^iw_>X^XR*(8l{)$iW*AEX06#& z!`(gLu}}*&PNj!t9%LA;_(R<14lgz4>NYP&pR6|zGm7LEPAx5+t}iaDQ@ICfL1=?; z&9R3L-+%ADM-R{3|6ujbqceBifB!uXJb3@R-d(->p}VK=o_XN*nY(yl(=qyZ%uP)+ z!1;&#FpMVczS|$XhqncSC8}nG7-Bc~;~J)w#xk?!d)38Sa9T~y+(&}HBl>&9pIwT9 zZcAe+SgbA7TfUX8E;v}r%+=~@cqTkuU*_*H9a##R)#Y0A6t~;&w%Z4|yRRND)|#`Y zjs}52aaMqD(DXz-tP2{qv&vXthnE%>d|N<|)`EtEO-?jz;mjT<0Fh`Cl|0%gT`!93o`mi@wdS5VSdBsxwI4OFq;>WYsq$U z9tPG+dl@h5<-ELCIG;-=Z*5z0b;9@qxQj zOK0O!$69sWmTONiGr|((_zC!msqww?iPiG*!pg~dqYR?(0Sk~qd0$+te74>^RbBvH zGWO-Y)xG6f;Fp&g3#(;S+;GqL9(ZE!ZPs&$$RPzX#k!t&fwQ0~X!i%Z@L&%3C6GTq5EL4m=Yz(H^qm(FPPSzFA0 zSjo(&k#AZKe46fuWop#t%7%6>`hdOFtG_cAcOs?@d{6Zq`7Y~=X^IgWR`IDNudnta zbVO5aBT=n;4qq~TRhCPY&d>SvW)-qh4eBRPHN$FisXDu~xVX}&H&?6S%CgpMCv#$H zX`wY6)m09gfdIgv-g0B9almkKdG-_#Gt0P!`^$@}t~^^?3H|a)<7^EY=as{{=a>Dt zITmraPfu#i^6Jt`P!{k6_3-rMWV%uS{hVYGFIR?Yp0{KaqLL$7Z`3DK!R?xYtQB}E z5j@1*%dLYCvy{Vwe7{jUxzzA#2ahbxt}ObECYekA!85f3{5jx1$voCwHlr>;Sq_%w zY0W`*d%$lzad4r2Lbp)$o2K4!5Cr5O5Gt&l^uvQyS1x)EWZCqoqHa?kphclBiDWUE zNv3-LUd|2m-G4Kex4Tp>nTpdY|E*lkmFiDZ%eR7usZ*G1)@Vg=2e$)gODIgJ%h1+- z5}=^Od@`75Cfo4Ek|Z8-d8uGuGu6v8)RLMGQ~~gwb7LMH;WoIN%S*}N5v4WQ?lMEF zvn3kC*+ox89qeCkJk5h}luII6;9n-X2JhAjh5I0gg4dT)odTe0>5KcJKA;cdeJkFPcd$@FRr_Tzv0DSMUTaiRR*f<pD!^3f-2FiHNQ@@@SCvX^hae((PBW3S(TPI@JO#pc91TcHc%d9))&15|qkF3EZLc>pTWc)wPT=UB<=;3J`hNLTv$-4|I(X1q znhik?s%dH|IC;=-91NR6!w35sICZMIxM1XA5qixq&n;QyzUxgm)%sCTWK0cXBCIaI zet%31b$8?b^3>E6b1lfQ)`#UmA^wmFCSLmGJ(aJ+GKh|CF3I^>18@No)-V_XsrOTLmBHoW&raFRUXImr@r?>pcJ zAtuilGcWb#gcvk09sSKjf3wlwT=X}uzsZoMrj?UhC}_ciKAE06z7BxV2F~==MHA5javjc{mBf zo?Z-3f?Nx6UMHhea4VD5$6eI=-z{o{x=Ce;$43R#&qL{UCcZayCi76=NU=;(Q#W+d;2;QG zLER%6_7UmgEm9}I(kK4hq3*oI(y%#z|)oSf*4F<7l zNh6QdnaM&Yy>wze*h5t|;%*K@ZabxzeHS|G6ljd$=uZArEi_p;6D(1#QW7$Vk!+=) zJ2hP5SO|4;9SHORV>QMvZtzd=FdXNSfP*T)Yvq#LlB3B9Qo$Ozo!ouK%8P-Q%xMxI zl+8`OMmqk{i0k(-zoBrKO?|4J@{*@h!Hk5vb*BG9GJI1LY$1+gJqv{f)0|9hPcXr6 z627o2k(Y<7RWTmbDmgEug7=XZJi|r9 z>pzLa6gp|WQ$z};pmk!auCOSG%YG#Oo-t6Su^NW^bR99S+1h0R_Yi>!VH4i2D-Eo#>hSVsSJ@}Zq zl(z453f%_ZLGEn-Bq-foe**nx{~CksJI6X?#iU#JJZKVo`RpmW2@%$aK>KV&gcM_H z7k|qb7^q*&oJckc%O^qIjj4-PpPFzJzu^upti7={R^B=@c_H~o0^TyEd3-53&1%cf zHSDXfh07F{zRqR?ZrSGYAu=8G4&5S#>B;5b)_)`v>T-FZYIT5w-fN$DUcZ&&SttV z2dLHrOb74hEe0Sr7pfD8`h^p<(APrmo&zcc*6YNP^M3SLTgh9WRr3W6%H1W$6A759tfdx1Mja?86T! z)hwtkyBkvKJ*lm@iZ9`iO;gqIXoU~qVmg}&OJgjw@ zo~f^zY?$ff5g0;5;Z6ZXsef`QSe5>F!A*Y7>2~!QKD@W`!tz<< zi*f)0dIH7)*}|ce@*AdZfRA}fK(q#54SR${Q9p@p(f5u)=F;fVMICA(+S%>FKI%oK z2)`0GO{3Du9+qx3_(44wbq`|t(m`itn%GVro&`aqDV!QV4X);uV7o5!dU?b+4wmae z9bO@LoAT1AJc4Ufq@l|Kmr4=7Hxd?u*DE9Yq*R{4Y0{mHuuW$~%HsGt;bcDe5xw1= zkKl8<{d+2(hVBJ_U$?fld$?ogCDFHdm=2}G8@VLlflA5oWGPk3>>wpJI`?WS%{7^O zxlky9mbHvhujbMd$+6T}GWaRpn$@fQKca!R@f-dr7e9fGK2b?M3~s2T{Ya+;?`3aEoP?(PBmSt}=Wa>(n|M0Lol5c7%fFZsca7C$Gv(|} zulQmTb!mybaes?crO&cxDifqPd1IE|N_w+5Zs~2Lw|Enl-tKRo-{EbI@4Ri^c5FdA zy{r6P{L6Veu?Fq(uE3geg})nX&{gwS&zJq2*~VVO-!v`K*LQnY^7TC)6ngUNPVs?N z7@Pa*Pu3eV9XCcb91pSc4X)@Ki?Ez;cb6iE4F=h=Sk{X^tC3qHj zHNr$71yPRjJ`j{pz%-hI$*E~Lh@VpjL`e+ML*LXRlsZU>8~PP|lqV(iNRyCA7n7~c zeFN!Q@i*$w+oKLSW$xdgKcA<4)}P>?D>b zS3Y5J`g(m@H6>CPjc|84@6iX0C0t2{ zqwh$3K+0Cs$jHLR93lnF^qp!QzSGwlmU{m(qglSMwyHHOoA_C5xD1pmOySB2TQ`2l zy3QDWb)%&*chvDqN^KatUnUO+r9>!}E|DIEsW$fj z6)%!`9PU(i;~%k!T>xuy1282nBqYTNsFKh&WVke%=LgoZ3_`Za5Cr?q>}Y4_Giy1^ z$&&NlW^O*eUTEi#43X7g-IBq3+o_<^&a4k9Hz^5XzIR6r8TSP*wO(vzE;#UfV-glBylf!{TvQ9qm8TQb3;$yTpzrYhW!a}5- z)KVA;W+B{8Q&MM6hQEf_$;>a+8*nT}HhxvF{Y$-(HEI#ORT=+67h`4yXR1?V)zB(6 z9Ph70Sf*ei725-fVo%`g$6!e^{FS80RBr8?%HI0}a5^wiPf({I)9Gly2MOcR;ni(= z-qFm=XCXqlovePF%7R4p;5KwJ2`~LYoM)8F#KLbkO3fkZdEntQaL|P!?KLD+PhZ!3c=Kl{~J`C%HgTa`rcbgS^2X6M8T zemIa-2L9hF3y7Pop*!;U%l*KQaNuqHhM(gSx6z09_mc4LI6c5AO9DWc4#HJQjX1EOTkpPRPbYb|JY1$9e;u^>C%Hl6IqanZh`>&bspYD05gBJU%8 zlY~Q8SCbckpiLFL+pt@7MrQifwHgxtXp*x0TEiY7AwIxO5KP*w(Df7w`!v&4c1{my z@X}8t!kf{{xxcV;Pb7j;GmZIAB~t<7qR{b~lC#USjvqed&z_bETdT~p#m-BxYlF)L&s)=XA$8QC{Ih+lqW;)61UrxUL<>X_>V1gf^7r}E} z3YAkikRa!Tww&3~(P+YQq5kzvP|Q01Ig((qj@W3q}#pE{ov1AJzG zGj|To%UEi0X7Ff~YQk;7IIj;~Kmo-42{^I*TA`h97MZ8!nG>#=lZ9!+eKT6BvYtUaf0%v`jEZ1TFsXS6cX83;xR7`cGU*zcU}raMr{-Ax zrU7D!7@zSI6h&j6F)L(`b4yk?%qYID@|Ha9ul~HIRi8_s#cRE`e<~xLNzZB!zaygo zuTG9R{?nKvjCr=k;4t&>oxu{_GJC|!R1IT_pUg){1^-yLS+y_hjNg6d^!*PWJyN}W z28Y!<9-cY+5Y`CKGX35=0BTR8;NQeX_vr~ zLwe#B>e9IkozI&&?2nAJ6=N$yCojr|{sL(KS%%Ru={_lR6z|a=$Esl+a*oN8NP@5P z;N|4wGm|+337Hla7Nn*Rq?HVcTsj4y8BYi=?39-9RFtQ;$Ul~VLA7A;d42X-E=V3- zl8IL!uD;{PB42-(Kl-J2YBI9})-yCPLE zhLs^F8v?L4j+LAge%xqeJE^(Fpr+<&c${Avwi$VUhN5LugPKJ{v|&jFKgmnKM1k-& z67o?jnKX_%1;aotl}eUaK(>4kbw*R$5ECcTquD~L$h}Mp@jn*aH(ERUm)3x_V|sF8 zqa-8T=fpDcJ@^@#9@udWU(+*jErEE2K=dpa(q)S-PE@>GR*#|XQVt60x|XbUs;{F) z9B1@E@|1jw%QOWpb0hurO z`T3zpPRYEOG-nXWL!}C21nCIVomo1DqdO-FmE;bNm15*M*v!f(gl5$A*{#WLmmFIU zLtQphjW3)$iUbhi8Nxw(HQnm@dltBHBq%00gfLW{&%HQw2TQ>(P^@pDY>a-Hyg$++g1{dlnW{+3- z<2r18)uaEezG}Rjk19kp?lG%N0S1?L%CX+g^H$9NoJ)N;PNzuYC?FXv*#A7Dfc|>u ziez1Ou>#_m<}*K(_-qPJBi+n;1q9k50nc;Exs(aZC2Y4~SCy=S@d1!hjS$Yn-8NX| zTKQC+=s0}ySDH&W;o)TMoYlb!xS7RHeejxca5N>Mb%#e$LAT(#d};F({@3-!6HBK7 zG^XO=WjFH&o4EcT!Q$m^#RTIeE?*10&`#j`XfANwFoLOKx5kIfkrcbJLwnBKP4&|r zdu*#Etr_{OAPhD|)nyFF2}Avcv{=Yj;kUQQcvA-!*T1{hKFy z0_#hu%5YyV&;s{C7Y|1VyVcpGd(RAeHjdLX4$ofkq`coR37gfI(v^HP{6L$=-DuWJ z>FMd|i&}J(M;ga#WbHY&t8#IfJRTIcwzwHp2SNg#gZ`!r16N< z5`-~9Xv|61+KnOuoiBl0Ve_R_u!nC4*Xq(mhMirP@7M@y-}zkRWlWYQPwP%M{ZoU> z(ScID7<;VeObMKyB0AD9-{ z36qWXcl-G|rEI0|CiN|T!>gz!k(Wt4Ba`^6VjnXpqhzVlw=%h1`PMbxMNbE~e__fj zzyJT>goAYJvN$0|m*u}LtN?omk*D<;nQyMh}Kh(;Ych+9t1<}Lz=GpCXH;&kQ8 zevpoHyPzAqR-h*I{2kW)G2n!?9<6*Yq17(7Il=||n)2<0U|xg^?%;`VLG&cX1?k`y zsWFg*5)h1^AimIapn{5kNd5j5zx&&D;d-rk@kNPz5i6u(HKLV&&n4Hx#v!=$+TU0T zEf-r1L`H6{@<2wsz7>AerB`@dPrSz2Um)t)jY`=XXWxXxZuLR}cp@*~L#^@+>$}u? z?RV9mffX)|hgi$Q{DvOi(G;$u^-vUGYXUVO{=i5=<|_AS;)P`6h71nmtM5so-AE={ z*TM^7dtT39NX!=$FNk!OY^OhvdNlEFrhhH{bh`11`+EcIU~_2oh3L(z^rn}^g|xU0 z{%ONIzt;E8@C(V+iRi5b_bo}L)=n8A^QTeVB2AXoN1CJ5l&MO#ECs*AHEL4>Idpv! z)~+lvWETDd36`%H<~Ljaa!7hu%{H{*SYI3POrsSKM6K9Et0!Ja1~+_w@uHsMMei2l zcWGww|i|5Uj;(!(vxoE~hs!P9buWVkndr^X$F?x1u;UF*OmR z7`=bj-P1FNBA+%C@|XZ+VyRZk2g|Yt+S|s-AB2B`af6eC!+=tkr6Q4#W_-VS{`Wpd zw~0i=T8J6<1bX(bis-aHez(QVTCA)T{q)}TS*Bon*}YfZ`;>FfT-!TUybG34@geYG zV!T|8L0{pJxD*%R=Asu+)zrP%HA3wRr;0OiB=@1>Q{AZUHCCsTSe>D6R_p(COyIH$ zf05UInTT8q{UZ*g<{BA$7@{pWON2yuW+^s7Xms2lL{iUw8!}DBZBd=lfls-LgNrla zR-<5^r67d8#YeE_?KkF1*?K0`y zUuIrSWmDhWmK!&n9`D%wjvRv8j{B_Lu%xyM1Se5XHf>NgNXJPS7CO#frZpQ>2>~1Q zBm#EkNjOh=ls*v838!eCSnN{ce zf5ax<#&0NdT34sA!%s_5=%Xf-A{|R8Bm!?m?QfJte}H>1R?Z<;$}5mFI(>(Tlm6+x0Nq!V*iiL94FnMA~jibY3Y6{!)$usg!839YIy}jFg3rSH{Peq zXLQlxAkw@}z!QouY17atnuO5n$KXf$CfDZmD08|bAQtUU4C6p+`mokm#{_>RDZ$=MwXPHj5_Dc7>E$x(yEQb|ZeN z_I*{J{8=iHz-qB zX9o)fFExu@g=V3R4Ns9b!JHYg^Q_kbfz^FX&as!G$P_KC%7`5HA#l0KH95_7nJumqHlb)U|`qX}g&W*pSVE zLTB`}?=LeJ1Z@zQ5(Xcj%afZrIpRF1&muN3YN}GX?5aLJQ&qib542W;cdAYSbMRKB zZsStfq+xbd3oODQqemapHzES#mcDWY00Fzb@mBXG+w~@bfiCL8KP!SKHSu$TgKK#= zkpbtTpPG0z^-3m-&xYWEdpYS9VTm%SVv4{3$FNWJS1};KW~oWP*YF#DoeL9X>Uj~P zb(UF#3e2`~o{$23SVVZ3d`%|C$UB;OrkqLERB3TGhh(~M7BNVI5-3~pa_3VR4?hFh zADJJ;6q%Vv(EKo>=K9zL28jF3hL%3|JU}Nv14Vu8Or<%llIK89%`K4vT>fJ{bn_Eu z^epN*e(o`7i6_vaH&B$lpX96?+c(9)bX6D!ey z4tc$w;l>7$jm0@Gh9B*DJ^?J?9G%RVoeM}z}HZ(5$ z7u03ZeIY^x{*_FXV`06pAls5DOpv2U5(UPjfHAVbqQG^2(}uwzLU9_(F>|r~aj<`8 zRCGdsxW3>nU4;FeiPCU^4T~HDtkZo`gb5HbCiO8>y%`ku+=$n6AqUVV6W>T-NzG(a zUoUMQ9UIR~WX47kugYzq@JeZi@!L+1D7cG%gwXYSm|wFWXbt0vpK6M{19`zNR%i0& zgot{uPYws?m{^oLiF15XB%6TQ)x$`Jrps_{OtEIoj4OTlu{A+DU6m=xt|CW15MviA>0bXUs8wKuq8cK@9Qw?sL`z8O-)!Oc{nth6UpYu3~j0}P** zlQ5!v`!S zmz3oO+L_y=FFN5%iQ!47RC9X+c2()9pvq-a)5iN#b@d*2bGqG9m=LL^%=a&1=N$Y2K^0lNi`T9-sYfA4XOJv@nfRRtN zP*N{%pJD2jS583a<*WxAz9P%=6fk8d@EP8HDHU9;;S&;NJb#uyf%07m8{V>>_uy3} z|C@$W%}AtMn-42cb*eu;`XX798hVk-KgLZgOUlJG^0{+TVlucFq>z^M7+&w9b=EUj zm&Lmg{h((O^A(LOl4#y~5Ow6sWbmQxn_l0W(jZ^vO-L~wdhl?G^`ibZ;q%1Fi48cZ zg^`au=UVp>z9*242$s|6nNP!pP-AIlYbZ^fK;J*?>^nyw;2{*WldXIIs?|08v*=&@ zCb7S^)V@gzOr9KZKDL@Q+o3{@aI<-uEDQa0YNb;uk)y5Di!pP+Kf!txn}sfNZ7`Rd z_-XJ3RcrM3QjQp+C*KgH+(}SQR5wq-HL2E!gRE%G zFXCpjSPlFeBxHe>6Q>*(nkh@OV9%vLoQm8Rrk>{>+&5)z3}~I48-q&DXS;3;xqdf> z=TdXd>pya2P}yoea%7OX?6O8n2N~blV(H3#2R8-`mR7EdV%_cEAycK?7~)=Z%x%Gp zuGT9|lvztu2F&@E6~kHe+ZiW?h6DyT@f+U3C2~F3E!P8ipvZTXsp~gLI_=c0l+gYH z8%p#HlVxgihDj|c(59hB5%t(%y-qrq*QJXfJ3IT0lmq?PLpz-ZJ6;K~brR2nBZm!z zew%gJg)bUdn?@*zo&hmhdG! zk>Uk>Jzcu&0`BsZe|u}#k@MancK3gWR&nfqWF(;9+sHETHj?$eGU#nIKrSv?#>PPi zp!kWI8C%A8ks%e}8kpP^$$PZtqp&;jIsp!BGJ+1)`5r{e{fL#2@_C?yB!=lgE|V6_ z1W#^02DU!LdB?ggK?Bn^x(Z}lnty*D!nFMie;mC&%Ia61g;wmE_O4sAoa#V zy>(-838u0$49hAbxHe}v(fd5BY@$0NeMR7jH^iTQQPtZwzC8ps)$z*$-xsLmL496S z72wMja(glc4Z)v~a}h*nkPh=}?t7wi7?Kp>4R3DlCu!3I4+&2fX$Jl_bkb3AWc)E?!| zG7bYjnuy!@4L`-Dw=}gdUrdQl$C8UkXyUXJu34-+9pb6;MbF{Mp7%=JYu6pc?G8~w z;vQJgNeu^QO#IR*3tyV#NeN| z*oPqGb;iBUPQ>iHB*kit@TC!{nr<|fq_MKt$x4BOLruLw1P9yWM*Q1*cz5Gu2%gSr zGNk-VK$q+v8oV5vPIPsB0)>pZC!qa#sq6h zJ*A7ny@FLz(QtH9vlkgEGB`S~zF)&((Ynv?p%vP%F0hoF^)sQzFe{V?K0L6>v(DkoVxW`|09z^fg>_id2 zrii*-6Stv@+BV(5{uAnzuxtWhLxM*?z{Z6JP#m@O6r5HtIiS?$;N|22 zn7=Rcn4SA-%g$~fz`0~l$rc`bpIV|xPo!F<-ssl7m#>VDWO!_^Td8c*yjLOw6BO=! zoEhbw&3yi*hs! zrsQOB4wWZ|JLy(^IrxA^qPq_I*0arsMX&W%`{#D(Owi;co#?tx=UV8oZf%N^#oK zkk4*;&|hdWr6&BXoaVII-{i_gYV{&H+wl8J;U@;2C+;j?+3srN$zP!+-WlO5?Bb`E zuxCd$+OuQFC0o-gZ7q#jvzd`_dwJ}P+A!F+U5v$q%6ePzQ*)kZS6r$j{ZR=HMuF4V zZJfq#V=rpEnTh)?p3rS*kFf>2i)=B6gjfzmUY-h3)Nx7C?)>FjqOg)FISMJ}6o*}T z6>cbZ>+!M*M3GLI30BKG#>B-p*^ZLs+Mw5J>~9)>&z_>{p5@>iPO`vPcgZY2=IRPX zn(OWipdc%j9dSH;8@6i`e)?iUp$a(5s`kLeowwGjsjG*fxwQTj_?LsnYi+(SI-H`*;2p!JbezUdwZ`s` z4$gYaoh9@*PP9g&WU+MfrmgO-d5g@H9B4EvD&?X2BgtWS(}o@1wVWm$tOxjUvP@ zZJS!*wV|yp%zQ(zx6%;5T=4OBZhpjF=SPkB6>wGjc)Mu91e+)cLF;}yLvD_+0=@Gk zh@!srrRLcBX61N85VMjOp%krNz2}D1aJeh8 zW*Mxf5Kclqt+TFDodD7ozj?^mG4;}Vqxgp2&+XXL78h#=LSLoqJRf%u5HYDs_E9+s z7;+L)iW~-P*Axx}S_%d#bBBH$v=cv~fwfS^GFDgGPryO9c&|}zc@4H|CYq(e+3|Fo z8HdS%M0MoV(wz1lj8HTa;(}g1b6MzqM?Jmx0F($9_q9jmn4#`DCedgS9MX@m$&Ii$ z-|HQ5;_`Yyt5Ids-7$9?B#Wl#xD*90bSqk1Aru8yf^KaCO`kqsgUL`ks^AlZ!pSWh zjB>zV;2=XHSK?aLAZv(0alP3CBsB-{;_EuwB5qV)=cCDp!E10XZtrfg9?yQb*Udji zM68?u;O$?+4NSqEl{X<7gG#W=9aG^3>#CosG~Jb?ewl|eHUuE z^g@R^4bEVE5~S+f$Mi((3n>uTWSQ~?GRKd{0~;PczJH)$Hm9@9lFqvk_HhU7F@}N7 zzEB?SDpXfC2#qW1gE0Z&qc{_R%-p$@#Cj_>yxSN}6|qdmkF!fn;Px>ySD}5A$B$16 zW(Hd18o>Oi^@Ne>93UoDhvFb%Qj0@5bVM~anl3T7uvAlWpsM4?1+6MT^N%0b=ETR3 z4?-^Zh)8y35*b-h`4Ja72!AH53ho95a<3&3ntEOf#+vCR=4##1K;2VXif@JZRZc?o57r^AL(Q=v8Xoo9km za|CzlSJWh6LNF3fM{gok8{3QF!MwoM2l9Ry7)zV_m>Bd;d4!TpAu_m{0fZRk1120$ zD?-f(swOLLi+~$LXCj%5y# zoeoG>z5dCjCx75!d-q0Ll57}qirhff&DK48BW%A{kW~|!_XC|;Ao61?BJxTCnbQ zTxN2TtY`X`&X5#)CZ-1;Q>N*IOq~PAAE~jW;+hujCTBiQ%YG+~oLM1}I9zfbAju5>a^EcG<;ZM`rdM)=KaOgT#0y5D=t?=mBG!$2>~F?zuCY&A({st-+O|$7QO-6!GtH^o3$SI(r!S=0#Sej$dv;1D zFKc_8ct?0gQ0` zcwgT|mb(ul40$h%qy<-jjUxf4tUP0&2E-LUV;tYcTE$@WFi6Y`!$d)V#MVgnq(j@l zq#IDH=EFJZy3^8hL|`8c>u=!$aTO2w&BuEcMjtUI72c)yAqUBv;VZNh@*TMu8)IR7 z@+Ddf!y`G%wb~uC7dKY~kpYK!>@Epz!GC>Y>-y0*4(!Lg%<+re@$C(~;}rukBk&#sH!T%*F3 zpo^7BcSsbUOI7mZSc9TBvEZ+r+QkJ(bo*c4O&}(9-0awsnOx zKRQt8NaFE%^f5A(*cO}N7ul?n%?ybV`f@wZ&h>74&Cgpd#~(FE=CqmN*Ypf6)Q~fD zocX+$>-#R7a-K^zH|dJ;#k@=P^)sJuE66ZE-FmISq$lzU!x|?GMThee=dW9%;;kch zH_A_~9pX2JQx+h&xlbTRES5;o6YwE1C)_gSLwfaC@EJ|YOjK9@=i^1^B!z<}8yrMb zI4E&Ir!%5Tvaj!RD?xE8W6pn(_#?p8w zXHtsPAx5XQt+!$Z*6nlDDjTkvZRFY(3N*6&BibZ2v@Mj6lMs+L6_Q5$G!}4FqQMR? z*+}V_+_iLL-k$4x#d-JK<+o+y+pnlZl%yQRAg_3{tt+ezz>WbPdoywB*y#>IYH)&> z&jGZ6y@KE7Da(Fs-LiHBTA}vAK5ABHcQmP51`s4z&8f3u0EP**ofjYiPF*h+yNAOe zG`(HZ1mWd9!X#9)Czc>%26>P$c2TWFSEuqDppueudknr&p@_Hq|BgOJ?K)I`$QWlQ ze_X!319W@?#f>P<5q97dC5Pw{E8QIfjp^+y@ma;nEH6t$iFFK)X=j+-PIiNG()Hy% zHWlF>xtD6ZVye_r7xayy!y0|_<=!#+E0WuzVrmeTM^pLjs;>cp8_5Xl1hcjm9k3rZ_BLHCZG`D=(;I3Vm{WQuj&AqLJq?id`ZLv>5$hPb z_Lol**YT9_oOXW^;xYeEu#aIX!%3T231FfnRphp=0z49qe(r$9&r9ele{=2lLNKq8+WbO^pMeIq?g%# z3?zJyYpQSLon1}v6^pJs>MU_QPR7HYFsq?d2!xE9)YYkNbV&G%~ZiV(@+ z+8Y>afu;X9=cNh96STgq`#oHn?>8 zq1}iH;3Nfbog=g!UZ?jB#uy8SPOm$Mqj6+fyXA; z`;F5^!AdjjTomqVyF5&0YH}Am297Y=xVVJK40+`wo}iABC{n&jqU%zzK5;iu;51VByL6XO@BSz-S_=Wwf(6hBw(>ObVDpT$r_w5L0S!I!t zue-1}imoyw2vZKUhIS#oM>x>fiG5ND$I8iYa(}G(7GbslXw-rL%c4^}cKfKu@njq3 zjhgnL&hKIvU_k0&h+*rR*WY~8jeV@#EaB1RWk!}l;Og7%0d;#qNsiXfG6CHm!2!!T zp|uq5;Ud?I$9Ey3*%{>8oPdz!l!trJeByiT$Z*^jR0Dsc7lZv?=FY_c7o+xqRZxE# zTi#tUo#HJLeeC$~uM2}{3CcyH@Jeg{<&=oM(2U;8hUkdL=%TgY@6!$!X^+$gvk)Cu z<{8$g^US85Z4hTFuAar812+D07W5i3i)2yeD1rP8RjMQ`d2^9X&|EPg;u(#bQ1;#Grxq<;OFE~Xsp z^-xnC|CC5{Jza00FxchE)(&a@z$D-e3p(1>&aI)aIP+-~7Kzm+3JcV=t=mvLBycFX z)i|2uXH$~f*4TRpbpmPx+lvM{ZhM0B5jbT4bwIwW+Q>hhKl5pPWce%AM&l}Q)~7jb zfpApuT2D5e1$VZ655nR?=&|te}CAx*`AKgZ$6iLArZXNdxs576!%O-LUWjBNhDLtA)IQs zJxoh%ixjTl;_b}1_PI&1N90d4kga#1ssdRCVp=eB*(Q!`^CV^s{Lpn>K%XeBTu(?e zP6Q+gny3r=p|b#3Y{m>ae^#?7RXT2!Z&!MObnt#sos8VqD!U@*HfhWHwg2b>+{&EP z3j&iqs%w150YH**vvg9TqqmfT9Qwp;?XDx6N=Q?&=Tt8qEE zBTA&3P)pUIOK>tAx={CENVW*PZ{hQaf^^_wXS7F|c1#*e?%|3Pr=4QJUOBj*?%=8Z z7NkihJwYxf)BiKR4N6iYJEiU@qcw6k$JT`DQZ_6Mk>mJBtP42Ak!ImkMd2B^bAb0e z6K}u|=k+`aj5ZrAoz4dPJnhhtc^-eqwR}4-@C;697o6u(&!y+sVZuv2Taf+_?=Rx> zj99Z=(AMFkY(UQTT>@=$9nMT&L>shHjW%e8j5h3cv;n>W-O2NWct4+3py2sjOdUW# z-CjZ@Y-l1P(FLuSqGO6|U~Fpbl`gReeu|O2M^mE_fl9pEy3)-TV|q!V@IlQ$&N=iN zJuqvJ*?7K86%JtyITZEJDSP>ekxy_XZ~M;xI@Y9rp%)BI4s}~`t6C?YyTp(@eaisD zSgQC+E(4zbf*KR~@{RO^L+H?>fr2eI>-HT~GT=BV94VYBY7l;mOFX5|W#*6uZBk9$ zYZICS95}xnRcopZ|HYL%l(WxEF-v}-$u1839b(S7hd;ZSIV^%*3T+PE_R^;_!L*k_ z#vH~mj+_hB&@SS4&RrJD@1@^NQ_yNnw`GCJUJNE;zzs~UBVk~2)c}|f>T z$Ix0#z;pyEHL!-&3w6TyiESjhPN7Lf2tl1d3vL@lUX=~IbT+6hV^0l3$bt^QjqSvX zI5`BrLaEO7D!Qz*U1q7OlMB@Bmr{;`sL%%fBDkB{pNJPUA#y3piD}NWJiR!`hvGeq zsAWLo6ARj*$aVm8Uab;B*qD$*!K9F7$1r?c*HLefN}I5v-B1hcOu=R`%_6`Iv3)TW zVaT-Yj#~O~M%$0@q!2yHu=|8Ik0~MiBD+)*XDBIy;GBr z=GalDE62`+$Tx1d4MpjMzpCEHx?~kR+Z%QwcwT>*$?GkUjqkI6WPHey4j*#!L=(C{ z@jO|FpC%jThr@KAPZ9c(40hYzX3R&hTjOTAnc&uFr+O1JP)R|b@a|=!(gv!qHE;zK zt7}JUYOpNGO)B_+4a(Wg9wj~dQ;n)5vzU=;Jqm&0&^0wL_!gH|rhMQ)c?O?}>`9JD zYP5Eh`9X&wtEuxj?VdhDp>*JT-~wYgzM(p7tTP}Z-)%9KV{~}6vq`5=a{^Ob3kLGv z;G_0tCm&5;WjI#Xq7mgLKEa8ZO9#KMSxhz08UXdp--xR@D*U`A&(=IdMyDo;{}_V} zSGXKI`K4M@-*G|*Dg=JMsg4=Z>7PYWTJa+`i&||0Tdg=3Dr>AS;j;6|@G6@}+w@VV zjjY>#c~V=-Kl=i+5arocq2xn!0Ce(g%iiRA;Q~v6HFX^{0!0J!3{xLA-H~Wv>&)@z zaRl&)NwL*-+*Vs|+MTWSL7Lp!91Tw7G=`Ad%IpJ)2mgjG-k+nFHyT z9S5O-0+<*=Y<94>qBHvpi ziaeD$&c&L?_QpUfq;$nMQMgk)<{rPv6gqHjxwMoEkR7g1kk(dKv6#s!bE=P0# z+%C}pEk#5JT^QEBwVav0waF!CQO+0Fhovcd`~v{|kcCAE;EDHRkJMu5oq0#Q$kP*? z-dAc5&vU{8ffk|_y4%z)%@H?<9_C|bPPGf`V{x7W6)=l$Mp_)6--4{moAVQFS-Dlg z9Ufy!os>Ew#t&*@!*Mtnc#J4ipr!VT78y*RNF1 z(TNb+!P~#kyb4}f@9wP{W8T)LSFKv!$v;LgIk^?xq-f7A$@l?~`jGx5hgbT*o_ zxtWby6aSTFK|i5DX&}ZlRpGEu;!JU8aR27=oUxww30>x@*wVZY&&(CEkZVrDAknD# z?zY||oEFcPigRx3MYiLwj8cNb=%U>r*xkXMRmhXg-l(^UOcTNd#rCm(t zM0BU~1u~Vb{x*NReQ_JROKkUckhjC-`8#RnE{pTjvz^{9o?T&^!|X;xy~57Yti8m1 z+}D;6Un=j7-h5rnEJgBlqX_@IUDqazC*ZSVU8A$!$WkJEM(o?#jlt>qTP$KFWW<;mxvis+ zbTMdp$vTNDEgF%HO{2k=ZW(bI7R4*IC{8u8lf2qsx>9E@Dj&mZ!M>=T*a?PSFH)ns zLv6SD)Y+|S>4e+;!Nm`mvk4P%{5a+sX<&~Z?>}HRcAj)|79T>}YdU*0*s-*@O5g&v z(N!nzi$HhJfzx~NN@>oX;w$~b5ix1KIt7}Jjl&tk7`RK!Z$#oW_`NqHPH}KbBExp9 z<&c%l03fOXk_NQ(H>kh=y}H$^-cGk#Eb1*r7F(FwDww89gH5@v)kaxMLi$Eti%N~^V)RBT+4x6#x2~e_kGP4pqBSamJ~(+Ey5G(= zQy5M5v^lt;nr;s*@6)D?)eIXmqBG&jwyvT_q&T>tSyYZBHZgp`2hc7jsvIgkUxJ2> ziFws~9Q8bcLh!xOJEM9Yy^{+*sS;>oa#5|MjY4R%uLA0iP0nm=UnrQ)`Zsw4)5!ez z^J)bO<@qhY}@HrC%ygRwW?eBF|b9yZqE{@%%SRW6b*5zeA_!(V(MVFg&F`#JcskQV? z$~&Qp0g8CR$|xdV-->pz@2e{F3BC6M7j$~IiNVENNHKm^S6p>gC?b? zXAlCg9fS@U!`+3v;q23+5vqOAO@|%lVMnj_fnBe)|C#STtk!BMw3}3GYq!1qvwBfm z)jMme@P|}8+P9guC0o}%3^yArn9-2-or3t<4mu)=dt-XRu$Fo8vYW%181(MGVeHN3 zTtr|mYLaHunk4@v8pL8mEFP6uY%X$W{K&=sJ64fq+^m{<=Sj83mW)B5jZklxewnB5 z?G2N5$mrG^^uUc%HWVflQzCn#1UcQ-A)a$@bCI=J@ zG*}15KxJ$zL>7y5ymri`)8o2r$RbCNXVk1O=+b1K+$Mqs0)ND9Wdviy!a{`h!b>&Z z&SW^Prpr8}J^EzEZH;!J0(oF%86ix^;~5`n#MW?vUf@SB3N#ExewBn%kkXkiT4&5V z^FoS{2DJMkfbtx0CzRPqNqBUNtRNO@7w@w*r|(CJ!3FuYr^!hhjehyCS1W*aIgg20KEJi$RX zovg6=>IR)chj>FE7$GAnc1Dm==8Flm13G&!$FH`HmJ$y#+Yk| zd0JktyeIYH8sv55(9q-MHQ48 zCG@12;cytP+~p>=!?L_GOC1A0LczE3YmRe+dKDd5Ar2O;M8?ll*fEo-%-P0CtxuRd zFeAy?ZNfyN2KDfl%O`8gWddSRL_mZ=xAlcGN5%S2C`MK?(y1DMYcAd~UOP@R>AZL( zfVmwwByqd5I`tkDuS}SU6mP!7*X5GwBLlKI z$jUN~MF`n^nuumrmK}i$@jw~cDLts3ExOpHOX6xlz^A)pCfV8*uQ4P&ajuNcR77wX zRJkcRMdt4+by&dAgP|T+Sjxa+j3O_netvBS+0k^grxoY@=*$n(_eE41=VaS z(0`!Y-_Y%EbLnIiy%5@QgF2AL&UB-BVW8nN{{a`sXIo1IA|IU`ufRGu_hYrd`5TM8 zxE<)kp#@@d0}Xv(3rham!6seCblI$nD8E|@zps?3VJDQ5SqGZvcoUuL1xhsZxm^#& z2Odn>Q>`>xP!(|{BJ&)Kx)g}7tq@|~&6x^ob9mTt-s}~6)Mu;`m*hOUu2fxO=^R!s zEA<*(_ULkrF4yYvT3z<)B7_*2!>{Kx9W0>3T=%Govj58I^tr--6;IB zX}$ady6n@F1G;=(w_ni3*X^7xMiDB?`-*N~ox&~7q9ERZ-RHQY#`*S(&PyPU6=cEWt?-^xtzcgCw&%n*hdCY6jkIB<4ut1OmNH90& z`~Gw4(#@7^&uoCST*bPaI+y?d&woG1?!-hXgWpo~7w%u($z=W;Z_>XaZZ6^o{?g85 zyo_fxGgbZ9s#>_WoAze5nzi^Wi|br9C)YV#=c{?S&NmC2Bh?YPE;Ngqqt#Kl9%+^~ z$Ess;U2KkTPE;r4dKA}_)k(Q7;d)nU76QGG(L_v89d^^jaoXnuL~SoN4ZJA~`6RKFtEPd1&+r>jrn+x`CWBN_i0@9@XAcf>pTQLg&z z*D~Ihyr(|Ocu(E8tIv5~_Ktm&sXmXVU-29~b^Pb_XThEqkQ$0U-4(XQ@F2qbLht$ z?)Q81M>6v4w08#2=KUwV7yZ-TOFy!!XZ&gJW$*0AmVelP!asvi9Mpfkmsjm086Ftj zVgI;)fMeu6>Uza{_2aDfRd3;=!hNgyqCfqZuP42i{RydO!k_S;+IVT>Wwzt}&c}K0 z9BRl(4UcY}Umf;dNxo!V-uaJn-UWV*)}P)u>%Uk#>mLJjUh^&jIv4#ZwsAj;x?l0E zTbc6ZpOL~B%T}*=BlMNK()4jX@-263tI=A+W&T#9>wA25)vq@;-6k)uv^Te!{ykhy zEGf6{uekNwOWRxgG<&JFjX%XJZnJ4>%hlZ=H-Up3pV zG;-4o(Tx_0y?K^;@YTpCgLf@$a zYOZPo?N;D~ZKu_4ooaP92@&Ux+wAzkg7f{S)vojXU36}ggS)0wTlEU7ZG{TT*(Q$e!;W7JZ5ph8^Np|@rz#ZW6K-$N}%{ryye}PH;%g!z8W)6CcH^J z8TaYW2fZiobf0Gfn@{#imjrQ_-N5gz-&E~84bOLibyxYG z(+L~RM%eHJ2Z$k>F;gCi=oLS_>-(*egCC%?0vtK`D)8U$_^mqXXs<@kD~mt#F!Jl4 z*u6r_ZPnVh)e+p+NsH01fD6*9(B=#-JZm?<14^uAcCt8IJ2uYtP7Y_>Kgd1IkSAkI z_To}G-zyUT_0acvS$8GqWtHDl&!U>%SgpS9*KZ5v%7tDo@SCgZIlNIPczT{EDY(B@ z3!^!|4ye>>LV)`86;@TO)f%lvSgXB+JA?b2RWSd$2NKX$qLQCjdZ-q@hR!YV+W_5%zKBwYwudS z_Kt;*$vLds_S+cY`!)xqPN5KnHHisDmQf4pZWFvM+p{b4J5iew1Ijj3@A9(%uYqiR`W*e0S7b@`wU@%!Q=-zZ~yWuyzT8cG&Vhap=1-Bk{ z+@@|%S{CFMYBkpR8e1eWW~_0m_-S|lo>uq3GA3JUStQxp_INaK-O2)94B`&$wLoKE z!>b5${cZ7WZ(KKjOJKIAW%g63=nyVKX?p|hZ(yoyWIgLgIhh{i>;S2kbLs+n)IR~r z8b0bQUy=3d6M>_M?Qh_SdU_-c6*rC7J$>9u=@ct=xBU(g?zuPJMgS@4#L~`btV#zXKBYw?2SSCJ_XeE4XC+1*3#Cviw5f%g7j8m7D2{)tm0d6+6YlRv4XpoCR#X3+^e!ZiD#vQ-b zY(U4@bhj{ZC=hz~y$1N4wVIkYui*JIj{k`x(|vx($HRG8sSz|}z$JO_67`9ci4%v#&>Nxs-1ldI#0lS;lL;Rd)LsQ6lIcN%b>LR$ydj;H$ zcq3jB+C2(|dNrUQL_h4$&knKZ+DvOf;OMicW3y9N9GXtq;Yk?Ykr#p#jNXduR zuJ!Fq>ufF)X1CwAGP|&>yT?M{fSS0M{VA-No$QD9w=?g6Xm_)}mu;2w-}%fuRJ9E zV96(%ID%=MGFCBHg62Gle>3)BO#SY3KbR?x`{q7@%!8C2Y~eHrWOYtJ4C%L#qYm@l z;oU4H@549+8!wj)S01@aogVx983pl7H$h>AYB1N zm=EVo9tQ_4%*V4HXdT)1vec0`tOxeP40Ys(+DQKSINFDHJ3~Xa+{ejS2^F;itn4YA z8k8~z2X*FuXuppctm)$nNQ4cwzFZ?0OD(Yt4lYIb<*;Gox z#2=w@_CzSQmnX3XsU}46h&kaOpr8>gIqT4;k;-B47B|y3QOUhYQ&;#cIdS({is=Uy zI5(s#rWd#vufe&Fe_NC$aKwYc5nr`nJ6Pm>0PTDx%sl{?(>(1H8(K{PiiaQeazv&c zz~rScRE?ERsQn5FOB$-}kqP}H_G=uc3>eT+t3+Mjj<|d>vONrlxQrRl3~n$I7)&Gu zup0319r&vFjDHQQ0q-_e5QMm}Ocx&l12MN$@8ZEQ&hS$_H$rIA>N*4X-OCgD=#B(D zibHTf0)a(MRv7j7EGW?4&4%_)HiSjJ0c=2tg4bX-7v^>db-pP)7~#8z);N-|&%;kt)R3ZDqrQn#FGtN-q>oT;uYl>+XobCzE#pMWYj~by;Vc?aUzV%QU~Lex z#M?t00p)cDo||cF3g@mdYGU*!>hve2FUD)xLaJ5ax-y^M!Hd6#Lz)J|%}(YgM~gYd z=XD%?KN8zT9Dj2x*T~mf#z)g^-1fU~{&svf^7o`nw;>Fb{D3@=LkehuQN!MVS`Lb| zLOaZcxz+3)OFb24sn>z_v??F7TkVc8%vM!VLm#cPe$LAQJzo3~V9^8K2qpYgYwcR2 z7LJ#oiE8_w;m~}wqUT*2YBASKG1tgv0o{P8G`uyWvsb;&2g^K>?HWtFS_JUKFQsXv zpY8r@6f`WV1PuwM5t;9o9@CdcbKnc8tNRo=u;IS=3(kAAkMr8w^8=hW`vBfH&3RRg z{m%EYO~0k5CKbWY!D;_I8HSCCCY&~&Y6BqM!)}RIlFXH;5)J|E2#hLbsl9Bsv8B88 zDBJ$u@pIOdvAWL<1K@AMNkxhQj^O-*oD@$7IB9mrHk=gHxQCOz_qjQ#8@djx?hZUy zDt4>GJ1_bt{Ob`Xjbz%j^6jrQ7);Dn*7_5Qk&3LALwNmQYzq{Yc%F{}8T)2i=B+PH`#Sj8z) zO5cK@)2?+NrmR0Ap{SylJ(uz5N>4w_pUe0quK;70Ms5yn^>Mk=ZubdqtUBrM^Txf2 zk02bYQ{EWNs!8#kbzlE2*(Nm4rf(eNm9_nUb?R^Z@jrh5eDCPhYu7Km_4-on(&bxk zy#ChGwc64fuU~uf(&CkCYd`wyfBWEn|K*=vND94q?e$AbH{MPa8g57z0Unt{Kg5M5 zTr%BNTf8O^^1S9}HYHYedGn(X<`zeji8lsJ_mbj^4Jl-kWs zt5J7D-mSEu%2Rb8aGSX;_{!G(*t-Yo9zj01OC)r`4-(HWw0%~!>3V+3jd&fw3jdx< z=i#igynMPc@BGD|Ij8+oXP1}xVCKaaUfL-0*~@t5{MR2o!1d`@=l|kQmzNW-VmDhk z?RTh2rb~3Quf6tKhXRS;m&Zhf(Nm97P*FAOhw=?i&#HQ!kFWCaXgm~Hk(=&X zuk0Y-DfS z>Z~{@a=Z&cZ$f)ZP&BRRF8Gd@*Dj23l%?*I8h3D2YIo__2uIl>RRG+LrONzRzLRfu ztizb8_-6dJ1$&-UR_HbiiNP5K!ho9))UgH!UMq3ILLqYDhi(rcwV1?cRW@2uVoUj_ z0)(3ys}1o%8``V%hf2PfQc||G7saS0F9XIYlT5TnswMx6&&Aw(egJEfoIZ2=g2K!m zpkfJHf$fM|AS?!+K@w&5c1F#HHrS2L=r(PakopXwQ|bbq=lKLI30}@lM!k)z46ZC( zLA8+D#J6@I5pld5xo;hWCvGPz^?ro!M+WN6qTWBk^P;p=M9dPb66819{f~zHNOtjr z9m1wkyh%bF^>zpG5qK(m5FW@DZVN;<+fA6csd~W0TnE8Uu**BC=d&@{dj6JA2i2O> zZaH_?p)9Z?2%aET8jnhx{H;??(=&AO1IJ8svcL4Rl!LooV^VYs18q@S?VrN5`Ej^ z6`caT?52A!npa&ss7^=YN!WaKI%+U^Hpu!pKaUWQ40mOU#r&jQgp)%AfOuRn{YH3i z+r?k!a;bOO$hBi8U<~x~{-Nft4C(+HK}HJFTtY3@7)4^52*8be5ctYvh*wc;&@Fk5 zpBCXYgJBO z{N}b%JJ|vmvGk8Um5^=ot_yc^&|X+bg3}i+TyW+qr=1sYKI5D}k3TQQtoS-|6?|Q` zR&&DMPzZUs`iG!rJO-7KZ zGq+ppZVOTaStHEmfw}{Rk3p07Gao9Lb^OX&MZeJ5J<_!Rwxq^X)SiKuc<2H_jR=nI zb|(hNI$9Q}R2=7xmhS)-xR?4oAG(zI;Gr-DPO6{yz#SlrfhHq50g$Dxm?Y9P;L_C& z_aAf5VkR|+1#r)qnHUvi=M+&0l>-Rtl_ojR0s>NK&IdPu8>g zZo%FWLx;q()^2-JGJ!p4$YU8=LU6021+)3 z3@kvKH$rq5S!1A1R7gUC&KO8-)JS4Pf<&OwtRfYQ5`+_(ulwyk-K|I%rr{~|8MJ}- z5N$wf%OM8(a3xcNW}-qwKLnIHf_l4EN8Uzr+i;g9w7k45^mcD~*{L@{z7E6{ea^^| zqtr_nOk!~|Ifb*%b%O0y=-xAfbO`~BE@m*i*=h$t0~2|Wepgj{)6tG0rnu0buKOXC zF{5PjJ@Tt;cOJlLmXKFtb$BbR%rOg3kl zX4`yqwmhr{SEE3{wsTVRmXm`meE)=dVgVHx4m#_cSdsfP!ckw^O|=yjMT4f$+1+8S zE-#DW1N2ESI~Z)z09OhTA+0R}_c!}JgCGc!5QA{RdEwSKZ`_R0KwP!9D$;|QcQHNz z;Cp2ZEB*KkAHRgh2}skgHEiE2mz_s8l;Hak=vf#5D7X&-j=SbEyM*K^CjZ|cL>}3% zF9LwmeE<-){m6Qg@m>a_)HVj)H_4oZ7K&_6B$q|}&4?0rh}i^gBPfgc<4mLCSA;bq z!Jtrk^#Dw?(>wHlIXVhst+DW^@`8tLeLvy$0b6NhaJvo6ukn z!#ExUtmc)=%k!0aVa!GnKo?;gTNt%@$!;OdmE1kzkzjE3W}AYe4d_S}rgv=znbjVg zks{}i((XG=FlI#YqMkLk`yeWe1DvuStOBpjMkh!Mw_1}dclMi9 zj{eLDDWON3z6(k+`qnBmUgT}3GVv*TxL5$H3Kekb3mgh6;)l+0C=DJ$3CAmCtxsN# zysR-4C=6qDB^J$b@&uFdMH|^h;wI!*YfZ)kpMg%yXH%5g1M@Is7`oLpA{rAnw=n>&=p9`y9I$s{PG9-}8$T%Tcf!|DZ1 z#~e&L6d<#|NwyUA3Yui6CG;ruX_iBCr|#3EiS8!Op}lm#9`}QXetM}Xf)fd-4@L?1 z-P032FVF|=>Cz*N9r{A<;U1cSon$5=A zI%$dg}K3ySOwNDHI*3`H3(~`vy=1mt1{W-4hl?pd3ix2Ez+2xjvI_7 zd{(m%=Waz+!b;>%2NI} zrfyktNHk3vKo4m)P}>N$H{qq?;yJB+!gs9$0Q$35TRX*Q#IxV{!gVl53^eu3J6=XF z$$JFsU^I~!!(83V&Q0Gid@3yw1xCCHUq@H2~R8RUS2>rTc()egZCj2FqLrKTrjpH&xIH#y5X?#HH z$Sg*%{ELu*{+Mo17}9g_qQNsV7k6!rIaZ3ZYOW9KDc2||0_g+rG6i8}38yPyYZdKv zywoIfx<)UqFqb}x7(luiU7~k^9)_S#gAu&x6&dJadpJKZwWYMLM^gl?1}c!$*0h>E z3n#PY5nPx-@x4g^kL)ewi38BMP7@fp1n18z%po|WXfQ>iy@LhB z1UU`eptB|1W49tiD;F@5?R1x%V@d`|iDD0z}%mEb7g@lka3v&D%vYCm)8 zCuA|40g#AVw?D_!zae=Jrn%HGH;Auwt^^ztEfw)>;gOltp}JMX5K6;I4@StM8XEH1WX5bg3z$z-f3gCT?63~ zxG|8W3~PGDhp@ytH~Ko_ReK^}O~)O@HGSHtw!D0Sf*XI%>%Zq>iE$23B7Vsw@e!7x zX48!=8AoW069PtA2iiCon&!bKRFWDK^->cuiM_k0%T5$zqVY2Cz#9mC16qEn2+}9k zuu|E0Y^7YH=_ZwGPphuxLi+nhRodKBX#^cjGq4ZtR(}V%7_qN| z%ga$^KoR*LMuoMX&ShxM^jN}YmUyE0Wf{ZV(<8wd4H-Jvq6&=w144|w1G0+v6$y_` zIqMBb7gb;1<`jVjww}ggRFh_A)Vfw1_bBuX-BdnH+i@PN?j%Bvt{JS=^qV5qo@uXe z69eKC;L^lAoYG&ZoR0b!K^mYCq9yFpG%c|~SQT-+ki(&sXHueK9Wyrg9A7U;Cu9b! z&SXyRnSN(0@YW3JRG(4GjMlV4D#qtP8w{EV{-er>AWTOZ!F@wBPR;U!&bgE5I%DN3 z%M8?g~ht%fx2Y_J{lM)WdnY;my$EAe*$F-_@&Y;OcBu(8pMQcTLj5+lLO zM=Znjhl3ar_UzWHB zSKy8E?}hKyuDypn9j2pTNR6&Uy=3(7vgso@#fT>XkeK;b5YH(WFO!mPB5#ZfWt4o} zpAUm%Lki;HQcPS0_4iR0iR=6X+u%S;?&aW&4tj;#&=b}I-8`F&er%D=zkx%RSPe9v z)6M?~uhsAK^anikcSKtKM%;@Lq;E-|lk$C|Mg_Rl0pu|J&Yr!%EXkN%xfb87jEB$=gZwh&<8VQ zA(B;emdEaNcqByNL2!ZpMvhz;cHu}fQeD=c836+>thyMO2y8A8%q7z2YHknHG(FHe|s zAD52fU}86jrY9QB^0Kk(G+tARpyZUWb6`~%40e)2s!v=Wg&pHq)EL)QyR(Kh2ReWi zlS|CPHTUM&zs&@z$e}yfurwHbJsWvosFfyUG0@!^tg@fQO2`(@0`K$3O{dx|IP~Pv z%<)Zz++;7Y9Ds*VCe%RA!$>H!9SC%p_`2H z$2USSeO;NU`l@}kaR*vryQSApB(R4gk}x|xH?PSGQ&h}=m{TOnS@rzq+lWh~SisEk z@}6c$cY2|Kv4W(dlG|05$PVJK$+&e8PyzvP_+YCl9YZ9yiIiSL`gG_r+7bto*e5J? zWzY~Vd=oAfFFGf#*tzL+JF{LUX`C$Jr+hNNP zlV)1bb~aUhAw(dDmboZ(>`}Jb{^F3AkrUed5#q+}5Y<@Wbfl0;z}`3i28#<`k?^z@ zygM&(w*gF}z1WzA6Ei*2Kbx@*AjWtyF%QCZL_fuxi8{x+0ba~9L{5;yxCf~Un<4?D zTU&5JX|F8G5+5-dqN;=uc+N=@>q(M2OUY)8kc;qhm`RUhO+YF_YQM~hrN`^|Krk=5 ziMuLUk?7^D3G++ckE=W`o>d~3KVtteeL(lq818cda7Lz9~@3TAYbt zpc1POHzH4xpif-WbR&q#&cySG7lqj8vgOPe0@B%|I%Ek=_4h*W1Q2MrKl` zD0bVYR{T@i&FM7V%@xm0lM<05!wUB5GP6;6>M+S%Id?4*9+XF%m=|>Czz|Fru+Lww zRvK_XBbhtypb&1PP3%eGKy24pow-+D0MfX@ELzv;%_JtY7Sq<_`@V`q*T;97_3`(a z_1ruE88u0lmdL7sE5z`aVh<|gmLrH==pZLFe`a>2Gwm5kYx#E}FK9d_5r3&}Kmh5g zQK?QWL~W48l5$SRnMBkmS|o}ryT_h_DnU<>R0dU)Oa_?OLq_Pefh1EHgO~tF_h(*6 zLeyHrphMKTDXT`XtRsoV)R9=)XYt7vnVexJ+k$m!)S;Kl1L{kpzeu8A+?#d3xh3*Q zw}+M?tzb4#W`V*{PiIRqVUbk10?P^@gWp~Pm1cw-*A>$<)Wkg0RF75wBxIq}#EoLm z^=%9!?f{hRKC*^xd+OAMy`bSkEF55td&q<*Gl%1P`lPz9LK``KU$GVqwL1VMkr%LR zT+5!$l#RN_RL{U51$w!^#q2(#5t1~_;B?juK_WT+qMOKgk?_%~H#@RelV1*Y!sr*q zFpK*mBZh2las?<>|G;kGK(T`=(uM#>WRmPfj02vTOjb$L(CR3#nabAyj75cAl_epj z18uH&!TKaK)VL!E*Gl@Q_APM!XeP_vgkUj13U-=etnC~m2i7|+;OPakntrMK$`JD^ zlEtob8FBoW2xOnkSbMJt!R zV$@7;BDIMzcIzB5Mtd(WR(o$Eh9IRwb%F#)Pp#Mt>Phx-e9(u883c<|5o&)*< zG7=tWTr2JhQq2PsH5a~VxDx1E5wETIPT$7sVQ$kUx=kC z>=N$igXY#u43>;z#UmH0PGT1W?$g^B&<;UvXg+$A6G$BhYoo!Tm75?|%~ecT-% zSNMzb6!*^eM)9TI>2BI%Y-U3J5xRo4z{WewO}%<0F}(rIEv0?a^hZZOUei~71&8by z$Mxvg2b()!V`oV2gYChETFb^h>vY}fpW?IL2ynxB94#pp$26Ok*Mk^&0WbasF+%WV z3VKWH?x8*^Lj+Rqe!X{(5fXRYVFnXI?ht}t%|mt*-s}ym43#zE@X`M<-m8DY(@$`! zj)G6rm?@{!KjXuBp8EalJ;X=AHGFJz{VR4-V1+%Ax=#*v(3C3YxF2|3_Er)|)L@nL z3i4=WFTDE}&2sHE7tO+Xox#okhv=N=b|@DA0`46vcvhU$dSgk(n_iM=6gIU8N5bNf zOo**2RUzdlkGdmIv1oo{jL(HyvWwiXL8hV-bO})Y zkU$X>GI*f&@ifH~H*`@WIAJcswN~q3B?M#U8q1u92wj$0=pj%4oTq=m6KAysl5qP0 zfMYLT{~mWbx3YluA(YJ&I6=7+6(**Aj2nj#1A9nBe^#~@%LfLRJ9DXcs@F1zt9-_w zh??YybT~jD>H(kq1D-fe#Yv(56;DZ@(ukx{e0}1Ue|pO$_9J6(K9#vG1o&r&6S! zUY^W9kUnCw*=_z`CKP9r8s$mQHpVL%>;$i5#0e@9waBPxo&-=gdBwR@o#^wt3V5oM z(KE?w2-OoTO*~bnqBlr4#!GaGY+VVUMW#5seVV81JW1S}aq%SDEsM)Fpg+WmUO~$@ z^#hi=!;_#xXpe!nWHzf?yd@HbVW(F-r@hG+$S{I?I0<(LF@45A$^+~kBBini$0yGe zN2Z>iI);BAWU^Bu(;sBsoGKoCVmymD`mZ2Euz{-m%gG{<&n;%L@69hJOUJC~Uypx~ JIa-7a`M)|b88-j` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbd04394e02a84bb9842944820f3f280fb0242a5 GIT binary patch literal 9757 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 Qjdy15P5!$RKeWvM1%cm%2mk;8 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/reports.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/reports.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39d30a57e94747b6aa525f42e1f48f374459049d GIT binary patch literal 16797 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 E0_dC0+yDRo literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/runner.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/runner.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4a0d4de318faa98c0bee243e4fa37557f3fb544 GIT binary patch literal 13674 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 NPQEbVR30qP{tpn-GB5xD literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e26640b6d47ded7022ce913f02f7e4cffce99a53 GIT binary patch literal 2907 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 J7p8Gt{vX#E&LRK+ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df6b59b9872c10f4ffee55d1aa49b0c0ac949f9c GIT binary patch literal 3724 zcmai1TW=f372eq!mlR1+7t3~`xLc)2%T|}5mxs7+npCzEr-f=nE{ZH#5G&4zTxz*X z%`9b!pn#%wAM?`JIDqo#|IuHuuX*ZUNP)KBnO(|~!k{Z~_M9`9J#)@?E(#lsnt|t8 z{O|A|EyMVln5&Ns<^%lHKQVBFv)CB2fH9&?&}Lw2+5&9_mZt644s6Uju`_f7SIgYE zGV}sZ)0Mb7tOYeqd-1}s9@I5mjT^&e&}4?NBSrXkl(4SQ0gE^ZF~3H+b`< z9V`obup&Ij79nf#rI+>#7OaXoB+HPj@Kvn3^^w7E@wJx*UwdH&Yuw&9+PD9On;31D z+rN)ghPU^JQ5L017=vy+`|RnX&hE#Lc7OL|@1s3PoFwH!fpB-z`;xzNKXeiot?uK2mXI|mrFdWBOZo_~W`yw7qs-(-7I*LY<#WxCRLEe}t z($Vw3iFygN^s_!b8_AoWJQ!y_t})X3Vu@CPbe<{IHUqCGvYt%Gqo5jco@zh0O}PNu z=8m3tZOB{TzM1a)5iV6b-F}#ay)@zB&cn1j9*QJ`Fcmw;;TB$7;&db=?n&a&8QG~b zlIcKnvy$Bs$;nO}9T3}3lb>(Dx1*v=Y>mS1QP>k|r&DaZ1jY8~EN_)ZT`IXu5ZJ(A zuo|codokG9w;f&nT0h?P&Wcl*`vqw+=^kg6T;6r4D&ci34zGm|KSa$O+tEKx& zWe=kyQvEF~R4xXC)mh7wKLph!_Uc3T{Qy6eVGu^ZxDlAb;_wEN7{VP%%oYxK2Rs5F zyelkG5#FGB7s-KpuPlxf0C`O;fM3A6I&Xlli-u@oP4gAwi@XKNBIb!(;>+M#oWa*C zavh)ElXoz7DFe+OqAy|dF_k3cv0W`pPd3dPQYX)j%ED}{q)##naFY}Ud5v-oMYrHmd&;I9frM%+ z5g*P)nAhpR5m~46OB|pG&<1n$Uz2&P#U{6}19f}0Uf#pfZ`p$Ep+krY=FArR#%s1G z--ZNf+)!lB^0uY#m^;WNLsf)>g(GH91qAV0!@q(c z$Sb%QRtKo|;kR=uGpFVY_R#pt`k&2n``np2nV5m($Ns+(d!x8fJ_GD-NI=$*J9Vh(ehU#puKUwp`Sc;IJ`J+%Nd_mi2yUpY_ z4AFMwkMWX}U-GI*kke(DiQJ{a9&yyAG(8HIN}Hopi5S^j@OGtes7_vVP*5-M2UKx! z3Qy>lO&<$tZ}#0x4sVqwchi^-MrG^_Qyv|jX)Qm*!HOc#un3#BWdoUYqnNyL>z*R(N9sy1 zA;-yfZg;~tZdY=*4E#0}mXzxYk5bwQYO0@(V@{nwZa?pfWEO}N^*4QSr}PC**QGm7 zdxfPw!(v4#P^Sp1&Ro;8Jk~VL|9WoCth33F=DXlqCjoVHEAVA-5)LWoidx0ZBS&t4 zH{}Kb4eu|EFHH(pZMD2|BIH4e)`Y6~1bti4N#6I%grJk5#x`n`PC(SoT&c77W>+q< z>l?@vRq7b4%PO@7tWCDQ$`|+O|&b@&}~q1`Rqwe?`=F z^>x)efqa%K-=44EwaVLG%8~n%a40&Ryw;&`8^=V~JDuZk7#A~=YP0+)4b*f?s@IZY zLpm4&`w8M&{(|@_4e!xFPDFE%N%??SD(P1g)~Zu;qf07nRrr_6K&`2^>>4xwQ@_)? zv+U^E_U+uw&M5a4-=vczmD|8A92Qi{MzK(*S#sNDmq*G6cdrY#L)oK$FX&LJ2SI%{ zKMbQp-!fR7i3;2+eYxP)Osd--G_)rX7gAECEBr$}X5dk#jE19FcW1@zDRs>=LGC>$ f%>F(BSNRy~z*pV$Xe|Cr8@z3`R&BQIwkrPv%C4i* literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/store.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/store.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae7bf3bccd6386f20d4e0e00e11245c718c607ea GIT binary patch literal 4412 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 Lg)P4o?yUa@+{>Xh literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c55e161f3b1aa8980907c0d7629ee83ce2f3d06 GIT binary patch literal 40859 zcmb`w3wT`Db>BPh84LzP5QO*=MUAKzArqh^%d$mFvLrtAG(?k-C66SJK%4^-ATR@- zGa$v$Aa+cpR_rKooWylv*##Xtj-1@qNz=4V>ojq0eVg1g?afVZoN3aV#7P_ZeogNy z=`HO@{r%TIX9fd+PHw-7!kK;c*?XV;SbOcY{%h@hriX@#3I3Z}`1W+`>xslSb+h)r z0yhuv7oJZi5?-R3@RD9?Az4k@Z>pNI-*h!?znN;rezVoA{pPAUe$xy2#X_}^RNlav{WGC032&=z zvv7&-wEIdMZcXhXgw-LU! zdaZ?13wx@2tZmm-ue0C1)xGw6eYL{x=t5=jhUyKL_8P+bs{1Uwec{H%o2oZic*nxc zi?>v7vGC4?TNn3N_gi?^!r0<%)!Qt*d*N-12dW1wd@bSe>bQmX5T2+`Sok`^2df7y zym#UD#XG8ZSor#dI~U(xeY=G#gzu`}W#JnZ-m!Rh^==FABmBV5X~)`j~Q-&1`L;r-PIys_$o-fh)~ z9!_{~^A5b6@DBLN`NNme)g%5LUdkJPE#*yk2Vc%qkNV@>-_HFV-ksbZ^T+*%?f&iF zUEDw7AN1bg-Ti9PzsKL@KdgVV>0Jr$o#c3zchAe2OUde^{+{{Ac+(;8-Ms1Dmr}$% z?%zhtz21Gq+-EUQQ1U(A{p5R(_W&io*Gil8ukjzIya!*)cn^7piGNb*{7GtYgt(*L zG2)KdahN z;s5E%=|;n=gpK83hQy7ave0OpRsQLwJ85im8&^NkE!^fqc6_*_J&96vyHxp6aJFwRJbq`UYPT& zJ=t0Hvr^TLFD#!veNopr6`ecWsL#$>g^#{C*F4ONlvmv~x45J%x#QD8XdRV#wCOJr zlbLj>O4Fyp#=>&bUvj-Rc#07V{DtVY_|SCdQyGSqOhbno3k&{Cvl~&IB6asJ|HK@l zVRg)(WVC7cX^mAP@rUmjD|B+R({l^H*U2qShatcD@a){u5`iKe-O1GM?M$p>n<*F5&R$9l0{Qj5W?TK%vQki5X zIh^GFisc$hxsJ5))fmM8r3ei0XXE=APZ(Phd>O;ctLYWy9b*ks^YUU=UeO!W+_Tt{ zH{_MQE#5G3*(h$S#f^B|*2Il^*ECa>d;6N)JG_Brn%JG*u5Rw#-nHJIX2!eD+v{E5 zeNV-^f%ojAIt z#J`>RyG-F16(z?@+fc??%MBmOqtrA35${Qxakp{~kP@l3?{}hVm2hS?`EM zD(~S}nFU7kk4&~k;tAXnI;sOfEZ9_`e~AlPP_xssq-~|f&$kA~#~qXPJ6T8NHf!=x zWNX`8eJ1c(Mti(?g2|L~OMv5HVuZ%`T7%=`^~U%af7*is6hlN4zt#+v{bR6$M$N^y z%HCWkk){&emn>7bytp_WTF~;*@bjYW!R)KR#|#y0y*oH&jhE=q?(qJxua7 z^{yOoOM?Ws+i8X41exYVIH)mj@osu0fP1Uzok7=ou;768+kU3^&_Cgs#)S&fQ!s5J zVkN3xWe!28G96aJC4a^(qNgraEZd>V{@F1TAP!XaA03k0u zI}ltNGhu_!aTlswm}{P?G?tr7%S~dZ$B;01N1Z($ODvRf>*TGKndy4vlwVnn7UlqH$BDQLNSqWO zm2#+Z>+I26$0o+I!IbF1h(f*E$uGick%t|LjBI>SYgaGMtwr5BbcUl^)#6&UcE&^z z$Hd*wfZ8b9o2?GfcrLUcF^$hgm6A+vx)Ns=>&qn%W}LBw3$ujcC0&AIb_ z>+bHWD@zD8O-W)tLre(=mGhuwWF@cr2VItf^#=9H1<$F!*LV8NnZ_JSi>qDPwSRnO zdI=c_!d9!J8wN}ZSSJ@YJqXKw5mh_sg~sVlnguSH=W**KVtO-ZEQA#if0P#u$TA(| zhdk8tv?iBIuU8uNilb?pB;mx`>31vT-r7$qzFN_nhD>DXVAd%bKi!PGyK%2kzqMJJ zSw^0$U+f>U-V8^GBn2Imc74+;8V)kkSZFZK?Po0ANnfO=)C)FJt(z>KYDe0kn{So< zq@vFc+MrIfhElx82cLRk zs+v=s>T{8}(8*c}Yh)1wp;8H?+t&4O9GiA)00Y(K5~UM_utOtuX^|0)9Pty zP2TmXH{44S$_>OhOg_5OyGL^FuZtw5ZhFGs>DKhGf`E3tp6cWnQFLM_BCi*rBqFEz z3m@Zxh@5C9=Tnzb-!H)+y_$Iik?L~Fi3#($@HUAH^ZDSIX^bf^{c-_8D=}Y~A6PAV znYN`h5h6R;8IDQbEqsuHWVZM}y;So0HvYnWT)gBeFGnU%UP@g~uB2W`tRjiGk;N}3 zyE@aAWTjPp)@H72)$(LGCRc!0ZK_t+>QB|f%d{qMyeHQ!qcxO ztEh9*Yc3TpIScM8C=uKPS$QZnV`{VR!c0J zOdg>~z%{9$NwLBA=whu|QK*mJyLJC2k3s{Jlci)iIh-nGf>(%}SxXigm9FOWd2SeB z^rjVqlh34981|KPJ9#AW{Nzdotu}Q&5gce|R#TS~y7w|?Q^6rGyOKV)le-*uk9hg$ zuE5>GR28r!?btCM-tpNrxZt2JDIb?%+v%kJTU@e9oREg3q-fO=SA?T1nQ6gpZ z)DpTD0rNBSU|)bR^t6QwhHgn=OjQS>n4&)jK2{Pg><`VC=eH;X2If)?d)ew%e*`Q{ zxL6Jp81)hqc$=pl9Ow)^?d0PVHu0xdsQpPHkF4A1E;ACCgh-u%wklnjbM4yM6^4+h zUw9$vl_+H`@Mju<=X)=_5FuQL=8%O|bOn4QgrOWVNGk%rg34?#=Yw@!j2~B~PccFU zrA1{r46IvDd$LjYD*|Zeyk1%Eo@@ZpPV)nxTw970`(Jp0Nl+7elo;^B3u9{whk_RT z_R_@(;i(hGLo2gkoVfR{V<4jyxFz5$IJhe+h7Kc$Jc8Au7JS!tk0pa=pn^%*q<&4v zm(!44QzZPF{;{eR)wV)sXY`6J30= zOCoR62j)=Z0&w}?QEJ@n{{k7cvy822Miw7MG_3lJg8hJ zHFwHM0;r2NEgZV_MI{^2MMz}OP=?gf#ZE>fsRaRzF8C?MUSncvt=WLayfDTPg5ApY z_jKv8uPzS@v{quJbU0@rl_{2tP3?Irmxfj8=>}GnC-MRQ!ZH-tWmP}xryW!;qa~&( zSPx)(PG}ZpDaUXG;1Ae6*p`{xio^_B%%E4Ym{K4TY4v^H{oLQ=-#mYdwedZq0be8TR)0TbKSY|t z-VyG{DCagS=O}TYZN$Be(hu)TH+aWjyLV#`AH1f-)W_J*G?U4|d0D(tFbS z|8{%paZZ9I=z3=7s zG4CVZC4L{LS6`;w@ALi^sh)`PeAIh|yZ1(S-|u~lyGi=sUVnmjYD_-v{Qz-KcE{*d z?+3X*Zln4`JpY9E!`#0wD&dpfk8pP)y8BV@$GAJ`PjRobpYr}T_fJJnKkfZEckhqx ze!}|VPoi*F9R5e#(6+<^S_7hOqQ(l%HRZ8}%bvqEGpB3T!a2LA0G*ysu5OX2d>I*_Hq6>RJinFDh~?kb97Si3Q6hLYtik@ zau5IjxeA)9V+vSx;LLOgN`Ds3A_q9yY+gi5_M4sEvN;;4RC7{Gh}>)Hy4Sg`=K1dZ zzO}lSR3cJm+h&ow)eX{PqGD>&uw3Ok*P`IjD(_+qd=?e(qVILV;m(fQS^wgx1`+_c z6MDE6x#+im{wIA`(MF%w8 z@n}vG?-h(3ndKyizFIZ(V0le7 zHg-qP7J2o0NWrkd(DE~?ZgIn+xM*-7jDM<1XoNQF5KN`r<=~6c4jMfqm{HOMI;+^NWOlQevh~h^}6TkJs@<_dLVf=6SX+_ z-&EixW8WE8^<~czUZfS^QtuV58#WNa`qWhTlPcPr3)VNd%P9Ad1fc@h^R?6}EKex6 z=B-BOJfkv}0b6`A_%z8V@NFtkSQ!18hVR%~7x#?4t)G57IhH~A)JIiwo5?7RtsNWc z?oz=fq{Mb|ZsFW04r#Deig{anjDY%5PDrZ&6qjkmuEe#1X@v3 zh)_n?)$C>1mQvKx%^b1$b_OsbOQ^6qV9yjP;@FgSFuk747uzZP$ueGc4!5T-B!4i5 z?{kw^Qil$i+f#5}t;o(UEQe>N#s-~)QxLixNijXMBAJ_!97HZ z2yqGfxW-FZLL#|6Im!~Qg}lTh!)9>PhSA%kfZ*fg1!c`G1^(>Zi@5a32VmofYBake zDzt+>h6)+6TL>|(HV!r+?H<8b29tIwIHLRcv_=D=Hy(-Z)E!GrzVYT;Z@mStwT|v~ zSMaB*StR7VK{)spmrm|1UKw851RWxIro<{bdi9R!%Y1d- z5Oc=>5j$?V3D9d;QF?u2Hsc%R=!Ljr39tG)s;;p~stTLDe#7|nr#}X&_(wEOj$p_q zO{819H+t&lCX{TW*BwD|6KZdBl39XMv2K>223XlCN55;wLS}_EFX|r%8?`T}CC15; zt?e6HVN`ha?u`YL)uc#DI*H@%QB72<&A71D=u6-A#Wv63U7C!WbP};430+HsWnUnzsesbH;zd;MD|7y0#ts92*U$|KUb(r z=+wvroL;rob?HTte_1Un5D+a(9^7OVb<~=?NH+uwPgOyfqb@qG&jAGd2yNs$IY~LN4dA=<>T|66XYGeL-EuYhLftfAXTH zS;vBh7#drVj3-}74o%`=n_g-xIa2vGJ$_vmt*VYYMoykR%K5)|R4r;Lat@vy;S~$( znI)*W_4rprRK_3&9BVA5q-L~sZkV%ChOw-a<0XLlxh61(RY|Lt<2IT&@qbwY-HCJ) zH%ysCv{oZz3&v=tXVZ{RrZ;WeL{G1oAAd>C7~@ws_|LlZ-rGM@Is%JnmqBg9nInKN0 zf&QDScf>k34nvd(j?q>bR?r5qG0mfH*YD(}$LQD0*7a5ONEmaXUg7<R8sCS-!Ik{w;c$63qy$mbAXFgA9!fJ_0Dbn&NHlK2i8g1_QYE%zr_KO*M$gF6yH z)^DrHG4mZoh}eYr)?hm>H6nK&2S(7MpnnzSrMAFmZdkK#A)p23Qd028ynaosEzFVfUzxQ6_Oz+TG&Ux@`PGahOBe8u1 z-29(U1#CZTC$asSJH{#^dDo{YHQCyB{E4R?esq%M%y|O&-k}PUMyR*LgT7CpwKO)M zUNK(L&n7yt8-=SOs!Ckqx72P&XGr9k*4WkByCEmKt0wKn){0k(+gWR6P}M@CcFJMQ zoWKxeZzCRQ*@o01TCTKp_1+o{S8w~%|E&r~?0DbRD%(pA)rr%K>bl%}x;K_~tlYeK%v72_DgekTkmXS=?4r=yJioRXv8f zHf~XN@E?hDlKbx|^k-aRwrEp?4Yr#zjhK!DZSMoN#wpgY`M*&27#(Nj3?s!;TGrL+ zczprEk5Pv{X;r0l`)4~{Z?+Sdgt-tIKIit!m?Vxb!*l7C%u048hm4wQW|7q22{2mF9=q zS(BUdPVo6evMv?6jBO`1KWuAV0=o`jQN#R5L=VMw;SDQXgOAvo1l^V9ui2SsZubUEwl5;v4>otmA7rJ}-02OrOYOl+>6M{&$#MQH+qs?RRA_2SYc+fg!6KTqh|NSG_F4tZFr=J(8BH@|ngc}eotU6k=V_VyvtUVk~^ z4eb!qFTcvHtjnWC`Qp;mC%LtK5?4U6)<#ro69U1UE{<3b0Z#j_RTO1|>!9k<_p^YbQex3ZP}FRhHVGSA)|K5xSJ zShAX#xj0=9{trqW+lTwFd4bgeqTGwLjniDdoJRN4s)9I6Cl5Mi+p+d5>Q-F@!8wfd zYYP3TF8_iHUg)0Z)}zG%(-&&mt_gDm zxT_=XILBx9rpzra(pCXh!Jp{z=en>*KEZw&q_PXs7x7H-IyYE3vFV0AeO=G#?qdGW zmGh6djFsIwsP0x*E(QNyk4%H+Z zivI~y;m_iCVpiGC-Dq-@Q0}c%D)naStyH!E^THKU`6y*Ozp%5n3Yn5&Yna2Xx?pXn z-PHk(3N?nnS)7)WVfnGdM*tYOXG$t|K7AHvtl0eUPe|*OKBJnl(PG#9x)}}v&z}pO zaZ!@@d(=tdrwJO`+Ph&1>r2dpvUOmCN-;owE!(!p{t^MzG%Tv_gpybXlEE*k4en}2 zyko94D=9XlrB|~cx3th*FN^)8>jj>$OWy?k{AwXW3==Tad8T^NSj`iJ1}`UI(Iu&aZu2vz0+sY>FXnEfFu-=bb)|KdR`7g zXwPfhXdTiRxeCYL1QNz4V76Z)Um#!2%@}#0&N!zg@CR_vx^nMu#TxCN&yJLAG zs%LOJwKY*%cHLl-yNt%lyvx&ULeH+|%tUkMBxoDI)w#l&ZKlq>Zcr%kptf9qkYShoGhf1WE6u@Q@ zhMI-$s=8V)W1rTMCcjom<4_+Y04R}=fk(iM^fGB$bv&$C;)Fv%+HtJ;#VURJNP@)- z_$EVlI?nV?4AwPGl3Y+FSH3H~W+#IQ-eU4$-*{=PHC}SQ2!%ABhO$6~lCdEo0E++N z)HzwDMe=m9*%idRUr%*X6SoJyPjKCI=oIhO)>igJEZu*JSNuODNwn_SKv0qX8+DQs zvwAy?dSx(-Cf-Wybz3fw;$)8uBpf(_MdRElyE03CYpqJf!F+Di$$lxWk9Nk=i6ZcHYMkyuk~V|8i6M%!7~ zC~H{oN$f6ptS{1F^G*B#=Zmahc@uhyXp^bn=e>b?%0ok9-%`Gv4mxPDg8*op3V>Pm zUwcKK4$P*uyXV>9TRbndv(bGfxWfHVI}_cj4!I2YSlbb^8Q?6;*iJ_D7Py{*z3wFL;BHs8gVMk2whOZW!Mw2IMK;dIp+jKbm| zsAdvi-BKY?!+aZBLoCNPdst$ys@)v6hTUxsf{7DEXW6vMErjd+qwRDmod0o6{7 z4X@`$VnhK4XC;geb@sTOp0&eIKJmoS!&4k1#NN-7Pd@R~)T2*MI$Hm#a{PfVM*oj1 zWPI)~6#7$L3^*$|*-q5hNj8K3Qx8lt$;7S6B0)a1h1#xb$S!zPIUcwvDRu{KT6QVD!mYIZX$lJx#(^fv> z23N~Xw1N4p&5?N=WaqbCPTP~j{54+bY$p6Vb=y9_V}57&^(N%1Fb7%iw%{A&E;om$ z&#p@tJ6*2T-R*P)h-{y~c3v9-St91I^R{3VW*V>VmE04-j>D6|f1z7i+oZEqqV8jp z6mG8J6kw#Nur+|UXH#Myw9lsL%5gi;rqW@ZTe?`oZ%)3oPB;JC6!wPJsaEM(Jsp2( zh5w&#U3Z}3*fw_xx*t1`ilFH_)q?Z_|I}Q)mC;+61GWJF3Z;!*?^tW_5HThuRnX zeZ>#f;s=}>W8n7biw-)mx?WOIFDqfuf6-?fyv}^ESruGEPj*+1h=`rXU+^o+us3qs zi5^M%3Tv}LW3grjn8l(cN7(_9P$!d*+j<`-+Tpfi8P-k5&bDZ+j0V>F>MNR_cau>Y ziG()j5E*T{l!%^5?gT|(@p>zl(WEVaSCs!Ml^rorz99I2qsPuBFL^x`JWRDVPDIHs z-PKI%sxvKNRGL5u2TN?Sr8?LLnMTO}IrfF|v}{;2(?z6v?v`@}P1Oj1(8gem5`dD4 z88(*2$|9?#s!|)-|%a2Ncx zE??H=XLNZ<7n^N11HYHs>ZpXjT4YlZDklo(z#F#3VksSspJ?k`G$q4$QrO({ROaK1 zV8HQ^Oh8wXO_P54EjY^a-_i>$k({W)$R zV#Yb*#m*>z;07<&*yw;W-~-ZtImKxhNk%dF2|Wj7keJ7L0c;b`NHm`>GKR*x$yHLC zDBlp`OwOnzO~JpY_7UD;t2g~HB8jOF2?YjZA&vQ_+RvP zW1UhcdOk-Qbe+i3mAxctg=S56a?D?nyi;s8nvwfX?1UjE>G+Q+0N3fZp9Z?tzg~Or zjTFlCXCjOUWn%7^V0jK*D#IRS2hAYWd^eP7Ly3JvGDMXHRGJ_%PsJFNo-}{HdzV7C zuX1))+RMBQOOVzky$$BpK2G} zGna>QK8-~x2Vo8vSq{t(nFjDD=YAtfmj{3W-7;H~YLx#>qCFr>uceRsA&(0G8_jZC z=a5OHDz>p31$Q>LtPZy!U-Sj6L+!!fH`;@%TQ4Vg@7@*JYSagVt0PGJ1Lz;6%U0?q zlII>e_Z#OvVgz4`Wc&Bg?VIYeAp;XuxS-AMl;5&#`OqAOJMT)oU(E)AU>8Cr_(PI8 zv7gdx)ASb=^Gz;DE}UEoOkk?PI>H;cSG&SrTng?_rAg1Rwu#oeHm)z55dIn+H!h-; zyID-vbCdKT-rY*=AG?ymVJppvb*;=xD;SQpt=AnOaR6c*-PoG$=Hb^=cjKUHMRmXw z@iX|PIMqR_c<^3pllZR*PaQbC4&378B7i#%%)`S)uE&VD_$0Cgg%kIxN}J{-o37g@dHNmA+jsG% zM5fFP9YKuQ$^6w>HTvg5qv#t&p2*yi;@K!EWj2xCu?frHjQwDG8dMZ$c_?1m{CrW%h`FZ>1;FX^IN~(fe>Xpb9*$R}J;Q5qUi*-KfMsLYp*yHN zTRAHvIX@Eo^qO2FYjP!4w{?*qz_X82cb1D~egdBsJDNH{NP5CHe#b_uMM1a`$kHRJ zYsu3|J^6UGGiCpyo;vy9!$)i8GE^Oze6r?}M(XU* zBf-;pu`To_Nyo%ISf&h^WZ&6Et>pW%ahqE3FDW?A#qo1XouTMh zxyVr}+PrUTiZ%SR$}kcq(zrve==STEm>4FCgm1UJ?1eZ&Dbk6I4mT+oAZ{f3?Mz+^ zi)3u%5eQT=xk8%|X1~PkO6HKu$;m#p#+cY%6r;UE+(;nNnDU#9eo=qzKJCU@ERLrj zN%m_`dy;mxQ|>m~9ERCd;*JljslO!*^kQPyrCuI8DKG4du(yPfkQf<{+Ga3naGLtx zqwXrJyO_)FAKjL^y6?t%dJrmrpPPw+@1U5z#aMih7r4cEgn&4{bpKa{4zM{EDJhMV z#3@L#%k>$#^pB-|Hk8%sO)+I*cA;^yfqRPnd)h8i2c2|k-+!;LRJSc*aUgcp3>l0%``vIVyLL)Nx zQX&JmKPB|;NhN6+kqO49+!{YvmzY#%>*LV|)kot&Xg#nW=W1m@(YG>t! z^g3t+V4`;*-k3Pw2!)Rot$RB<^)r~kQTmK?=@S^yTYIav@z+kL3Z^s=T@D!|&eVBR zUo{)I$2A@T+XXPoejq;pnb*ql4q!P;y}1c3U6t?kod7IoOskXzEj-2Lqlu5=G_0w> zv1OphtUf58NT9h_G)2n}3Mio*s?IKNS=UX>v&|C*Au)bmRICY54-mr0k@Fg|eLL=8GT?nY=wuhqQuK2g zOJi#`l0sJ`inPWz%(6b;xSj-Pr362zm;RwH5KiJs@;Q*Rd&euS;-X5^njnrE5ACxFBa@4-apry;dSDdxvMct}7f&7A=Rb#|G!FvZK zY3(*Y(d{g4oB49YRg;|9$Bik5Rc9SH)`$jRrWnFCZn%J!)+>2YA+j$Ip`n>ZUS&^lC7`i;^c#I&EHR;RTiuz>*b~7(-3;k6t!X)(u8$0xlX)e0RiPn)U0Lu!_BEA_(#^@AhHlu5-YU=XC@O}uf|W`hGt zqVMYtP2(=>znvSjP06a7&gL(V&bAZS+&o9|g&cu<#K<0D8|LajZOJ<&=3K}7^Q&t0 zALzm-Pn;$a`-GZ4@qixvzAo!VTmq?+{K89WpaFXfvSu8O+qlWMm%eW`liDr}Pc$0d zMjhZd(hFryy5$uVczMH%Q(=m$Xx$+$t+mbQ_x?5FOGW2q&#`%>zZ^DsN zYOo#Tok5Vxtd>ks_5chDQqtQ4uyPdZ;0mchkEB?WuCTfVypSW2R)=*|Ho(K6fCt)D zYL}#JuM}`uEx1sW(?A6mz6{--`vn*Fwx~o~fw3`|2gEpiCvhwq4~GrYmEHfe0_o3| zfI0!_h6TVheAvLMI#rdS!vY+!zeLUp;xH$)$1aO)d=&2LpK)ZacX-~+pzwfmA5K~cgwgdli7Eo1f>7X3}K5H?VN z<9%n=QE8yl6uu<#)QjLiPj6TTVlg`ws(UMe;HK#%uBwfUzuTdgpt^3xf z0|aQ)P0oGBgSc(;6A}Q><6bWXX{9fxBLGB@ake%5(ux4!_%JoBI1f>X**}dzap1l| zyey8^@Z{M}^898k|FT+cJR_BRYiz(B8fKcF5C^y3t~1oApA1$2@w-H#&Op4XgpSta zs#+a!kVEa%MeTdGL)X>_zqTCjx@z}*R<&~Cmew@0O@jUnqlcv4Du4;*-fSKD?=eoT zo$;W?2WoR>xqY;yHN5_b8SAEwK}!-losBq6sBd7^IT~CWSV7zjY?&iT8PXjwh9z=O z1s@>mq#KgkRbD^yG_w3fN^&68UH?BC+oibHyRhW({R~j5GtK5wcKBHlzXiIH+5^$*P9Ccye>u|HfcYvTbperb^n!a zzov@`Wi>TVM8sjC@?2Yru&HWEUy_JlY#tcZ?slPwQB`+GftsC^%uIuzqOC-o)P*4U zeii?F3w%a$XVg(vnTTyQ!V2)aQB&G-}027oc3L(UWoXZw``!gh^ zi)F+z&4#dexb0+kJ?WGkYrj$Kojm$2HC+-?j%{M2Z*Jzoh-lU8;b=;&S){tYU%X#q z7c0dY0%P*7$A4J$&}%!_m;-Zn%%T#1Qx$RYJR^Y1eCw7CjBC@38@gT{cbGr32C3`i z`;9Q7W6mVX8ugyTND&*NIUe6L33A}uz48vLMcMZ1nwMB~%rmKrq>JTB|TW`R6^sTEj!ZF*7ap%6F(fsKSjl3j%bj?{eK z&IEr(E7=Z=IQ0>a;h4vaiN2-ditz;#(d+1SbT55IN!nK;Ug^%=ZIb{qZM7R5Fzg^cb21!oQJg3Zz=NB%TLS|f9fQ1VqVgq`Xp3KOoW80!EqRWS z+cH#FlvSViF`$Pc2K3Om{mQ+X6B~*c58icRG&Uv+#&|Ma#v@eNF`oU_=b*sW=kw~4 zA5|apxK3dW8r$p39{=gGp5Vte={)sn7J`cyjC_V9jyV<+F__8*@?9r_^il5?>eyx2 z0*XbwFqK45R$F49#d#fpf_f}t4Sj|y0P3DYX zTJ05VWO47Ml=3BcIpx@3TJDx{KV_7ecSS&J92VHmVI^TBz!);Wg?JqT6UD`|&`5y( zO$1MHooeMc`~;2G#Hd#0+GlTHeB(RvfYBVa<2;Z|%nUm0;M7sYPmndZk4tCgspUC7 zgAj8jl$s`c+zs49 zm@HZyj*N=kbe$}x&tP7^mE_f;-m0_6n_+b@I!6+jcF7vS?gbs6rJ7U+g=n(}Oec#P zi>?mZhOOwV(L!7cj;4<4!yd72j_Qa@r>#LX@Ns*^I^HrzeMU4r4)H#%zd9@ujmGpD z=_)e5kMb+cr5xSq@K1Ro>Yu{9Q=7n$ef``wfus*g0)(X{!r$itZUpq_CQnl=gXt6j zXp*5zKskI(r7zwAYpD(K*fmI>-Vm>ke3r~spB5VB6)$16x6t4vJGICwT}pXFujN*< z^1V@wn3B8kg~_gF$Vuf%aOjkGC&;goyk=H z!U!37?NK_pkNs-#6ALl9|BPq|W0qzeX!J>C9+byt7j=+1lvOCLkTsl~v$3d!mvW{f7h3(AZE3ciFIfCiBXpq1F!>A* z!AbIAg{?^SYNxa|vkq?xgNjlZ24VBp^sAEfO7vyjqicKGVGQyU2CUq;LFcdS>8Q*N zU9CxLBs)c5ldd*o;VNC7n%A1A$U5jv*OKvNb%6RY_`E{a zdt%XX?^!OR$)D8oFX-}JI?c3s?@4u9i9#jZx=v$1(0@yZx$^otOr0f9LhCST>gzi! z*xNL!WPt|1H5*JSbdU{p-CYO{Kg+Hi7jIOJ?l(p)ovCu*r27Knp~@R-Lu>2J zVddGIz2|WtK6Eo@y2uH6O_cnK*Uo_@IpNUl&yAsuCGAYN)6d zLuW$k2@^#JY^{ozzo^Sk>GHE&sw2HG$JHKWKhcwuCtW9gQsroVI>pi0MGw4ekoTSH zB+VDIsHS!n`B&IEqAwYPo%2_)ue3RQOh+B4^EPoX*~muv=$|+BPTGBl8w5R8b`EOY zO$fL<3Z+0XqBFUaB0LZu2oT;Kg-kJ-O~o)yCirDa3;vldH>yJty7ZW1mqXf~;I>+; zZcbqLmck0kw~4$jp55CEj~Q(1a=mxdJdzV%li2{>P;{?}C%91q*m4f`bRLBx@{R$T zK^MIfNJg=@Gd1m(?Rj_O;+wATl$OeqBkGp$F`O{4K_&(9sAvD{3Z3+2rr3uO4^1K+YPoER<~IZ zeS{#&R!<$~FBD}uv!NDhvwH5+0xchcW(p`v`>&Gr%if9z!z(v4Y&&5&*T%(~ts*ST zrq^(f6Ld@(1J>3~29GX29Ps$$lTT09rao{S6h(YS9~Q;9V1)B?>W=qP86&0$`l}AD z9dy2wHj7WJXCE6_*L6m7KctRwCV458;x)E!coXvKKP9fOd#H#>!qz<%ZbU?qVc_qa zy`v3q2IAuZh?51gU@SfT;{eCqvmYOlT4U!^nnvNiNUKk_p5Y_&Y}x8E9@BW%pNgr& zCnHf^`^uW3CD7MOEkBBixnS`Pd-ts^^oRqMefQmWU*(QFqi^dTsNCtcQtH#0m1ja8 z?&~|=lEUPlXCKrwC5#g-c}?%c%Y9b!eY}#lEKZ;GE6Yn2v=*y_)FBmY4<)%C;I7+x z)zW`)+P+K1_oUAIZ1}8Ph`uX&sMt+c;lF45?i90sU$+%}6!fKicHYmvLv|eK%GiNT z(#K7;?Emho;8yP}(%?8GJqZUgaZQ^?I=9rE?j3<`A_q{Pirnp9Y~ByC*9%xsRn=hq^)* z*~uxx>#tb$szkojrb1fgR`5w|ckZG_dzJ$R~=ncIAk*pG&)x`j>b#(EIB>qK>h2hO?vt*07 zkRik;9TKw}k+7U<-LY|e{+h|#H&9ZKG+%Am`xXJb?xJOnfzU}6x}$@+!|;NoTl*;w z$&&4XAG&Vlc!Ny+!@xOuHj8%^F06n$NORLu4KR10>RYH%O?6 zf(>90#TIv%q?76B>_ls%OBNCD!0$lP-ISTpTU(5HVw;2S2_p=ft1&2PipJzk!6|F} z7Lt2Xt#@9H;-U&&ZCM#h%Gc@RybJ$S1*MveC6A50u>QM?yLsTguPBh;zIhbqkG?fycsrUrgnAy1bfU4$9hF@eE$g|J>Zb)Zvz+I?N}z0nD}1wGlNG)0A^ z5(k3{35*NM3f-j84GP_;%a|^EbomoKxJ99Zy46Lm`}7W3c>-bCfizslRZYY<-B7$Hkd)v^UHmN&5w0Q3-m3T^5^KwT zC@r&)+Iekxn5}3T{?cpyvYF&|yC2Ss42}+zv%Ayf^zPiqHN5aUoCFB=WsIJA6%fYdNn_xiDND+0Q=z>+E@PbnEcw=spfmd#k(~ z-=X4Yx|}Hw7q%C2g;F_RzN=gwEs%eR{9Cx*Tt2!xKU^vg+_q~pzxU13HGAK*ug;+s znjj`{tPg~ie4{_(z@iH{uG|b{EkYxUb*fZwUS0J?60ov&i(2Dk((5~6bKla;<`4C; zlr*#sQBXD%XJfuzA=9Mw?)X2g1j=J|d5wVVky3%gHO*dLIe=)0DO@6t*r&d-`rJY{?vrSkmgZfybDz>ut~BzlP4_Xq$%~$yTfos$ zAN97k$-@Nxoojvgn4R8N;T!BIBWzP~=Ai=>4hjYFnrq1}j3P2OTk#rxSicp?U$>}H z{U=vOlDj+XE3DWji^;?mdZ)le<%N}*>8KDVXq!WZ8A&HwY0k-V?2YfoBl~;74tmh_ zxfnbkRe%3?l5^EUTynMK>h4Y#Tr(x^Qz5$j*{`mDL{Vq6+nbO}iy&p1LUj9{kq9!cx5;w!Dv zMD*!MVCNKt;G)Kmq1C4#5LTU;B>wvJghA9CFEoxta+2lpvRZ4{v~~W6blcSB=V@c` zVTII}u|GhshkjCzzMzXo%8s)QIzvlaS#84D6tjzs%4bPlU@>qGuo*xtBxVi(H`|@o zQ9*W$iPAA?e^up->9PUB8YEV$iVcUvFr3^GkieV2qN!(yT{7G)B#zo*OsUqKuFSD;nx-0L&SYeo&1nxgCr@EumJs(EFj*cyrq_VU z^1wi^$#H;lebczMW8M~`tuPiH>~ZFNk28m%*xc#FWgV$O?e}6#FJQnPSRJ5toO;DM z$OWB(Y-zvV9?&$#x0Vj-6wt$Dr$J1n1F(DnKv#>-1y;H_4vaROU*c)-)4FI@1vls- zrsXuO&nfh#F2b6e_w!({>pMx0k907to9hAX|4GSBHh+NJeCtJt*6&sgUME#WL+*qo zi9}@(%{@#<47OPv29x73m>h?}ln|f84m*ufNbT8E@e^3Cb%fCA1P ziuqMlNj2!?Be&O{lR9#mwnDLdnYQwqO1f?uNL+nbeIkZpJ5Xu66Jq}r5q z$GCAQB&2e$^Um;axfDUZsaaelwRQov`iW7Z3gShU0eM5SPXa-GgETE2F>NEJl zeU@Qu0!qVx0XaBsOK!#PS#(b7XW46@FJ_JvTJMXNL;-RU%3<)4fr8VOGs}z9f;T>H`H8Kw>zC>1%mk3RKJxDEBmh`$DpK6!JBUKJxo5< zKA@}TFJM_}kPE6&+D^(_|2c)=$B93+mS{?>V;0*-FKJ5ApV9lr2uLFZV@U;f$+-ZX zP=~%FH{djgmda_ENdN%Yp%=+0q6|{y!uySR+OF6pT4^{LnKcTqMfnA}GN7m6yp;ia zjLYDVbLJ0h@gvv+NJnRLy2V(&bn6?BN7d3t=RtoulO35oqDKE$MG`@?tvl=exW{Ip!c{*KK4k{8q z#7F3WwT6M_fWG((I^Ae2gn@>vPkZl&i1U(w~TYV`LB z^}Zw$pIa9EmJ-Nl9E>W#Q2eP8JH){BsOm^x9G_P9d{YRk+qw_t8WR_0oD?B>q`Kxi z%!{C<6YR;&!8+-=wTtT{qY4(>prREevi}d?_x{w(H)a^zXFp(`891at|7C9vsxO zK$ll^G0po&6uPX-r*-+fF4FLvs3ulnVwp)wZmksj5&RQfw7eJ>)z=*xbedR)5!xCe zHI^))tW&smvEeN*_#(GN__JIxnfOoy{W^cC?{bB0OKSTYq=d!m_{CD1ohE~vT7UC! yI9wXu1xm7oWq%*qQgT(AkP4!e)b=6nZ)f4x6ZH8+z`!oKz z!`QDhID7dRyo=lX4kDT48H=hXkGLCqk%y<3)h2%AbCT7v`lJyx9Pfi~Moq`p!3R;` z_y+h^)N*_id^>78o@bq?^C6RgY~5wDb?il570C7~lbw59c4hCbAN5pcr!VPu={xU? zbV**m>mT#z(n7xci2RCNaq^`LNrtbak9I)KUH{9 zdoSO|Be)S~S-h3etJa4qS30(ZCc!6Dn-+PTf%i!#Aya3sItK6JHeZ1#MoI5#NZP|gep_(dCtyQ+lz~Ko>KzXuz&zC*f$DEGz5Xz-; zHHmF97Iv&vEYtk9PW&R3aUS1RI&`x!EnFxJH=Sy@&9v_%-_&Z%N+hbHWe(`4*CQWWlXisYvUN)E%DShqs*ai0)gtPQ#AYyl9hWLTLz zarKr#Lf&*@f_g8t<5RrFSY=bChZiRI`XG!=m`qL$kdbQuN2<_F$jCP|XNqYjwIcLm}iv zm=!q{BQj?!`~mD^6V1f~pnPe511MhfI^wAIGCxMrkuj#KRqTHI;ue%F-(T~8%*O6VFT_Ip??XZ$_QcQ0MnPjm34>Z0IYGA`N6aQ z;w#~Paqd=N#j+jp!8>F-HOmC}xi5a%T0~g3E7ZuI?vY1Vi1dk^bFd`@GN`l=FF|S= zAdCn29bEetN9Q^NzYMUHuxS2elQ?w0vf*BBRe-uAkyTv8F94+!A`BGbjGO+)>{|r0 z!z>p#X9?y*_En9_vx3^M;vfw46GSLw1HVH4D!q7$$SRR@A);ugD+=)ngeCxCjXom$ zufnO623@MUJf0{a%C-=bLe4VcyFz?Di?b6+QwUikLg;1kz`^|_aRjJtf|R}YW20`) z&m&DZ=%lP*?xEEIq-2A2a z0Rzj9+4pyj0Q6^!6XaY^iS7XC8P_jW{7&tN=f31eTzZH6N1VVv)sDPFPx|+LTfg6M zvzm?uj)I^VQqj-3Mp$7bNfs_T?=^TWCGT%K1GC{VaK5_()N5CE*dHW+I5uNF7J9iXoLHF z`7iIUMqRH#x``IuA%#61Ptjqcrq%mdZ0sYsCp%PF^wRZu%Kc4X{?Q5YZZkdP&g24x=!X?|MK+9;L-7hctinw*iQBTazc+_;U8VNa?XVWpPw6NJfLm- zKcc>P!K>#MwD??+sLU_UEcz<=!LoM!hdP(O4pP>s@fo+5QJwo!6qBa?r>WCv`uaU> z0QyB5(#Ce^M*(FsolG+asivqjt@4kU1aDP+=N&rD&2u2sqXVjz`Ogdd{&4wnm#_HM F{Wm%{Jih<{ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/timing.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/timing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbf3ee5ca0a7617c5fb3197fd87f97e89649a790 GIT binary patch literal 604 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unittest.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unittest.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..993f5e6f518c87894b0e295b479cadcb0b2d5e53 GIT binary patch literal 11248 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unraisableexception.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/unraisableexception.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47ec79b42b638cc36bfe82d173f64bb769a3bd19 GIT binary patch literal 3311 zcmbVO-EY)J5a0Fre7?_wBtXMMWj-D-QtqIAsER5Cr3loAv=sqV_hE6oo17D$eeCWL z5+!|TkSbNG`dSGpC6D||eyY@`O8pCps&;05Ux^Zsx_2JWe$4F5Z)Rs5Hk&m9Z6$dd zZZ!z`13S~rhR$tx)L%e2;WQyZ@umSaeJilwZ6&3i9oQ7-N=dor1dict;44AJ@MYlL zz%{%Bd^M;Vz5;wLs2QFn^`L&AaFbC#6R~Zcubaknm1Qu7>U&|+FaT03m7Qf zNw?wU-VKu^TupGOdS9eMhB}it@l{{PSsEt5+b9#Es_9mS&TV+qQy_vMlmH_QO5EZl zaMb2yaMb1w^ee9|1BV@y5nPz9@jA>pq9R;TT_lSHICuj^pe){aZSy8?!J0a^z?GT& zLKNz#%Lb_oV}&L&@gfq1mA_kr+|5!gUTCiurs2Afo>?BJ>)u4d8%uZtg;Q!I5^h)eRBp`Rj6?%vu*r0+kH}B!16|YWyijWyuMV{E(#%V;6!Q_{rZnUR zpxuS2_p>B5aH%@_7q9tn`O&yXzoa%2QrN)9Jx_L9@3xOg-6W-;bfPv zg2I`6NZ!z8-^yzfAM+~963#H)L8q-mvi62Z6#e_h&LbtH>O@^UMOn(j&KFrU=!sOr zU?w`x!XZyj*7Kd>O%>F3{V|b ztm{n78XwMfe>-;>i_=&$b_LzS5>BW?cP^h^1ll8sopX4sAC2q81Mu^XTvxs&;d&qv zZ;J>K8#Uy|2;%H6Ef9Q2WF5LbHG&{N1HD2CqGceHXMoGg2F87LsH3RT+a23dz_K%qj;6p)UM( zuDlb`p^$IqF4j2>7eQVbR=nIXL#qX#CPb(f8uDX6F@WF&B;NZ$NBRN~&~ZjuOu zyntk4ui-FpB$|m?rBIzq?S+;-TW(cqV6{W7w`SfiEiW(2Dl9m13724CG^l=sjwbu$ zm+$ug>Bod3=2SjMx&cTJsJvG2do;B<-J`qo6=5?BDD?YuDoTfvw92jv22Q z>Ka--EFIEM$eyKZ2X);zXzp5v^pH~W1opGxw_l}jq74ZqCF`m6+$nCO+}?m^? zsgYNQnK=-7)8yt@z}*UE3U`IdZRB#Ns6K)BwJBbcVlg)JNg?2uax3b|i>ST?HcbW5 z9Q=!uV>q#f$pIWTsEK$oZ-J=#5C|SPjB10{ESoL>XPdtBkM*xpk$5myd@HD<(3TAQ zP+1RtJ{Vex3g>%jjHCr{t-{ILx+e4FrWEB$V2(;LA1 zbJA2BtWy#zcwWXbr_9049pE8vBpx=2bxq=T@8q=0YuLq8Y`_j&j9%R9Ck8=@UP;{L jL`>YziXw9h?{IYq2$p0QGGzR-T)Wkon{UtsyLkQs!n!#A literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warning_types.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warning_types.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86358f29256fcf03348e1b0df801b79e82c23238 GIT binary patch literal 4466 zcmbVPU2ojR6(zac4@oOoUdyuNI`ITSnl50i9-5a5hGErC-J-4F#8v_<5Ugf7v%8eJ z!_5pSWfiADYrnd`paoi>FaApe^3?icy;@}h(5 zY4kt-hoI&SWxnLXEWgd^O@8M|HArLl*x*Y)hUf^nr=v11=&zw6s^HL7;2 zm|Kf#<9fI5I*%Mt7mb&WXvoHkN_Rolfh>qcAd7P0MdgLtU9{MeXaZ{%*pkJT#R{;M z0&80AidY4_IyzsH(6^mXIYe3h4u3Ge}MX!r>pzFo@mc?#}4PYAu zcFkg&;wG@01-53fpNU(*ZWY*d>5KQ|f_VRx*Ik!&Sv|Y~-EIqpIZS$TebXWRAkT)V zK5^O~{D+usyIEydP4QEIB$d=*fT90%I*|m??-yL}VO$-AibnyhnZIx@`3bFIAf2u& zoUSL_Zbf()SMVM`TYfxEq)EQyT7_zO()l$vk_8brh7DrPcoebY{Fz~L943hrtUqNF zyQ-7b^1JgLEua0x*`_U-K>eY{_pX;2?+d*Y+klK9oVSo!@Rrb+jFYeYzdR z>X)5gZJRKWTN55U<3nk-d-=TWLZe;+3QcD+)nv;{XvRm^aDC4rmm>7$V@u7hflkTi z@>oNHlHE^=!90`6NEU1|kd8G>=Zjd*VMjM0)1F>He@a?Dx>gw%nTeO^uQ?z zi8GUIE^d)_lRR`TE;3w*e9TPXI%*hA5dgr6WV?@l$Hw7slrRd3LwmmR2*xl+QxK0Q zkxV335o30U@G6F_u|POK__K4;+*ijftEGXojN4bENY_=eaAqS4c`A zp|W?f^KG%kckmj<)tp{DVtAIH=a^4tV&nW+f{5LA^*VM%rmDdO$2@^Rl^`+}oeOFd z&$5~cNws?*LB&-GpJ2mgnY4}a<)I{CW2c3smlP4Qf#aB=i9x@^fB;8-Fvh^%4~ zt#8t^SsI#)sMqn}6Es7Sx8eCUA0lW%&zaP_q}bcJ)cXpDDIzHz#5zusP|ZT@_oW4u z3D#y_#nN&G{JmJx$<1c>6hV4MW>!I*szhoSo(&sCfPh&sO7CotDjRIf9oA3bbXWu1 zv=&lGMOmFIvcahew)ioJ|K-uWW2eoeKC>aCi4>a@{QijS(WH|}tWhZB3onYT?}67x z#J0`7o?q95T#D%;4DG9DDn3?!GJ#tW3DNZ3Xkbt}oTH(t3CX)AXe zQp$ZyBYGiG6udkgA;aLmOr)bYLR>39Lu}cgWS(R5ohWUF8m}-0g+4=BQEYgpw5J#` zsYuJ5i4bKNfg2gs|2J9DHQhslTy5lt1#rRcOP zS)-hpH4=#oiFKM@4_SR9qk;YiH*@*SejLz*zn~eC+VM^{&!15zr*N;m{MNkb^CZ!k z7xp#9XH5m8zD3=~)X}z@n6skHz1`+jE5e6(dW4o|(~4hPb(@|hw7r?tz&6f5--nDb ze@m4{ZFJq0;+w5Q1rJX&F`q4=Y|@2^*)KKw$)oA(K~rI_sgTWD=gUz|bz!#r8(UtM z8&G1mImbq58#}D$SiLgWSXtkuTGqImf1}+a`>-z5bnTzl@S2s?n&-C8v^CtXwSX_7 MEtJ2_3Y9qj2dyVpd;kCd literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warnings.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/__pycache__/warnings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90228d4ca8bfd91d1c066d8fb27e25be9672797f GIT binary patch literal 3957 zcmcgv&668P6`!6Njb^oytX=P}?Zl1;2e3uOD^nzpm=FxMNy5jHau!k=* zL+}8%+*B9wyf?)E}8;af`FW3d=7G8Rm9kr%vcFT5}RNt%WrMyMVpW zGjI)XJ**p;C4T6G)=TEnM%XZGb-?rCyn%hd&9G_Uxuli0!?uAN$%(WRb__h9EToI! zBC|wYJ_fI5axz^CmoT5_jJH0wd7GbjJ(OJ)NE)bod67vnmH>fklC9wXSe{lq4hLt$sFsHc&<1Nk2Or4+Oh zTV1Mv?$sdTJcxp79+YH(&I3IX!7#ojvOo)^R|6$PFw%OWuJ(F7AE?#IjzQ#d*b`Y# z>F9PMdb0yoM>&KL4Edexo3J zZ!jW1r)5&EIH!!%$+*p`Q5r%Ve$*inusTVkKM37xv=X`{c#J>%I+NT((UOSpW}@^ z`_C4$?*8fSr`-K5ps5F%H+LITHa;=#=!I!xY7<93zvuGi9s>m%FIJfP)CQ(C!`%Jy zBa6E(!ulb~W7heurJh{@Px^g~!Cgdk`@uhuX_US}mA?qpNC$&R4@N@GB3T8)GNlC% zV#F|_8UE1`!i)#vz8Gv#jt94sd~j#A(FpKtY>+=THoyu95v=8z2;$9PTLh65L6+;o zoJfFtGXN>=I$&8byBQ5eGg-iJg8aq7TE;_E9A)I$*r-CRN}9eDgIsdKA;KunAr?th zw2x-aI!XlU0a30MUX*M{J5+vr=r4x3+$k)~+$>7P&xlee94@+Uabk8#iYi29?AQHw9P8Wjb>4hL}JnJvzIATrd7;fp9q?N*-0 zD{R6MvR_ra!X>#RhVhdowzb( z(PpYJ@IzBX64&Uma=z$;BMC(wq=cfB`Y*z4^>~;W`H{O6Rq`sG&*V=CJ{C{sDn*h! z;(chL<0vr_(gjXE!j3X!+$ypt3A%y2K&%41iU!>ESMCM6lwAry-4pvwlh{V?aO~V&G}X$ y-XZs?_we%3ototj-Km1;YrCxJbbOm0lwj{^`;Em`ldaS-{06N(Yd6r=0.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= + +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK. + +INSTALL/DEBUGGING +================= + +To include this support in another application that has setup.py generated +scripts: + +- Add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point. + +- Include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + Call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument(). + +If things do not work right away: + +- Switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 + +- Run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not. + +- Sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" +import argparse +import os +import sys +from glob import glob +from typing import Any +from typing import List +from typing import Optional + + +class FastFilesCompleter: + """Fast file completer class.""" + + def __init__(self, directories: bool = True) -> None: + self.directories = directories + + def __call__(self, prefix: str, **kwargs: Any) -> List[str]: + # Only called on non option completions. + if os.path.sep in prefix[1:]: + prefix_dir = len(os.path.dirname(prefix) + os.path.sep) + else: + prefix_dir = 0 + completion = [] + globbed = [] + if "*" not in prefix and "?" not in prefix: + # We are on unix, otherwise no bash. + if not prefix or prefix[-1] == os.path.sep: + globbed.extend(glob(prefix + ".*")) + prefix += "*" + globbed.extend(glob(prefix)) + for x in sorted(globbed): + if os.path.isdir(x): + x += "/" + # Append stripping the prefix (like bash, not like compgen). + completion.append(x[prefix_dir:]) + return completion + + +if os.environ.get("_ARGCOMPLETE"): + try: + import argcomplete.completers + except ImportError: + sys.exit(-1) + filescompleter: Optional[FastFilesCompleter] = FastFilesCompleter() + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + argcomplete.autocomplete(parser, always_complete_options=False) + + +else: + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + pass + + filescompleter = None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__init__.py new file mode 100644 index 0000000..511d0dd --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,22 @@ +"""Python inspection/code generation API.""" +from .code import Code +from .code import ExceptionInfo +from .code import filter_traceback +from .code import Frame +from .code import getfslineno +from .code import Traceback +from .code import TracebackEntry +from .source import getrawcode +from .source import Source + +__all__ = [ + "Code", + "ExceptionInfo", + "filter_traceback", + "Frame", + "getfslineno", + "getrawcode", + "Traceback", + "TracebackEntry", + "Source", +] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd12a8639faa84653b3b44509617d795395f51ac GIT binary patch literal 651 zcmZ{h&u-K(5XPNs{_h4j@Bn+Nw1-_EI3Xbff+F>TDz&}%V!6p|8c6J5d!dUs@hIGQ zrCfXB71~Q@vQkx5!cji|zVUbxk5yR~499tQr)~nqZpb_yA)0six?dctsbl|PS8_x{T0Wvkr)m2Zvam8oIbfCj6`FW;Y@Z2sXn*eV#K54ebPd3;^LK+7k3 zYcTL)+jbtT^j1}Hsj9CSi$7V_Q}(<8zja-!K~rvi{wV3$gE`jT?s2)mY^#3IZ=g%g z%+6M@$}P3c&PXDXm?R;|Nm7!Ggqr_(RgS5YwW*{WBP#X(?GHQ0#7*Bi6r?g#Lv@{$ zf0=zwwMlM&^9u*o@#;!x)finX{?Sys9<;}x0sc*`aazOm0Jc4ZhrI{qRktK>0;bVlqgs{ GeZqUp&Zrvz literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6607755b90f59f64cd57c7230e3345b34737021 GIT binary patch literal 37769 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/code.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/code.py new file mode 100644 index 0000000..4230693 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/code.py @@ -0,0 +1,1259 @@ +import inspect +import re +import sys +import traceback +from inspect import CO_VARARGS +from inspect import CO_VARKEYWORDS +from io import StringIO +from pathlib import Path +from traceback import format_exception_only +from types import CodeType +from types import FrameType +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generic +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from weakref import ref + +import attr +import pluggy +import py + +import _pytest +from _pytest._code.source import findsource +from _pytest._code.source import getrawcode +from _pytest._code.source import getstatementrange_ast +from _pytest._code.source import Source +from _pytest._io import TerminalWriter +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr +from _pytest.compat import final +from _pytest.compat import get_real_func + +if TYPE_CHECKING: + from typing_extensions import Literal + from weakref import ReferenceType + + _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] + + +class Code: + """Wrapper around Python code objects.""" + + __slots__ = ("raw",) + + def __init__(self, obj: CodeType) -> None: + self.raw = obj + + @classmethod + def from_function(cls, obj: object) -> "Code": + return cls(getrawcode(obj)) + + def __eq__(self, other): + return self.raw == other.raw + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def firstlineno(self) -> int: + return self.raw.co_firstlineno - 1 + + @property + def name(self) -> str: + return self.raw.co_name + + @property + def path(self) -> Union[py.path.local, str]: + """Return a path object pointing to source code, or an ``str`` in + case of ``OSError`` / non-existing file.""" + if not self.raw.co_filename: + return "" + try: + p = py.path.local(self.raw.co_filename) + # maybe don't try this checking + if not p.check(): + raise OSError("py.path check failed.") + return p + except OSError: + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? + return self.raw.co_filename + + @property + def fullsource(self) -> Optional["Source"]: + """Return a _pytest._code.Source object for the full source file of the code.""" + full, _ = findsource(self.raw) + return full + + def source(self) -> "Source": + """Return a _pytest._code.Source object for the code object's source only.""" + # return source only for that part of code + return Source(self.raw) + + def getargs(self, var: bool = False) -> Tuple[str, ...]: + """Return a tuple with the argument names for the code object. + + If 'var' is set True also return the names of the variable and + keyword arguments when present. + """ + # Handy shortcut for getting args. + raw = self.raw + argcount = raw.co_argcount + if var: + argcount += raw.co_flags & CO_VARARGS + argcount += raw.co_flags & CO_VARKEYWORDS + return raw.co_varnames[:argcount] + + +class Frame: + """Wrapper around a Python frame holding f_locals and f_globals + in which expressions can be evaluated.""" + + __slots__ = ("raw",) + + def __init__(self, frame: FrameType) -> None: + self.raw = frame + + @property + def lineno(self) -> int: + return self.raw.f_lineno - 1 + + @property + def f_globals(self) -> Dict[str, Any]: + return self.raw.f_globals + + @property + def f_locals(self) -> Dict[str, Any]: + return self.raw.f_locals + + @property + def code(self) -> Code: + return Code(self.raw.f_code) + + @property + def statement(self) -> "Source": + """Statement this frame is at.""" + if self.code.fullsource is None: + return Source("") + return self.code.fullsource.getstatement(self.lineno) + + def eval(self, code, **vars): + """Evaluate 'code' in the frame. + + 'vars' are optional additional local variables. + + Returns the result of the evaluation. + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + return eval(code, self.f_globals, f_locals) + + def repr(self, object: object) -> str: + """Return a 'safe' (non-recursive, one-line) string repr for 'object'.""" + return saferepr(object) + + def getargs(self, var: bool = False): + """Return a list of tuples (name, value) for all arguments. + + If 'var' is set True, also include the variable and keyword arguments + when present. + """ + retval = [] + for arg in self.code.getargs(var): + try: + retval.append((arg, self.f_locals[arg])) + except KeyError: + pass # this can occur when using Psyco + return retval + + +class TracebackEntry: + """A single entry in a Traceback.""" + + __slots__ = ("_rawentry", "_excinfo", "_repr_style") + + def __init__( + self, + rawentry: TracebackType, + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, + ) -> None: + self._rawentry = rawentry + self._excinfo = excinfo + self._repr_style: Optional['Literal["short", "long"]'] = None + + @property + def lineno(self) -> int: + return self._rawentry.tb_lineno - 1 + + def set_repr_style(self, mode: "Literal['short', 'long']") -> None: + assert mode in ("short", "long") + self._repr_style = mode + + @property + def frame(self) -> Frame: + return Frame(self._rawentry.tb_frame) + + @property + def relline(self) -> int: + return self.lineno - self.frame.code.firstlineno + + def __repr__(self) -> str: + return "" % (self.frame.code.path, self.lineno + 1) + + @property + def statement(self) -> "Source": + """_pytest._code.Source object for the current statement.""" + source = self.frame.code.fullsource + assert source is not None + return source.getstatement(self.lineno) + + @property + def path(self) -> Union[py.path.local, str]: + """Path to the source code.""" + return self.frame.code.path + + @property + def locals(self) -> Dict[str, Any]: + """Locals of underlying frame.""" + return self.frame.f_locals + + def getfirstlinesource(self) -> int: + return self.frame.code.firstlineno + + def getsource(self, astcache=None) -> Optional["Source"]: + """Return failing source code.""" + # we use the passed in astcache to not reparse asttrees + # within exception info printing + source = self.frame.code.fullsource + if source is None: + return None + key = astnode = None + if astcache is not None: + key = self.frame.code.path + if key is not None: + astnode = astcache.get(key, None) + start = self.getfirstlinesource() + try: + astnode, _, end = getstatementrange_ast( + self.lineno, source, astnode=astnode + ) + except SyntaxError: + end = self.lineno + 1 + else: + if key is not None: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self) -> bool: + """Return True if the current frame has a var __tracebackhide__ + resolving to True. + + If __tracebackhide__ is a callable, it gets called with the + ExceptionInfo instance and can decide whether to hide the traceback. + + Mostly for internal use. + """ + tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = ( + False + ) + for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): + # in normal cases, f_locals and f_globals are dictionaries + # however via `exec(...)` / `eval(...)` they can be other types + # (even incorrect types!). + # as such, we suppress all exceptions while accessing __tracebackhide__ + try: + tbh = maybe_ns_dct["__tracebackhide__"] + except Exception: + pass + else: + break + if tbh and callable(tbh): + return tbh(None if self._excinfo is None else self._excinfo()) + return tbh + + def __str__(self) -> str: + name = self.frame.code.name + try: + line = str(self.statement).lstrip() + except KeyboardInterrupt: + raise + except BaseException: + line = "???" + # This output does not quite match Python's repr for traceback entries, + # but changing it to do so would break certain plugins. See + # https://github.com/pytest-dev/pytest/pull/7535/ for details. + return " File %r:%d in %s\n %s\n" % ( + str(self.path), + self.lineno + 1, + name, + line, + ) + + @property + def name(self) -> str: + """co_name of underlying code.""" + return self.frame.code.raw.co_name + + +class Traceback(List[TracebackEntry]): + """Traceback objects encapsulate and offer higher level access to Traceback entries.""" + + def __init__( + self, + tb: Union[TracebackType, Iterable[TracebackEntry]], + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, + ) -> None: + """Initialize from given python traceback object and ExceptionInfo.""" + self._excinfo = excinfo + if isinstance(tb, TracebackType): + + def f(cur: TracebackType) -> Iterable[TracebackEntry]: + cur_: Optional[TracebackType] = cur + while cur_ is not None: + yield TracebackEntry(cur_, excinfo=excinfo) + cur_ = cur_.tb_next + + super().__init__(f(tb)) + else: + super().__init__(tb) + + def cut( + self, + path=None, + lineno: Optional[int] = None, + firstlineno: Optional[int] = None, + excludepath: Optional[py.path.local] = None, + ) -> "Traceback": + """Return a Traceback instance wrapping part of this Traceback. + + By providing any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined. + + This allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback). + """ + for x in self: + code = x.frame.code + codepath = code.path + if ( + (path is None or codepath == path) + and ( + excludepath is None + or not isinstance(codepath, py.path.local) + or not codepath.relto(excludepath) + ) + and (lineno is None or x.lineno == lineno) + and (firstlineno is None or x.frame.code.firstlineno == firstlineno) + ): + return Traceback(x._rawentry, self._excinfo) + return self + + @overload + def __getitem__(self, key: int) -> TracebackEntry: + ... + + @overload + def __getitem__(self, key: slice) -> "Traceback": + ... + + def __getitem__(self, key: Union[int, slice]) -> Union[TracebackEntry, "Traceback"]: + if isinstance(key, slice): + return self.__class__(super().__getitem__(key)) + else: + return super().__getitem__(key) + + def filter( + self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden() + ) -> "Traceback": + """Return a Traceback instance with certain items removed + + fn is a function that gets a single argument, a TracebackEntry + instance, and should return True when the item should be added + to the Traceback, False when not. + + By default this removes all the TracebackEntries which are hidden + (see ishidden() above). + """ + return Traceback(filter(fn, self), self._excinfo) + + def getcrashentry(self) -> TracebackEntry: + """Return last non-hidden traceback entry that lead to the exception of a traceback.""" + for i in range(-1, -len(self) - 1, -1): + entry = self[i] + if not entry.ishidden(): + return entry + return self[-1] + + def recursionindex(self) -> Optional[int]: + """Return the index of the frame/TracebackEntry where recursion originates if + appropriate, None if no recursion occurred.""" + cache: Dict[Tuple[Any, int, int], List[Dict[str, Any]]] = {} + for i, entry in enumerate(self): + # id for the code.raw is needed to work around + # the strange metaprogramming in the decorator lib from pypi + # which generates code objects that have hash/value equality + # XXX needs a test + key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno + # print "checking for recursion at", key + values = cache.setdefault(key, []) + if values: + f = entry.frame + loc = f.f_locals + for otherloc in values: + if f.eval( + co_equal, + __recursioncache_locals_1=loc, + __recursioncache_locals_2=otherloc, + ): + return i + values.append(entry.frame.f_locals) + return None + + +co_equal = compile( + "__recursioncache_locals_1 == __recursioncache_locals_2", "?", "eval" +) + + +_E = TypeVar("_E", bound=BaseException, covariant=True) + + +@final +@attr.s(repr=False) +class ExceptionInfo(Generic[_E]): + """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" + + _assert_start_repr = "AssertionError('assert " + + _excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]]) + _striptext = attr.ib(type=str, default="") + _traceback = attr.ib(type=Optional[Traceback], default=None) + + @classmethod + def from_exc_info( + cls, + exc_info: Tuple[Type[_E], _E, TracebackType], + exprinfo: Optional[str] = None, + ) -> "ExceptionInfo[_E]": + """Return an ExceptionInfo for an existing exc_info tuple. + + .. warning:: + + Experimental API + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + """ + _striptext = "" + if exprinfo is None and isinstance(exc_info[1], AssertionError): + exprinfo = getattr(exc_info[1], "msg", None) + if exprinfo is None: + exprinfo = saferepr(exc_info[1]) + if exprinfo and exprinfo.startswith(cls._assert_start_repr): + _striptext = "AssertionError: " + + return cls(exc_info, _striptext) + + @classmethod + def from_current( + cls, exprinfo: Optional[str] = None + ) -> "ExceptionInfo[BaseException]": + """Return an ExceptionInfo matching the current traceback. + + .. warning:: + + Experimental API + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + """ + tup = sys.exc_info() + assert tup[0] is not None, "no current exception" + assert tup[1] is not None, "no current exception" + assert tup[2] is not None, "no current exception" + exc_info = (tup[0], tup[1], tup[2]) + return ExceptionInfo.from_exc_info(exc_info, exprinfo) + + @classmethod + def for_later(cls) -> "ExceptionInfo[_E]": + """Return an unfilled ExceptionInfo.""" + return cls(None) + + def fill_unfilled(self, exc_info: Tuple[Type[_E], _E, TracebackType]) -> None: + """Fill an unfilled ExceptionInfo created with ``for_later()``.""" + assert self._excinfo is None, "ExceptionInfo was already filled" + self._excinfo = exc_info + + @property + def type(self) -> Type[_E]: + """The exception class.""" + assert ( + self._excinfo is not None + ), ".type can only be used after the context manager exits" + return self._excinfo[0] + + @property + def value(self) -> _E: + """The exception value.""" + assert ( + self._excinfo is not None + ), ".value can only be used after the context manager exits" + return self._excinfo[1] + + @property + def tb(self) -> TracebackType: + """The exception raw traceback.""" + assert ( + self._excinfo is not None + ), ".tb can only be used after the context manager exits" + return self._excinfo[2] + + @property + def typename(self) -> str: + """The type name of the exception.""" + assert ( + self._excinfo is not None + ), ".typename can only be used after the context manager exits" + return self.type.__name__ + + @property + def traceback(self) -> Traceback: + """The traceback.""" + if self._traceback is None: + self._traceback = Traceback(self.tb, excinfo=ref(self)) + return self._traceback + + @traceback.setter + def traceback(self, value: Traceback) -> None: + self._traceback = value + + def __repr__(self) -> str: + if self._excinfo is None: + return "" + return "<{} {} tblen={}>".format( + self.__class__.__name__, saferepr(self._excinfo[1]), len(self.traceback) + ) + + def exconly(self, tryshort: bool = False) -> str: + """Return the exception as a string. + + When 'tryshort' resolves to True, and the exception is a + _pytest._code._AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning). + """ + lines = format_exception_only(self.type, self.value) + text = "".join(lines) + text = text.rstrip() + if tryshort: + if text.startswith(self._striptext): + text = text[len(self._striptext) :] + return text + + def errisinstance( + self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]] + ) -> bool: + """Return True if the exception is an instance of exc. + + Consider using ``isinstance(excinfo.value, exc)`` instead. + """ + return isinstance(self.value, exc) + + def _getreprcrash(self) -> "ReprFileLocation": + exconly = self.exconly(tryshort=True) + entry = self.traceback.getcrashentry() + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + return ReprFileLocation(path, lineno + 1, exconly) + + def getrepr( + self, + showlocals: bool = False, + style: "_TracebackStyle" = "long", + abspath: bool = False, + tbfilter: bool = True, + funcargs: bool = False, + truncate_locals: bool = True, + chain: bool = True, + ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]: + """Return str()able representation of this exception info. + + :param bool showlocals: + Show locals per traceback entry. + Ignored if ``style=="native"``. + + :param str style: + long|short|no|native|value traceback style. + + :param bool abspath: + If paths should be changed to absolute or left unchanged. + + :param bool tbfilter: + Hide entries that contain a local variable ``__tracebackhide__==True``. + Ignored if ``style=="native"``. + + :param bool funcargs: + Show fixtures ("funcargs" for legacy purposes) per traceback entry. + + :param bool truncate_locals: + With ``showlocals==True``, make sure locals can be safely represented as strings. + + :param bool chain: + If chained exceptions in Python 3 should be shown. + + .. versionchanged:: 3.9 + + Added the ``chain`` parameter. + """ + if style == "native": + return ReprExceptionInfo( + ReprTracebackNative( + traceback.format_exception( + self.type, self.value, self.traceback[0]._rawentry + ) + ), + self._getreprcrash(), + ) + + fmt = FormattedExcinfo( + showlocals=showlocals, + style=style, + abspath=abspath, + tbfilter=tbfilter, + funcargs=funcargs, + truncate_locals=truncate_locals, + chain=chain, + ) + return fmt.repr_excinfo(self) + + def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]": + """Check whether the regular expression `regexp` matches the string + representation of the exception using :func:`python:re.search`. + + If it matches `True` is returned, otherwise an `AssertionError` is raised. + """ + __tracebackhide__ = True + msg = "Regex pattern {!r} does not match {!r}." + if regexp == str(self.value): + msg += " Did you mean to `re.escape()` the regex?" + assert re.search(regexp, str(self.value)), msg.format(regexp, str(self.value)) + # Return True to allow for "assert excinfo.match()". + return True + + +@attr.s +class FormattedExcinfo: + """Presenting information about failing Functions and Generators.""" + + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + showlocals = attr.ib(type=bool, default=False) + style = attr.ib(type="_TracebackStyle", default="long") + abspath = attr.ib(type=bool, default=True) + tbfilter = attr.ib(type=bool, default=True) + funcargs = attr.ib(type=bool, default=False) + truncate_locals = attr.ib(type=bool, default=True) + chain = attr.ib(type=bool, default=True) + astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False) + + def _getindent(self, source: "Source") -> int: + # Figure out indent for the given source. + try: + s = str(source.getstatement(len(source) - 1)) + except KeyboardInterrupt: + raise + except BaseException: + try: + s = str(source[-1]) + except KeyboardInterrupt: + raise + except BaseException: + return 0 + return 4 + (len(s) - len(s.lstrip())) + + def _getentrysource(self, entry: TracebackEntry) -> Optional["Source"]: + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]: + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + args.append((argname, saferepr(argvalue))) + return ReprFuncArgs(args) + return None + + def get_source( + self, + source: Optional["Source"], + line_index: int = -1, + excinfo: Optional[ExceptionInfo[BaseException]] = None, + short: bool = False, + ) -> List[str]: + """Return formatted and marked up source lines.""" + lines = [] + if source is None or line_index >= len(source.lines): + source = Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + for line in source.lines[line_index + 1 :]: + lines.append(space_prefix + line) + if excinfo is not None: + indent = 4 if short else self._getindent(source) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines + + def get_exconly( + self, + excinfo: ExceptionInfo[BaseException], + indent: int = 4, + markall: bool = False, + ) -> List[str]: + lines = [] + indentstr = " " * indent + # Get the real exception information out. + exlines = excinfo.exconly(tryshort=True).split("\n") + failindent = self.fail_marker + indentstr[1:] + for line in exlines: + lines.append(failindent + line) + if not markall: + failindent = indentstr + return lines + + def repr_locals(self, locals: Mapping[str, object]) -> Optional["ReprLocals"]: + if self.showlocals: + lines = [] + keys = [loc for loc in locals if loc[0] != "@"] + keys.sort() + for name in keys: + value = locals[name] + if name == "__builtins__": + lines.append("__builtins__ = ") + else: + # This formatting could all be handled by the + # _repr() function, which is only reprlib.Repr in + # disguise, so is very configurable. + if self.truncate_locals: + str_repr = saferepr(value) + else: + str_repr = safeformat(value) + # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): + lines.append(f"{name:<10} = {str_repr}") + # else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) + return None + + def repr_traceback_entry( + self, + entry: TracebackEntry, + excinfo: Optional[ExceptionInfo[BaseException]] = None, + ) -> "ReprEntry": + lines: List[str] = [] + style = entry._repr_style if entry._repr_style is not None else self.style + if style in ("short", "long"): + source = self._getentrysource(entry) + if source is None: + source = Source("???") + line_index = 0 + else: + line_index = entry.lineno - entry.getfirstlinesource() + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source(source, line_index, excinfo, short=short) + lines.extend(s) + if short: + message = "in %s" % (entry.name) + else: + message = excinfo and excinfo.typename or "" + path = self._makepath(entry.path) + reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) + elif style == "value": + if excinfo: + lines.extend(str(excinfo.value).split("\n")) + return ReprEntry(lines, None, None, None, style) + else: + if excinfo: + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None, None, style) + + def _makepath(self, path): + if not self.abspath: + try: + np = py.path.local().bestrelpath(path) + except OSError: + return path + if len(np) < len(str(path)): + path = np + return path + + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback": + traceback = excinfo.traceback + if self.tbfilter: + traceback = traceback.filter() + + if isinstance(excinfo.value, RecursionError): + traceback, extraline = self._truncate_recursive_traceback(traceback) + else: + extraline = None + + last = traceback[-1] + entries = [] + if self.style == "value": + reprentry = self.repr_traceback_entry(last, excinfo) + entries.append(reprentry) + return ReprTraceback(entries, None, style=self.style) + + for index, entry in enumerate(traceback): + einfo = (last == entry) and excinfo or None + reprentry = self.repr_traceback_entry(entry, einfo) + entries.append(reprentry) + return ReprTraceback(entries, extraline, style=self.style) + + def _truncate_recursive_traceback( + self, traceback: Traceback + ) -> Tuple[Traceback, Optional[str]]: + """Truncate the given recursive traceback trying to find the starting + point of the recursion. + + The detection is done by going through each traceback entry and + finding the point in which the locals of the frame are equal to the + locals of a previous frame (see ``recursionindex()``). + + Handle the situation where the recursion process might raise an + exception (for example comparing numpy arrays using equality raises a + TypeError), in which case we do our best to warn the user of the + error and show a limited traceback. + """ + try: + recursionindex = traceback.recursionindex() + except Exception as e: + max_frames = 10 + extraline: Optional[str] = ( + "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" + " The following exception happened when comparing locals in the stack frame:\n" + " {exc_type}: {exc_msg}\n" + " Displaying first and last {max_frames} stack frames out of {total}." + ).format( + exc_type=type(e).__name__, + exc_msg=str(e), + max_frames=max_frames, + total=len(traceback), + ) + # Type ignored because adding two instaces of a List subtype + # currently incorrectly has type List instead of the subtype. + traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore + else: + if recursionindex is not None: + extraline = "!!! Recursion detected (same locals & position)" + traceback = traceback[: recursionindex + 1] + else: + extraline = None + + return traceback, extraline + + def repr_excinfo( + self, excinfo: ExceptionInfo[BaseException] + ) -> "ExceptionChainRepr": + repr_chain: List[ + Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]] + ] = [] + e: Optional[BaseException] = excinfo.value + excinfo_: Optional[ExceptionInfo[BaseException]] = excinfo + descr = None + seen: Set[int] = set() + while e is not None and id(e) not in seen: + seen.add(id(e)) + if excinfo_: + reprtraceback = self.repr_traceback(excinfo_) + reprcrash: Optional[ReprFileLocation] = ( + excinfo_._getreprcrash() if self.style != "value" else None + ) + else: + # Fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work. + reprtraceback = ReprTracebackNative( + traceback.format_exception(type(e), e, None) + ) + reprcrash = None + + repr_chain += [(reprtraceback, reprcrash, descr)] + if e.__cause__ is not None and self.chain: + e = e.__cause__ + excinfo_ = ( + ExceptionInfo((type(e), e, e.__traceback__)) + if e.__traceback__ + else None + ) + descr = "The above exception was the direct cause of the following exception:" + elif ( + e.__context__ is not None and not e.__suppress_context__ and self.chain + ): + e = e.__context__ + excinfo_ = ( + ExceptionInfo((type(e), e, e.__traceback__)) + if e.__traceback__ + else None + ) + descr = "During handling of the above exception, another exception occurred:" + else: + e = None + repr_chain.reverse() + return ExceptionChainRepr(repr_chain) + + +@attr.s(eq=False) +class TerminalRepr: + def __str__(self) -> str: + # FYI this is called from pytest-xdist's serialization of exception + # information. + io = StringIO() + tw = TerminalWriter(file=io) + self.toterminal(tw) + return io.getvalue().strip() + + def __repr__(self) -> str: + return "<{} instance at {:0x}>".format(self.__class__, id(self)) + + def toterminal(self, tw: TerminalWriter) -> None: + raise NotImplementedError() + + +# This class is abstract -- only subclasses are instantiated. +@attr.s(eq=False) +class ExceptionRepr(TerminalRepr): + # Provided by subclasses. + reprcrash: Optional["ReprFileLocation"] + reprtraceback: "ReprTraceback" + + def __attrs_post_init__(self) -> None: + self.sections: List[Tuple[str, str, str]] = [] + + def addsection(self, name: str, content: str, sep: str = "-") -> None: + self.sections.append((name, content, sep)) + + def toterminal(self, tw: TerminalWriter) -> None: + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) + + +@attr.s(eq=False) +class ExceptionChainRepr(ExceptionRepr): + chain = attr.ib( + type=Sequence[ + Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]] + ] + ) + + def __attrs_post_init__(self) -> None: + super().__attrs_post_init__() + # reprcrash and reprtraceback of the outermost (the newest) exception + # in the chain. + self.reprtraceback = self.chain[-1][0] + self.reprcrash = self.chain[-1][1] + + def toterminal(self, tw: TerminalWriter) -> None: + for element in self.chain: + element[0].toterminal(tw) + if element[2] is not None: + tw.line("") + tw.line(element[2], yellow=True) + super().toterminal(tw) + + +@attr.s(eq=False) +class ReprExceptionInfo(ExceptionRepr): + reprtraceback = attr.ib(type="ReprTraceback") + reprcrash = attr.ib(type="ReprFileLocation") + + def toterminal(self, tw: TerminalWriter) -> None: + self.reprtraceback.toterminal(tw) + super().toterminal(tw) + + +@attr.s(eq=False) +class ReprTraceback(TerminalRepr): + reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]]) + extraline = attr.ib(type=Optional[str]) + style = attr.ib(type="_TracebackStyle") + + entrysep = "_ " + + def toterminal(self, tw: TerminalWriter) -> None: + # The entries might have different styles. + for i, entry in enumerate(self.reprentries): + if entry.style == "long": + tw.line("") + entry.toterminal(tw) + if i < len(self.reprentries) - 1: + next_entry = self.reprentries[i + 1] + if ( + entry.style == "long" + or entry.style == "short" + and next_entry.style == "long" + ): + tw.sep(self.entrysep) + + if self.extraline: + tw.line(self.extraline) + + +class ReprTracebackNative(ReprTraceback): + def __init__(self, tblines: Sequence[str]) -> None: + self.style = "native" + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + + +@attr.s(eq=False) +class ReprEntryNative(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + style: "_TracebackStyle" = "native" + + def toterminal(self, tw: TerminalWriter) -> None: + tw.write("".join(self.lines)) + + +@attr.s(eq=False) +class ReprEntry(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"]) + reprlocals = attr.ib(type=Optional["ReprLocals"]) + reprfileloc = attr.ib(type=Optional["ReprFileLocation"]) + style = attr.ib(type="_TracebackStyle") + + def _write_entry_lines(self, tw: TerminalWriter) -> None: + """Write the source code portions of a list of traceback entries with syntax highlighting. + + Usually entries are lines like these: + + " x = 1" + "> assert x == 2" + "E assert 1 == 2" + + This function takes care of rendering the "source" portions of it (the lines without + the "E" prefix) using syntax highlighting, taking care to not highlighting the ">" + character, as doing so might break line continuations. + """ + + if not self.lines: + return + + # separate indents and source lines that are not failures: we want to + # highlight the code but not the indentation, which may contain markers + # such as "> assert 0" + fail_marker = f"{FormattedExcinfo.fail_marker} " + indent_size = len(fail_marker) + indents: List[str] = [] + source_lines: List[str] = [] + failure_lines: List[str] = [] + for index, line in enumerate(self.lines): + is_failure_line = line.startswith(fail_marker) + if is_failure_line: + # from this point on all lines are considered part of the failure + failure_lines.extend(self.lines[index:]) + break + else: + if self.style == "value": + source_lines.append(line) + else: + indents.append(line[:indent_size]) + source_lines.append(line[indent_size:]) + + tw._write_source(source_lines, indents) + + # failure lines are always completely red and bold + for line in failure_lines: + tw.line(line, bold=True, red=True) + + def toterminal(self, tw: TerminalWriter) -> None: + if self.style == "short": + assert self.reprfileloc is not None + self.reprfileloc.toterminal(tw) + self._write_entry_lines(tw) + if self.reprlocals: + self.reprlocals.toterminal(tw, indent=" " * 8) + return + + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) + + self._write_entry_lines(tw) + + if self.reprlocals: + tw.line("") + self.reprlocals.toterminal(tw) + if self.reprfileloc: + if self.lines: + tw.line("") + self.reprfileloc.toterminal(tw) + + def __str__(self) -> str: + return "{}\n{}\n{}".format( + "\n".join(self.lines), self.reprlocals, self.reprfileloc + ) + + +@attr.s(eq=False) +class ReprFileLocation(TerminalRepr): + path = attr.ib(type=str, converter=str) + lineno = attr.ib(type=int) + message = attr.ib(type=str) + + def toterminal(self, tw: TerminalWriter) -> None: + # Filename and lineno output for each entry, using an output format + # that most editors understand. + msg = self.message + i = msg.find("\n") + if i != -1: + msg = msg[:i] + tw.write(self.path, bold=True, red=True) + tw.line(f":{self.lineno}: {msg}") + + +@attr.s(eq=False) +class ReprLocals(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + + def toterminal(self, tw: TerminalWriter, indent="") -> None: + for line in self.lines: + tw.line(indent + line) + + +@attr.s(eq=False) +class ReprFuncArgs(TerminalRepr): + args = attr.ib(type=Sequence[Tuple[str, object]]) + + def toterminal(self, tw: TerminalWriter) -> None: + if self.args: + linesofar = "" + for name, value in self.args: + ns = f"{name} = {value}" + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") + + +def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]: + """Return source location (path, lineno) for the given object. + + If the source cannot be determined return ("", -1). + + The line number is 0-based. + """ + # xxx let decorators etc specify a sane ordering + # NOTE: this used to be done in _pytest.compat.getfslineno, initially added + # in 6ec13a2b9. It ("place_as") appears to be something very custom. + obj = get_real_func(obj) + if hasattr(obj, "place_as"): + obj = obj.place_as # type: ignore[attr-defined] + + try: + code = Code.from_function(obj) + except TypeError: + try: + fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] + except TypeError: + return "", -1 + + fspath = fn and py.path.local(fn) or "" + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except OSError: + pass + return fspath, lineno + + return code.path, code.firstlineno + + +# Relative paths that we use to filter traceback entries from appearing to the user; +# see filter_traceback. +# note: if we need to add more paths than what we have now we should probably use a list +# for better maintenance. + +_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc")) +# pluggy is either a package or a single module depending on the version +if _PLUGGY_DIR.name == "__init__.py": + _PLUGGY_DIR = _PLUGGY_DIR.parent +_PYTEST_DIR = Path(_pytest.__file__).parent +_PY_DIR = Path(py.__file__).parent + + +def filter_traceback(entry: TracebackEntry) -> bool: + """Return True if a TracebackEntry instance should be included in tracebacks. + + We hide traceback entries of: + + * dynamically generated code (no code to show up for it); + * internal traceback from pytest or its internal libraries, py and pluggy. + """ + # entry.path might sometimes return a str object when the entry + # points to dynamically generated code. + # See https://bitbucket.org/pytest-dev/py/issues/71. + raw_filename = entry.frame.code.raw.co_filename + is_generated = "<" in raw_filename and ">" in raw_filename + if is_generated: + return False + + # entry.path might point to a non-existing file, in which case it will + # also return a str object. See #1133. + p = Path(entry.path) + + parents = p.parents + if _PLUGGY_DIR in parents: + return False + if _PYTEST_DIR in parents: + return False + if _PY_DIR in parents: + return False + + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/source.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/source.py new file mode 100644 index 0000000..6f54057 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_code/source.py @@ -0,0 +1,212 @@ +import ast +import inspect +import textwrap +import tokenize +import types +import warnings +from bisect import bisect_right +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Tuple +from typing import Union + + +class Source: + """An immutable object holding a source code fragment. + + When using Source(...), the source lines are deindented. + """ + + def __init__(self, obj: object = None) -> None: + if not obj: + self.lines: List[str] = [] + elif isinstance(obj, Source): + self.lines = obj.lines + elif isinstance(obj, (tuple, list)): + self.lines = deindent(x.rstrip("\n") for x in obj) + elif isinstance(obj, str): + self.lines = deindent(obj.split("\n")) + else: + try: + rawcode = getrawcode(obj) + src = inspect.getsource(rawcode) + except TypeError: + src = inspect.getsource(obj) # type: ignore[arg-type] + self.lines = deindent(src.split("\n")) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Source): + return NotImplemented + return self.lines == other.lines + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @overload + def __getitem__(self, key: int) -> str: + ... + + @overload + def __getitem__(self, key: slice) -> "Source": + ... + + def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]: + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + newsource = Source() + newsource.lines = self.lines[key.start : key.stop] + return newsource + + def __iter__(self) -> Iterator[str]: + return iter(self.lines) + + def __len__(self) -> int: + return len(self.lines) + + def strip(self) -> "Source": + """Return new Source object with trailing and leading blank lines removed.""" + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end - 1].strip(): + end -= 1 + source = Source() + source.lines[:] = self.lines[start:end] + return source + + def indent(self, indent: str = " " * 4) -> "Source": + """Return a copy of the source object with all lines indented by the + given indent-string.""" + newsource = Source() + newsource.lines = [(indent + line) for line in self.lines] + return newsource + + def getstatement(self, lineno: int) -> "Source": + """Return Source statement which contains the given linenumber + (counted from 0).""" + start, end = self.getstatementrange(lineno) + return self[start:end] + + def getstatementrange(self, lineno: int) -> Tuple[int, int]: + """Return (start, end) tuple which spans the minimal statement region + which containing the given lineno.""" + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self) -> "Source": + """Return a new Source object deindented.""" + newsource = Source() + newsource.lines[:] = deindent(self.lines) + return newsource + + def __str__(self) -> str: + return "\n".join(self.lines) + + +# +# helper functions +# + + +def findsource(obj) -> Tuple[Optional[Source], int]: + try: + sourcelines, lineno = inspect.findsource(obj) + except Exception: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + return source, lineno + + +def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: + """Return code object for given function.""" + try: + return obj.__code__ # type: ignore[attr-defined,no-any-return] + except AttributeError: + pass + if trycall: + call = getattr(obj, "__call__", None) + if call and not isinstance(obj, type): + return getrawcode(call, trycall=False) + raise TypeError(f"could not get code object for {obj!r}") + + +def deindent(lines: Iterable[str]) -> List[str]: + return textwrap.dedent("\n".join(lines)).splitlines() + + +def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]: + # Flatten all statements and except handlers into one lineno-list. + # AST's line numbers start indexing at 1. + values: List[int] = [] + for x in ast.walk(node): + if isinstance(x, (ast.stmt, ast.ExceptHandler)): + values.append(x.lineno - 1) + for name in ("finalbody", "orelse"): + val: Optional[List[ast.stmt]] = getattr(x, name, None) + if val: + # Treat the finally/orelse part as its own statement. + values.append(val[0].lineno - 1 - 1) + values.sort() + insert_index = bisect_right(values, lineno) + start = values[insert_index - 1] + if insert_index >= len(values): + end = None + else: + end = values[insert_index] + return start, end + + +def getstatementrange_ast( + lineno: int, + source: Source, + assertion: bool = False, + astnode: Optional[ast.AST] = None, +) -> Tuple[ast.AST, int, int]: + if astnode is None: + content = str(source) + # See #4260: + # Don't produce duplicate warnings when compiling source to find AST. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + astnode = ast.parse(content, "source", "exec") + + start, end = get_statement_startend2(lineno, astnode) + # We need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # Make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself. + block_finder = inspect.BlockFinder() + # If we start with an indented line, put blockfinder to "started" mode. + block_finder.started = source.lines[start][0].isspace() + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # The end might still point to a comment or empty line, correct it. + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__init__.py new file mode 100644 index 0000000..db001e9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__init__.py @@ -0,0 +1,8 @@ +from .terminalwriter import get_terminal_width +from .terminalwriter import TerminalWriter + + +__all__ = [ + "TerminalWriter", + "get_terminal_width", +] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e458118750b835405f7db372875b1fe31c565b1 GIT binary patch literal 346 zcmYk1u};G<5Qbwng$lJDcm|dXsRt$`gcz7vC_>6|GVv)fICijIN;jT^H{g}BGVux^ zAqaUwK&gq0BYARFAWF4y*OPZ7B6(>DWdBdxT znqFna-aDgc!G^;IBSiF_w$i{>tJo4Ca(CZO-O7XZ-6M Dp4wqT literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/saferepr.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/saferepr.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be78f725edc49d99c73e456a2475cadb00e3e40e GIT binary patch literal 4438 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 Sza8KC*0ZLCi5(dCpZ^8@N9~OO literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f57d6bc319dcb3629235a3aff4addd24c051c6e GIT binary patch literal 5987 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 RM+f#%I=0)D@_0pO{{ej^pt%45 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b548d4338c36304a5f8026b1dfa8a583ec75a2c9 GIT binary patch literal 1332 zcmbVM&x;#36xJ`>v)=5kNt;qa4GsfAFOQ_8I-sqe6o-|LNEVj1V2*&OFnRv<& zdJ-pR2XN>Hn2(^4!Z{j5PjWmV1A>tzik@OkHB;U!8#EQ( zN8R=ks-Rn4TyErUI+Q|=^il!UB_)<+)9EmEM&cN?8WeoAT`>O-KtKz0iche@3(Vlv z7`smt86)?pVHg>v=ssFt#m2-JsPPuTJd1H%6yTFITu`xgcB0dcxGT)=Zn1GZgUtvR88`{ca3LwD4gl zd?w6rmZ>o6QWsTMV(U8K|3>ZuaQ{6>?|AKe3e7R%$QmWmJu9aZZ32*1r}sn;Tu=X6 zXqVM=F%KGw7ta2oWk~C3Pn-K`o*l&L$eJ(v*HfPb-7AC~izDsRVI7waGn>w2wWoeD zFI%hU2JFUv1%O%!{TMUWd_@R*nGo_aq3lIM$%}-s=LsXv+f7E=G{LO0ZOn+f0C}-J z-o-j^Nj-jbv`XsVz=Hyr2lNQ5C_b`>e!6r*0!R?#PZv~S5dG&M=SuJk`ZYSi_d1Js z&)D(r|3h{@fNYN5Q**0eX+dZiXxUiPTg&bpRaG)7>8+c)F8<%Z5$}L2H;i=?k!Q!c z?hA7tMY>V-yi&n{%w%=`HGCLfCo}1*h$}Gj+W@G+a0e&wYvML`7lG-vR!baCjSSZ2 s-bFE%6gFN`xE|GtRp^=eQE9GT str: + try: + return repr(obj) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + return '{}("{}")'.format(type(obj).__name__, obj) + + +def _format_repr_exception(exc: BaseException, obj: object) -> str: + try: + exc_info = _try_repr_or_str(exc) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + exc_info = "unpresentable exception ({})".format(_try_repr_or_str(exc)) + return "<[{} raised in repr()] {} object at 0x{:x}>".format( + exc_info, type(obj).__name__, id(obj) + ) + + +def _ellipsize(s: str, maxsize: int) -> str: + if len(s) > maxsize: + i = max(0, (maxsize - 3) // 2) + j = max(0, maxsize - 3 - i) + return s[:i] + "..." + s[len(s) - j :] + return s + + +class SafeRepr(reprlib.Repr): + """repr.Repr that limits the resulting size of repr() and includes + information on exceptions raised during the call.""" + + def __init__(self, maxsize: int) -> None: + super().__init__() + self.maxstring = maxsize + self.maxsize = maxsize + + def repr(self, x: object) -> str: + try: + s = super().repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + return _ellipsize(s, self.maxsize) + + def repr_instance(self, x: object, level: int) -> str: + try: + s = repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + return _ellipsize(s, self.maxsize) + + +def safeformat(obj: object) -> str: + """Return a pretty printed string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info. + """ + try: + return pprint.pformat(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) + + +def saferepr(obj: object, maxsize: int = 240) -> str: + """Return a size-limited safe repr-string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. + + This function is a wrapper around the Repr/reprlib functionality of the + standard 2.6 lib. + """ + return SafeRepr(maxsize).repr(obj) + + +class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter): + """PrettyPrinter that always dispatches (regardless of width).""" + + def _format( + self, + object: object, + stream: IO[str], + indent: int, + allowance: int, + context: Dict[int, Any], + level: int, + ) -> None: + # Type ignored because _dispatch is private. + p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined] + + objid = id(object) + if objid in context or p is None: + # Type ignored because _format is private. + super()._format( # type: ignore[misc] + object, stream, indent, allowance, context, level, + ) + return + + context[objid] = 1 + p(self, object, stream, indent, allowance, context, level + 1) + del context[objid] + + +def _pformat_dispatch( + object: object, + indent: int = 1, + width: int = 80, + depth: Optional[int] = None, + *, + compact: bool = False, +) -> str: + return AlwaysDispatchingPrettyPrinter( + indent=indent, width=width, depth=depth, compact=compact + ).pformat(object) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/terminalwriter.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/terminalwriter.py new file mode 100644 index 0000000..8edf4cd --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/terminalwriter.py @@ -0,0 +1,210 @@ +"""Helper functions for writing to terminals and files.""" +import os +import shutil +import sys +from typing import Optional +from typing import Sequence +from typing import TextIO + +from .wcwidth import wcswidth +from _pytest.compat import final + + +# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. + + +def get_terminal_width() -> int: + width, _ = shutil.get_terminal_size(fallback=(80, 24)) + + # The Windows get_terminal_size may be bogus, let's sanify a bit. + if width < 40: + width = 80 + + return width + + +def should_do_markup(file: TextIO) -> bool: + if os.environ.get("PY_COLORS") == "1": + return True + if os.environ.get("PY_COLORS") == "0": + return False + if "NO_COLOR" in os.environ: + return False + if "FORCE_COLOR" in os.environ: + return True + return ( + hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" + ) + + +@final +class TerminalWriter: + _esctable = dict( + black=30, + red=31, + green=32, + yellow=33, + blue=34, + purple=35, + cyan=36, + white=37, + Black=40, + Red=41, + Green=42, + Yellow=43, + Blue=44, + Purple=45, + Cyan=46, + White=47, + bold=1, + light=2, + blink=5, + invert=7, + ) + + def __init__(self, file: Optional[TextIO] = None) -> None: + if file is None: + file = sys.stdout + if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32": + try: + import colorama + except ImportError: + pass + else: + file = colorama.AnsiToWin32(file).stream + assert file is not None + self._file = file + self.hasmarkup = should_do_markup(file) + self._current_line = "" + self._terminal_width: Optional[int] = None + self.code_highlight = True + + @property + def fullwidth(self) -> int: + if self._terminal_width is not None: + return self._terminal_width + return get_terminal_width() + + @fullwidth.setter + def fullwidth(self, value: int) -> None: + self._terminal_width = value + + @property + def width_of_current_line(self) -> int: + """Return an estimate of the width so far in the current line.""" + return wcswidth(self._current_line) + + def markup(self, text: str, **markup: bool) -> str: + for name in markup: + if name not in self._esctable: + raise ValueError(f"unknown markup: {name!r}") + if self.hasmarkup: + esc = [self._esctable[name] for name, on in markup.items() if on] + if esc: + text = "".join("\x1b[%sm" % cod for cod in esc) + text + "\x1b[0m" + return text + + def sep( + self, + sepchar: str, + title: Optional[str] = None, + fullwidth: Optional[int] = None, + **markup: bool, + ) -> None: + if fullwidth is None: + fullwidth = self.fullwidth + # The goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth. + if sys.platform == "win32": + # If we print in the last column on windows we are on a + # new line but there is no way to verify/neutralize this + # (we may not know the exact line width). + # So let's be defensive to avoid empty lines in the output. + fullwidth -= 1 + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) + fill = sepchar * N + line = f"{fill} {title} {fill}" + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # In some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line. + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + + self.line(line, **markup) + + def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: + if msg: + current_line = msg.rsplit("\n", 1)[-1] + if "\n" in msg: + self._current_line = current_line + else: + self._current_line += current_line + + msg = self.markup(msg, **markup) + + try: + self._file.write(msg) + except UnicodeEncodeError: + # Some environments don't support printing general Unicode + # strings, due to misconfiguration or otherwise; in that case, + # print the string escaped to ASCII. + # When the Unicode situation improves we should consider + # letting the error propagate instead of masking it (see #7475 + # for one brief attempt). + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) + + if flush: + self.flush() + + def line(self, s: str = "", **markup: bool) -> None: + self.write(s, **markup) + self.write("\n") + + def flush(self) -> None: + self._file.flush() + + def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: + """Write lines of source code possibly highlighted. + + Keeping this private for now because the API is clunky. We should discuss how + to evolve the terminal writer so we can have more precise color support, for example + being able to write part of a line in one color and the rest in another, and so on. + """ + if indents and len(indents) != len(lines): + raise ValueError( + "indents size ({}) should have same size as lines ({})".format( + len(indents), len(lines) + ) + ) + if not indents: + indents = [""] * len(lines) + source = "\n".join(lines) + new_lines = self._highlight(source).splitlines() + for indent, new_line in zip(indents, new_lines): + self.line(indent + new_line) + + def _highlight(self, source: str) -> str: + """Highlight the given source code if we have markup support.""" + if not self.hasmarkup or not self.code_highlight: + return source + try: + from pygments.formatters.terminal import TerminalFormatter + from pygments.lexers.python import PythonLexer + from pygments import highlight + except ImportError: + return source + else: + highlighted: str = highlight( + source, PythonLexer(), TerminalFormatter(bg="dark") + ) + return highlighted diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/wcwidth.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/wcwidth.py new file mode 100644 index 0000000..e5c7bf4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_io/wcwidth.py @@ -0,0 +1,55 @@ +import unicodedata +from functools import lru_cache + + +@lru_cache(100) +def wcwidth(c: str) -> int: + """Determine how many columns are needed to display a character in a terminal. + + Returns -1 if the character is not printable. + Returns 0, 1 or 2 for other characters. + """ + o = ord(c) + + # ASCII fast path. + if 0x20 <= o < 0x07F: + return 1 + + # Some Cf/Zp/Zl characters which should be zero-width. + if ( + o == 0x0000 + or 0x200B <= o <= 0x200F + or 0x2028 <= o <= 0x202E + or 0x2060 <= o <= 0x2063 + ): + return 0 + + category = unicodedata.category(c) + + # Control characters. + if category == "Cc": + return -1 + + # Combining characters with zero width. + if category in ("Me", "Mn"): + return 0 + + # Full/Wide east asian characters. + if unicodedata.east_asian_width(c) in ("F", "W"): + return 2 + + return 1 + + +def wcswidth(s: str) -> int: + """Determine how many columns are needed to display a string in a terminal. + + Returns -1 if the string contains non-printable characters. + """ + width = 0 + for c in unicodedata.normalize("NFC", s): + wc = wcwidth(c) + if wc < 0: + return -1 + width += wc + return width diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_version.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_version.py new file mode 100644 index 0000000..8351858 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/_version.py @@ -0,0 +1,5 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '6.2.5' +version_tuple = (6, 2, 5) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__init__.py new file mode 100644 index 0000000..a18cf19 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,179 @@ +"""Support for presenting detailed information in failing assertions.""" +import sys +from typing import Any +from typing import Generator +from typing import List +from typing import Optional +from typing import TYPE_CHECKING + +from _pytest.assertion import rewrite +from _pytest.assertion import truncate +from _pytest.assertion import util +from _pytest.assertion.rewrite import assertstate_key +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item + +if TYPE_CHECKING: + from _pytest.main import Session + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--assert", + action="store", + dest="assertmode", + choices=("rewrite", "plain"), + default="rewrite", + metavar="MODE", + help=( + "Control assertion debugging tools.\n" + "'plain' performs no assertion debugging.\n" + "'rewrite' (the default) rewrites assert statements in test modules" + " on import to provide assert expression information." + ), + ) + parser.addini( + "enable_assertion_pass_hook", + type="bool", + default=False, + help="Enables the pytest_assertion_pass hook." + "Make sure to delete any previously generated pyc cache files.", + ) + + +def register_assert_rewrite(*names: str) -> None: + """Register one or more module names to be rewritten on import. + + This function will make sure that this module or all modules inside + the package will get their assert statements rewritten. + Thus you should make sure to call this before the module is + actually imported, usually in your __init__.py if you are a plugin + using a package. + + :raises TypeError: If the given module names are not strings. + """ + for name in names: + if not isinstance(name, str): + msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] + raise TypeError(msg.format(repr(names))) + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + importhook = hook + break + else: + # TODO(typing): Add a protocol for mark_rewrite() and use it + # for importhook and for PytestPluginManager.rewrite_hook. + importhook = DummyRewriteHook() # type: ignore + importhook.mark_rewrite(*names) + + +class DummyRewriteHook: + """A no-op import hook for when rewriting is disabled.""" + + def mark_rewrite(self, *names: str) -> None: + pass + + +class AssertionState: + """State for the assertion plugin.""" + + def __init__(self, config: Config, mode) -> None: + self.mode = mode + self.trace = config.trace.root.get("assertion") + self.hook: Optional[rewrite.AssertionRewritingHook] = None + + +def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: + """Try to install the rewrite hook, raise SystemError if it fails.""" + config._store[assertstate_key] = AssertionState(config, "rewrite") + config._store[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config._store[assertstate_key].trace("installed rewrite import hook") + + def undo() -> None: + hook = config._store[assertstate_key].hook + if hook is not None and hook in sys.meta_path: + sys.meta_path.remove(hook) + + config.add_cleanup(undo) + return hook + + +def pytest_collection(session: "Session") -> None: + # This hook is only called when test modules are collected + # so for example not in the master process of pytest-xdist + # (which does not collect test modules). + assertstate = session.config._store.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +@hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks. + + The rewrite module will use util._reprcompare if it exists to use custom + reporting via the pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ + + ihook = item.ihook + + def callbinrepr(op, left: object, right: object) -> Optional[str]: + """Call the pytest_assertrepr_compare hook and prepare the result. + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are truncated unless configured otherwise + (eg. if running in verbose mode). + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ + hook_result = ihook.pytest_assertrepr_compare( + config=item.config, op=op, left=left, right=right + ) + for new_expl in hook_result: + if new_expl: + new_expl = truncate.truncate_if_required(new_expl, item) + new_expl = [line.replace("\n", "\\n") for line in new_expl] + res = "\n~".join(new_expl) + if item.config.getvalue("assertmode") == "rewrite": + res = res.replace("%", "%%") + return res + return None + + saved_assert_hooks = util._reprcompare, util._assertion_pass + util._reprcompare = callbinrepr + + if ihook.pytest_assertion_pass.get_hookimpls(): + + def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: + ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl) + + util._assertion_pass = call_assertion_pass_hook + + yield + + util._reprcompare, util._assertion_pass = saved_assert_hooks + + +def pytest_sessionfinish(session: "Session") -> None: + assertstate = session.config._store.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(None) + + +def pytest_assertrepr_compare( + config: Config, op: str, left: Any, right: Any +) -> Optional[List[str]]: + return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b9a37d7dec54afe0eb131c4529fc2d0201781f8 GIT binary patch literal 6662 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48f22783edcf9fc9d303bcfacb7a14fea73dc227 GIT binary patch literal 32989 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 oHX657p>YSU{}kVF2>s5kk2RsZ3C0mt?IssI20 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/util.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db3459749f65ee671a59778bc18944de3937f885 GIT binary patch literal 12720 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/rewrite.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/rewrite.py new file mode 100644 index 0000000..37ff076 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/rewrite.py @@ -0,0 +1,1125 @@ +"""Rewrite assertion AST to produce nice error messages.""" +import ast +import errno +import functools +import importlib.abc +import importlib.machinery +import importlib.util +import io +import itertools +import marshal +import os +import struct +import sys +import tokenize +import types +from pathlib import Path +from pathlib import PurePath +from typing import Callable +from typing import Dict +from typing import IO +from typing import Iterable +from typing import List +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import py + +from _pytest._io.saferepr import saferepr +from _pytest._version import version +from _pytest.assertion import util +from _pytest.assertion.util import ( # noqa: F401 + format_explanation as _format_explanation, +) +from _pytest.config import Config +from _pytest.main import Session +from _pytest.pathlib import fnmatch_ex +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from _pytest.assertion import AssertionState + + +assertstate_key = StoreKey["AssertionState"]() + + +# pytest caches rewritten pycs in pycache dirs +PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" +PYC_EXT = ".py" + (__debug__ and "c" or "o") +PYC_TAIL = "." + PYTEST_TAG + PYC_EXT + + +class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): + """PEP302/PEP451 import hook which rewrites asserts.""" + + def __init__(self, config: Config) -> None: + self.config = config + try: + self.fnpats = config.getini("python_files") + except ValueError: + self.fnpats = ["test_*.py", "*_test.py"] + self.session: Optional[Session] = None + self._rewritten_names: Set[str] = set() + self._must_rewrite: Set[str] = set() + # flag to guard against trying to rewrite a pyc file while we are already writing another pyc file, + # which might result in infinite recursion (#3506) + self._writing_pyc = False + self._basenames_to_check_rewrite = {"conftest"} + self._marked_for_rewrite_cache: Dict[str, bool] = {} + self._session_paths_checked = False + + def set_session(self, session: Optional[Session]) -> None: + self.session = session + self._session_paths_checked = False + + # Indirection so we can mock calls to find_spec originated from the hook during testing + _find_spec = importlib.machinery.PathFinder.find_spec + + def find_spec( + self, + name: str, + path: Optional[Sequence[Union[str, bytes]]] = None, + target: Optional[types.ModuleType] = None, + ) -> Optional[importlib.machinery.ModuleSpec]: + if self._writing_pyc: + return None + state = self.config._store[assertstate_key] + if self._early_rewrite_bailout(name, state): + return None + state.trace("find_module called for: %s" % name) + + # Type ignored because mypy is confused about the `self` binding here. + spec = self._find_spec(name, path) # type: ignore + if ( + # the import machinery could not find a file to import + spec is None + # this is a namespace package (without `__init__.py`) + # there's nothing to rewrite there + # python3.6: `namespace` + # python3.7+: `None` + or spec.origin == "namespace" + or spec.origin is None + # we can only rewrite source files + or not isinstance(spec.loader, importlib.machinery.SourceFileLoader) + # if the file doesn't exist, we can't rewrite it + or not os.path.exists(spec.origin) + ): + return None + else: + fn = spec.origin + + if not self._should_rewrite(name, fn, state): + return None + + return importlib.util.spec_from_file_location( + name, + fn, + loader=self, + submodule_search_locations=spec.submodule_search_locations, + ) + + def create_module( + self, spec: importlib.machinery.ModuleSpec + ) -> Optional[types.ModuleType]: + return None # default behaviour is fine + + def exec_module(self, module: types.ModuleType) -> None: + assert module.__spec__ is not None + assert module.__spec__.origin is not None + fn = Path(module.__spec__.origin) + state = self.config._store[assertstate_key] + + self._rewritten_names.add(module.__name__) + + # The requested module looks like a test file, so rewrite it. This is + # the most magical part of the process: load the source, rewrite the + # asserts, and load the rewritten source. We also cache the rewritten + # module code in a special pyc. We must be aware of the possibility of + # concurrent pytest processes rewriting and loading pycs. To avoid + # tricky race conditions, we maintain the following invariant: The + # cached pyc is always a complete, valid pyc. Operations on it must be + # atomic. POSIX's atomic rename comes in handy. + write = not sys.dont_write_bytecode + cache_dir = get_cache_dir(fn) + if write: + ok = try_makedirs(cache_dir) + if not ok: + write = False + state.trace(f"read only directory: {cache_dir}") + + cache_name = fn.name[:-3] + PYC_TAIL + pyc = cache_dir / cache_name + # Notice that even if we're in a read-only directory, I'm going + # to check for a cached pyc. This may not be optimal... + co = _read_pyc(fn, pyc, state.trace) + if co is None: + state.trace(f"rewriting {fn!r}") + source_stat, co = _rewrite_test(fn, self.config) + if write: + self._writing_pyc = True + try: + _write_pyc(state, co, source_stat, pyc) + finally: + self._writing_pyc = False + else: + state.trace(f"found cached rewritten pyc for {fn}") + exec(co, module.__dict__) + + def _early_rewrite_bailout(self, name: str, state: "AssertionState") -> bool: + """A fast way to get out of rewriting modules. + + Profiling has shown that the call to PathFinder.find_spec (inside of + the find_spec from this class) is a major slowdown, so, this method + tries to filter what we're sure won't be rewritten before getting to + it. + """ + if self.session is not None and not self._session_paths_checked: + self._session_paths_checked = True + for initial_path in self.session._initialpaths: + # Make something as c:/projects/my_project/path.py -> + # ['c:', 'projects', 'my_project', 'path.py'] + parts = str(initial_path).split(os.path.sep) + # add 'path' to basenames to be checked. + self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0]) + + # Note: conftest already by default in _basenames_to_check_rewrite. + parts = name.split(".") + if parts[-1] in self._basenames_to_check_rewrite: + return False + + # For matching the name it must be as if it was a filename. + path = PurePath(os.path.sep.join(parts) + ".py") + + for pat in self.fnpats: + # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based + # on the name alone because we need to match against the full path + if os.path.dirname(pat): + return False + if fnmatch_ex(pat, path): + return False + + if self._is_marked_for_rewrite(name, state): + return False + + state.trace(f"early skip of rewriting module: {name}") + return True + + def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: + # always rewrite conftest files + if os.path.basename(fn) == "conftest.py": + state.trace(f"rewriting conftest file: {fn!r}") + return True + + if self.session is not None: + if self.session.isinitpath(py.path.local(fn)): + state.trace(f"matched test file (was specified on cmdline): {fn!r}") + return True + + # modules not passed explicitly on the command line are only + # rewritten if they match the naming convention for test files + fn_path = PurePath(fn) + for pat in self.fnpats: + if fnmatch_ex(pat, fn_path): + state.trace(f"matched test file {fn!r}") + return True + + return self._is_marked_for_rewrite(name, state) + + def _is_marked_for_rewrite(self, name: str, state: "AssertionState") -> bool: + try: + return self._marked_for_rewrite_cache[name] + except KeyError: + for marked in self._must_rewrite: + if name == marked or name.startswith(marked + "."): + state.trace(f"matched marked file {name!r} (from {marked!r})") + self._marked_for_rewrite_cache[name] = True + return True + + self._marked_for_rewrite_cache[name] = False + return False + + def mark_rewrite(self, *names: str) -> None: + """Mark import names as needing to be rewritten. + + The named module or package as well as any nested modules will + be rewritten on import. + """ + already_imported = ( + set(names).intersection(sys.modules).difference(self._rewritten_names) + ) + for name in already_imported: + mod = sys.modules[name] + if not AssertionRewriter.is_rewrite_disabled( + mod.__doc__ or "" + ) and not isinstance(mod.__loader__, type(self)): + self._warn_already_imported(name) + self._must_rewrite.update(names) + self._marked_for_rewrite_cache.clear() + + def _warn_already_imported(self, name: str) -> None: + from _pytest.warning_types import PytestAssertRewriteWarning + + self.config.issue_config_time_warning( + PytestAssertRewriteWarning( + "Module already imported so cannot be rewritten: %s" % name + ), + stacklevel=5, + ) + + def get_data(self, pathname: Union[str, bytes]) -> bytes: + """Optional PEP302 get_data API.""" + with open(pathname, "rb") as f: + return f.read() + + +def _write_pyc_fp( + fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType +) -> None: + # Technically, we don't have to have the same pyc format as + # (C)Python, since these "pycs" should never be seen by builtin + # import. However, there's little reason to deviate. + fp.write(importlib.util.MAGIC_NUMBER) + # https://www.python.org/dev/peps/pep-0552/ + if sys.version_info >= (3, 7): + flags = b"\x00\x00\x00\x00" + fp.write(flags) + # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) + mtime = int(source_stat.st_mtime) & 0xFFFFFFFF + size = source_stat.st_size & 0xFFFFFFFF + # " bool: + try: + with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp: + _write_pyc_fp(fp, source_stat, co) + except OSError as e: + state.trace(f"error writing pyc file at {pyc}: {e}") + # we ignore any failure to write the cache file + # there are many reasons, permission-denied, pycache dir being a + # file etc. + return False + return True + + +else: + + def _write_pyc( + state: "AssertionState", + co: types.CodeType, + source_stat: os.stat_result, + pyc: Path, + ) -> bool: + proc_pyc = f"{pyc}.{os.getpid()}" + try: + fp = open(proc_pyc, "wb") + except OSError as e: + state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") + return False + + try: + _write_pyc_fp(fp, source_stat, co) + os.rename(proc_pyc, os.fspath(pyc)) + except OSError as e: + state.trace(f"error writing pyc file at {pyc}: {e}") + # we ignore any failure to write the cache file + # there are many reasons, permission-denied, pycache dir being a + # file etc. + return False + finally: + fp.close() + return True + + +def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]: + """Read and rewrite *fn* and return the code object.""" + fn_ = os.fspath(fn) + stat = os.stat(fn_) + with open(fn_, "rb") as f: + source = f.read() + tree = ast.parse(source, filename=fn_) + rewrite_asserts(tree, source, fn_, config) + co = compile(tree, fn_, "exec", dont_inherit=True) + return stat, co + + +def _read_pyc( + source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None +) -> Optional[types.CodeType]: + """Possibly read a pytest pyc containing rewritten code. + + Return rewritten code if successful or None if not. + """ + try: + fp = open(os.fspath(pyc), "rb") + except OSError: + return None + with fp: + # https://www.python.org/dev/peps/pep-0552/ + has_flags = sys.version_info >= (3, 7) + try: + stat_result = os.stat(os.fspath(source)) + mtime = int(stat_result.st_mtime) + size = stat_result.st_size + data = fp.read(16 if has_flags else 12) + except OSError as e: + trace(f"_read_pyc({source}): OSError {e}") + return None + # Check for invalid or out of date pyc file. + if len(data) != (16 if has_flags else 12): + trace("_read_pyc(%s): invalid pyc (too short)" % source) + return None + if data[:4] != importlib.util.MAGIC_NUMBER: + trace("_read_pyc(%s): invalid pyc (bad magic number)" % source) + return None + if has_flags and data[4:8] != b"\x00\x00\x00\x00": + trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source) + return None + mtime_data = data[8 if has_flags else 4 : 12 if has_flags else 8] + if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: + trace("_read_pyc(%s): out of date" % source) + return None + size_data = data[12 if has_flags else 8 : 16 if has_flags else 12] + if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: + trace("_read_pyc(%s): invalid pyc (incorrect size)" % source) + return None + try: + co = marshal.load(fp) + except Exception as e: + trace(f"_read_pyc({source}): marshal.load error {e}") + return None + if not isinstance(co, types.CodeType): + trace("_read_pyc(%s): not a code object" % source) + return None + return co + + +def rewrite_asserts( + mod: ast.Module, + source: bytes, + module_path: Optional[str] = None, + config: Optional[Config] = None, +) -> None: + """Rewrite the assert statements in mod.""" + AssertionRewriter(module_path, config, source).run(mod) + + +def _saferepr(obj: object) -> str: + r"""Get a safe repr of an object for assertion error messages. + + The assertion formatting (util.format_explanation()) requires + newlines to be escaped since they are a special character for it. + Normally assertion.util.format_explanation() does this but for a + custom repr it is possible to contain one of the special escape + sequences, especially '\n{' and '\n}' are likely to be present in + JSON reprs. + """ + return saferepr(obj).replace("\n", "\\n") + + +def _format_assertmsg(obj: object) -> str: + r"""Format the custom assertion message given. + + For strings this simply replaces newlines with '\n~' so that + util.format_explanation() will preserve them instead of escaping + newlines. For other objects saferepr() is used first. + """ + # reprlib appears to have a bug which means that if a string + # contains a newline it gets escaped, however if an object has a + # .__repr__() which contains newlines it does not get escaped. + # However in either case we want to preserve the newline. + replaces = [("\n", "\n~"), ("%", "%%")] + if not isinstance(obj, str): + obj = saferepr(obj) + replaces.append(("\\n", "\n~")) + + for r1, r2 in replaces: + obj = obj.replace(r1, r2) + + return obj + + +def _should_repr_global_name(obj: object) -> bool: + if callable(obj): + return False + + try: + return not hasattr(obj, "__name__") + except Exception: + return True + + +def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: + explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" + return explanation.replace("%", "%%") + + +def _call_reprcompare( + ops: Sequence[str], + results: Sequence[bool], + expls: Sequence[str], + each_obj: Sequence[object], +) -> str: + for i, res, expl in zip(range(len(ops)), results, expls): + try: + done = not res + except Exception: + done = True + if done: + break + if util._reprcompare is not None: + custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1]) + if custom is not None: + return custom + return expl + + +def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None: + if util._assertion_pass is not None: + util._assertion_pass(lineno, orig, expl) + + +def _check_if_assertion_pass_impl() -> bool: + """Check if any plugins implement the pytest_assertion_pass hook + in order not to generate explanation unecessarily (might be expensive).""" + return True if util._assertion_pass else False + + +UNARY_MAP = {ast.Not: "not %s", ast.Invert: "~%s", ast.USub: "-%s", ast.UAdd: "+%s"} + +BINOP_MAP = { + ast.BitOr: "|", + ast.BitXor: "^", + ast.BitAnd: "&", + ast.LShift: "<<", + ast.RShift: ">>", + ast.Add: "+", + ast.Sub: "-", + ast.Mult: "*", + ast.Div: "/", + ast.FloorDiv: "//", + ast.Mod: "%%", # escaped for string formatting + ast.Eq: "==", + ast.NotEq: "!=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.Pow: "**", + ast.Is: "is", + ast.IsNot: "is not", + ast.In: "in", + ast.NotIn: "not in", + ast.MatMult: "@", +} + + +def set_location(node, lineno, col_offset): + """Set node location information recursively.""" + + def _fix(node, lineno, col_offset): + if "lineno" in node._attributes: + node.lineno = lineno + if "col_offset" in node._attributes: + node.col_offset = col_offset + for child in ast.iter_child_nodes(node): + _fix(child, lineno, col_offset) + + _fix(node, lineno, col_offset) + return node + + +def _get_assertion_exprs(src: bytes) -> Dict[int, str]: + """Return a mapping from {lineno: "assertion test expression"}.""" + ret: Dict[int, str] = {} + + depth = 0 + lines: List[str] = [] + assert_lineno: Optional[int] = None + seen_lines: Set[int] = set() + + def _write_and_reset() -> None: + nonlocal depth, lines, assert_lineno, seen_lines + assert assert_lineno is not None + ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\") + depth = 0 + lines = [] + assert_lineno = None + seen_lines = set() + + tokens = tokenize.tokenize(io.BytesIO(src).readline) + for tp, source, (lineno, offset), _, line in tokens: + if tp == tokenize.NAME and source == "assert": + assert_lineno = lineno + elif assert_lineno is not None: + # keep track of depth for the assert-message `,` lookup + if tp == tokenize.OP and source in "([{": + depth += 1 + elif tp == tokenize.OP and source in ")]}": + depth -= 1 + + if not lines: + lines.append(line[offset:]) + seen_lines.add(lineno) + # a non-nested comma separates the expression from the message + elif depth == 0 and tp == tokenize.OP and source == ",": + # one line assert with message + if lineno in seen_lines and len(lines) == 1: + offset_in_trimmed = offset + len(lines[-1]) - len(line) + lines[-1] = lines[-1][:offset_in_trimmed] + # multi-line assert with message + elif lineno in seen_lines: + lines[-1] = lines[-1][:offset] + # multi line assert with escapd newline before message + else: + lines.append(line[:offset]) + _write_and_reset() + elif tp in {tokenize.NEWLINE, tokenize.ENDMARKER}: + _write_and_reset() + elif lines and lineno not in seen_lines: + lines.append(line) + seen_lines.add(lineno) + + return ret + + +class AssertionRewriter(ast.NodeVisitor): + """Assertion rewriting implementation. + + The main entrypoint is to call .run() with an ast.Module instance, + this will then find all the assert statements and rewrite them to + provide intermediate values and a detailed assertion error. See + http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html + for an overview of how this works. + + The entry point here is .run() which will iterate over all the + statements in an ast.Module and for each ast.Assert statement it + finds call .visit() with it. Then .visit_Assert() takes over and + is responsible for creating new ast statements to replace the + original assert statement: it rewrites the test of an assertion + to provide intermediate values and replace it with an if statement + which raises an assertion error with a detailed explanation in + case the expression is false and calls pytest_assertion_pass hook + if expression is true. + + For this .visit_Assert() uses the visitor pattern to visit all the + AST nodes of the ast.Assert.test field, each visit call returning + an AST node and the corresponding explanation string. During this + state is kept in several instance attributes: + + :statements: All the AST statements which will replace the assert + statement. + + :variables: This is populated by .variable() with each variable + used by the statements so that they can all be set to None at + the end of the statements. + + :variable_counter: Counter to create new unique variables needed + by statements. Variables are created using .variable() and + have the form of "@py_assert0". + + :expl_stmts: The AST statements which will be executed to get + data from the assertion. This is the code which will construct + the detailed assertion message that is used in the AssertionError + or for the pytest_assertion_pass hook. + + :explanation_specifiers: A dict filled by .explanation_param() + with %-formatting placeholders and their corresponding + expressions to use in the building of an assertion message. + This is used by .pop_format_context() to build a message. + + :stack: A stack of the explanation_specifiers dicts maintained by + .push_format_context() and .pop_format_context() which allows + to build another %-formatted string while already building one. + + This state is reset on every new assert statement visited and used + by the other visitors. + """ + + def __init__( + self, module_path: Optional[str], config: Optional[Config], source: bytes + ) -> None: + super().__init__() + self.module_path = module_path + self.config = config + if config is not None: + self.enable_assertion_pass_hook = config.getini( + "enable_assertion_pass_hook" + ) + else: + self.enable_assertion_pass_hook = False + self.source = source + + @functools.lru_cache(maxsize=1) + def _assert_expr_to_lineno(self) -> Dict[int, str]: + return _get_assertion_exprs(self.source) + + def run(self, mod: ast.Module) -> None: + """Find all assert statements in *mod* and rewrite them.""" + if not mod.body: + # Nothing to do. + return + + # We'll insert some special imports at the top of the module, but after any + # docstrings and __future__ imports, so first figure out where that is. + doc = getattr(mod, "docstring", None) + expect_docstring = doc is None + if doc is not None and self.is_rewrite_disabled(doc): + return + pos = 0 + lineno = 1 + for item in mod.body: + if ( + expect_docstring + and isinstance(item, ast.Expr) + and isinstance(item.value, ast.Str) + ): + doc = item.value.s + if self.is_rewrite_disabled(doc): + return + expect_docstring = False + elif ( + isinstance(item, ast.ImportFrom) + and item.level == 0 + and item.module == "__future__" + ): + pass + else: + break + pos += 1 + # Special case: for a decorated function, set the lineno to that of the + # first decorator, not the `def`. Issue #4984. + if isinstance(item, ast.FunctionDef) and item.decorator_list: + lineno = item.decorator_list[0].lineno + else: + lineno = item.lineno + # Now actually insert the special imports. + if sys.version_info >= (3, 10): + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, + ), + ] + else: + aliases = [ + ast.alias("builtins", "@py_builtins"), + ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), + ] + imports = [ + ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases + ] + mod.body[pos:pos] = imports + + # Collect asserts. + nodes: List[ast.AST] = [mod] + while nodes: + node = nodes.pop() + for name, field in ast.iter_fields(node): + if isinstance(field, list): + new: List[ast.AST] = [] + for i, child in enumerate(field): + if isinstance(child, ast.Assert): + # Transform assert. + new.extend(self.visit(child)) + else: + new.append(child) + if isinstance(child, ast.AST): + nodes.append(child) + setattr(node, name, new) + elif ( + isinstance(field, ast.AST) + # Don't recurse into expressions as they can't contain + # asserts. + and not isinstance(field, ast.expr) + ): + nodes.append(field) + + @staticmethod + def is_rewrite_disabled(docstring: str) -> bool: + return "PYTEST_DONT_REWRITE" in docstring + + def variable(self) -> str: + """Get a new variable.""" + # Use a character invalid in python identifiers to avoid clashing. + name = "@py_assert" + str(next(self.variable_counter)) + self.variables.append(name) + return name + + def assign(self, expr: ast.expr) -> ast.Name: + """Give *expr* a name.""" + name = self.variable() + self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr)) + return ast.Name(name, ast.Load()) + + def display(self, expr: ast.expr) -> ast.expr: + """Call saferepr on the expression.""" + return self.helper("_saferepr", expr) + + def helper(self, name: str, *args: ast.expr) -> ast.expr: + """Call a helper in this module.""" + py_name = ast.Name("@pytest_ar", ast.Load()) + attr = ast.Attribute(py_name, name, ast.Load()) + return ast.Call(attr, list(args), []) + + def builtin(self, name: str) -> ast.Attribute: + """Return the builtin called *name*.""" + builtin_name = ast.Name("@py_builtins", ast.Load()) + return ast.Attribute(builtin_name, name, ast.Load()) + + def explanation_param(self, expr: ast.expr) -> str: + """Return a new named %-formatting placeholder for expr. + + This creates a %-formatting placeholder for expr in the + current formatting context, e.g. ``%(py0)s``. The placeholder + and expr are placed in the current format context so that it + can be used on the next call to .pop_format_context(). + """ + specifier = "py" + str(next(self.variable_counter)) + self.explanation_specifiers[specifier] = expr + return "%(" + specifier + ")s" + + def push_format_context(self) -> None: + """Create a new formatting context. + + The format context is used for when an explanation wants to + have a variable value formatted in the assertion message. In + this case the value required can be added using + .explanation_param(). Finally .pop_format_context() is used + to format a string of %-formatted values as added by + .explanation_param(). + """ + self.explanation_specifiers: Dict[str, ast.expr] = {} + self.stack.append(self.explanation_specifiers) + + def pop_format_context(self, expl_expr: ast.expr) -> ast.Name: + """Format the %-formatted string with current format context. + + The expl_expr should be an str ast.expr instance constructed from + the %-placeholders created by .explanation_param(). This will + add the required code to format said string to .expl_stmts and + return the ast.Name instance of the formatted string. + """ + current = self.stack.pop() + if self.stack: + self.explanation_specifiers = self.stack[-1] + keys = [ast.Str(key) for key in current.keys()] + format_dict = ast.Dict(keys, list(current.values())) + form = ast.BinOp(expl_expr, ast.Mod(), format_dict) + name = "@py_format" + str(next(self.variable_counter)) + if self.enable_assertion_pass_hook: + self.format_variables.append(name) + self.expl_stmts.append(ast.Assign([ast.Name(name, ast.Store())], form)) + return ast.Name(name, ast.Load()) + + def generic_visit(self, node: ast.AST) -> Tuple[ast.Name, str]: + """Handle expressions we don't have custom code for.""" + assert isinstance(node, ast.expr) + res = self.assign(node) + return res, self.explanation_param(self.display(res)) + + def visit_Assert(self, assert_: ast.Assert) -> List[ast.stmt]: + """Return the AST statements to replace the ast.Assert instance. + + This rewrites the test of an assertion to provide + intermediate values and replace it with an if statement which + raises an assertion error with a detailed explanation in case + the expression is false. + """ + if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: + from _pytest.warning_types import PytestAssertRewriteWarning + import warnings + + # TODO: This assert should not be needed. + assert self.module_path is not None + warnings.warn_explicit( + PytestAssertRewriteWarning( + "assertion is always true, perhaps remove parentheses?" + ), + category=None, + filename=os.fspath(self.module_path), + lineno=assert_.lineno, + ) + + self.statements: List[ast.stmt] = [] + self.variables: List[str] = [] + self.variable_counter = itertools.count() + + if self.enable_assertion_pass_hook: + self.format_variables: List[str] = [] + + self.stack: List[Dict[str, ast.expr]] = [] + self.expl_stmts: List[ast.stmt] = [] + self.push_format_context() + # Rewrite assert into a bunch of statements. + top_condition, explanation = self.visit(assert_.test) + + negation = ast.UnaryOp(ast.Not(), top_condition) + + if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook + msg = self.pop_format_context(ast.Str(explanation)) + + # Failed + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + gluestr = "\n>assert " + else: + assertmsg = ast.Str("") + gluestr = "assert " + err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg) + err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) + err_name = ast.Name("AssertionError", ast.Load()) + fmt = self.helper("_format_explanation", err_msg) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + statements_fail = [] + statements_fail.extend(self.expl_stmts) + statements_fail.append(raise_) + + # Passed + fmt_pass = self.helper("_format_explanation", msg) + orig = self._assert_expr_to_lineno()[assert_.lineno] + hook_call_pass = ast.Expr( + self.helper( + "_call_assertion_pass", + ast.Num(assert_.lineno), + ast.Str(orig), + fmt_pass, + ) + ) + # If any hooks implement assert_pass hook + hook_impl_test = ast.If( + self.helper("_check_if_assertion_pass_impl"), + self.expl_stmts + [hook_call_pass], + [], + ) + statements_pass = [hook_impl_test] + + # Test for assertion condition + main_test = ast.If(negation, statements_fail, statements_pass) + self.statements.append(main_test) + if self.format_variables: + variables = [ + ast.Name(name, ast.Store()) for name in self.format_variables + ] + clear_format = ast.Assign(variables, ast.NameConstant(None)) + self.statements.append(clear_format) + + else: # Original assertion rewriting + # Create failure message. + body = self.expl_stmts + self.statements.append(ast.If(negation, body, [])) + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + explanation = "\n>assert " + explanation + else: + assertmsg = ast.Str("") + explanation = "assert " + explanation + template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation)) + msg = self.pop_format_context(template) + fmt = self.helper("_format_explanation", msg) + err_name = ast.Name("AssertionError", ast.Load()) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + + body.append(raise_) + + # Clear temporary variables by setting them to None. + if self.variables: + variables = [ast.Name(name, ast.Store()) for name in self.variables] + clear = ast.Assign(variables, ast.NameConstant(None)) + self.statements.append(clear) + # Fix line numbers. + for stmt in self.statements: + set_location(stmt, assert_.lineno, assert_.col_offset) + return self.statements + + def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]: + # Display the repr of the name if it's a local variable or + # _should_repr_global_name() thinks it's acceptable. + locs = ast.Call(self.builtin("locals"), [], []) + inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) + dorepr = self.helper("_should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Str(name.id)) + return name, self.explanation_param(expr) + + def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]: + res_var = self.variable() + expl_list = self.assign(ast.List([], ast.Load())) + app = ast.Attribute(expl_list, "append", ast.Load()) + is_or = int(isinstance(boolop.op, ast.Or)) + body = save = self.statements + fail_save = self.expl_stmts + levels = len(boolop.values) - 1 + self.push_format_context() + # Process each operand, short-circuiting if needed. + for i, v in enumerate(boolop.values): + if i: + fail_inner: List[ast.stmt] = [] + # cond is set in a prior loop iteration below + self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa + self.expl_stmts = fail_inner + self.push_format_context() + res, expl = self.visit(v) + body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) + expl_format = self.pop_format_context(ast.Str(expl)) + call = ast.Call(app, [expl_format], []) + self.expl_stmts.append(ast.Expr(call)) + if i < levels: + cond: ast.expr = res + if is_or: + cond = ast.UnaryOp(ast.Not(), cond) + inner: List[ast.stmt] = [] + self.statements.append(ast.If(cond, inner, [])) + self.statements = body = inner + self.statements = save + self.expl_stmts = fail_save + expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or)) + expl = self.pop_format_context(expl_template) + return ast.Name(res_var, ast.Load()), self.explanation_param(expl) + + def visit_UnaryOp(self, unary: ast.UnaryOp) -> Tuple[ast.Name, str]: + pattern = UNARY_MAP[unary.op.__class__] + operand_res, operand_expl = self.visit(unary.operand) + res = self.assign(ast.UnaryOp(unary.op, operand_res)) + return res, pattern % (operand_expl,) + + def visit_BinOp(self, binop: ast.BinOp) -> Tuple[ast.Name, str]: + symbol = BINOP_MAP[binop.op.__class__] + left_expr, left_expl = self.visit(binop.left) + right_expr, right_expl = self.visit(binop.right) + explanation = f"({left_expl} {symbol} {right_expl})" + res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) + return res, explanation + + def visit_Call(self, call: ast.Call) -> Tuple[ast.Name, str]: + new_func, func_expl = self.visit(call.func) + arg_expls = [] + new_args = [] + new_kwargs = [] + for arg in call.args: + res, expl = self.visit(arg) + arg_expls.append(expl) + new_args.append(res) + for keyword in call.keywords: + res, expl = self.visit(keyword.value) + new_kwargs.append(ast.keyword(keyword.arg, res)) + if keyword.arg: + arg_expls.append(keyword.arg + "=" + expl) + else: # **args have `arg` keywords with an .arg of None + arg_expls.append("**" + expl) + + expl = "{}({})".format(func_expl, ", ".join(arg_expls)) + new_call = ast.Call(new_func, new_args, new_kwargs) + res = self.assign(new_call) + res_expl = self.explanation_param(self.display(res)) + outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" + return res, outer_expl + + def visit_Starred(self, starred: ast.Starred) -> Tuple[ast.Starred, str]: + # A Starred node can appear in a function call. + res, expl = self.visit(starred.value) + new_starred = ast.Starred(res, starred.ctx) + return new_starred, "*" + expl + + def visit_Attribute(self, attr: ast.Attribute) -> Tuple[ast.Name, str]: + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + value, value_expl = self.visit(attr.value) + res = self.assign(ast.Attribute(value, attr.attr, ast.Load())) + res_expl = self.explanation_param(self.display(res)) + pat = "%s\n{%s = %s.%s\n}" + expl = pat % (res_expl, res_expl, value_expl, attr.attr) + return res, expl + + def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]: + self.push_format_context() + left_res, left_expl = self.visit(comp.left) + if isinstance(comp.left, (ast.Compare, ast.BoolOp)): + left_expl = f"({left_expl})" + res_variables = [self.variable() for i in range(len(comp.ops))] + load_names = [ast.Name(v, ast.Load()) for v in res_variables] + store_names = [ast.Name(v, ast.Store()) for v in res_variables] + it = zip(range(len(comp.ops)), comp.ops, comp.comparators) + expls = [] + syms = [] + results = [left_res] + for i, op, next_operand in it: + next_res, next_expl = self.visit(next_operand) + if isinstance(next_operand, (ast.Compare, ast.BoolOp)): + next_expl = f"({next_expl})" + results.append(next_res) + sym = BINOP_MAP[op.__class__] + syms.append(ast.Str(sym)) + expl = f"{left_expl} {sym} {next_expl}" + expls.append(ast.Str(expl)) + res_expr = ast.Compare(left_res, [op], [next_res]) + self.statements.append(ast.Assign([store_names[i]], res_expr)) + left_res, left_expl = next_res, next_expl + # Use pytest.assertion.util._reprcompare if that's available. + expl_call = self.helper( + "_call_reprcompare", + ast.Tuple(syms, ast.Load()), + ast.Tuple(load_names, ast.Load()), + ast.Tuple(expls, ast.Load()), + ast.Tuple(results, ast.Load()), + ) + if len(comp.ops) > 1: + res: ast.expr = ast.BoolOp(ast.And(), load_names) + else: + res = load_names[0] + return res, self.explanation_param(self.pop_format_context(expl_call)) + + +def try_makedirs(cache_dir: Path) -> bool: + """Attempt to create the given directory and sub-directories exist. + + Returns True if successful or if it already exists. + """ + try: + os.makedirs(os.fspath(cache_dir), exist_ok=True) + except (FileNotFoundError, NotADirectoryError, FileExistsError): + # One of the path components was not a directory: + # - we're in a zip file + # - it is a file + return False + except PermissionError: + return False + except OSError as e: + # as of now, EROFS doesn't have an equivalent OSError-subclass + if e.errno == errno.EROFS: + return False + raise + return True + + +def get_cache_dir(file_path: Path) -> Path: + """Return the cache directory to write .pyc files for the given .py file path.""" + if sys.version_info >= (3, 8) and sys.pycache_prefix: + # given: + # prefix = '/tmp/pycs' + # path = '/home/user/proj/test_app.py' + # we want: + # '/tmp/pycs/home/user/proj' + return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) + else: + # classic pycache directory + return file_path.parent / "__pycache__" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/truncate.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/truncate.py new file mode 100644 index 0000000..5ba9ddc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,100 @@ +"""Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +~8 terminal lines, unless running in "-vv" mode or running on CI. +""" +import os +from typing import List +from typing import Optional + +from _pytest.nodes import Item + + +DEFAULT_MAX_LINES = 8 +DEFAULT_MAX_CHARS = 8 * 80 +USAGE_MSG = "use '-vv' to show" + + +def truncate_if_required( + explanation: List[str], item: Item, max_length: Optional[int] = None +) -> List[str]: + """Truncate this assertion explanation if the given test item is eligible.""" + if _should_truncate_item(item): + return _truncate_explanation(explanation) + return explanation + + +def _should_truncate_item(item: Item) -> bool: + """Whether or not this test item is eligible for truncation.""" + verbose = item.config.option.verbose + return verbose < 2 and not _running_on_ci() + + +def _running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + env_vars = ["CI", "BUILD_NUMBER"] + return any(var in os.environ for var in env_vars) + + +def _truncate_explanation( + input_lines: List[str], + max_lines: Optional[int] = None, + max_chars: Optional[int] = None, +) -> List[str]: + """Truncate given list of strings that makes up the assertion explanation. + + Truncates to either 8 lines, or 640 characters - whichever the input reaches + first. The remaining lines will be replaced by a usage message. + """ + + if max_lines is None: + max_lines = DEFAULT_MAX_LINES + if max_chars is None: + max_chars = DEFAULT_MAX_CHARS + + # Check if truncation required + input_char_count = len("".join(input_lines)) + if len(input_lines) <= max_lines and input_char_count <= max_chars: + return input_lines + + # Truncate first to max_lines, and then truncate to max_chars if max_chars + # is exceeded. + truncated_explanation = input_lines[:max_lines] + truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars) + + # Add ellipsis to final line + truncated_explanation[-1] = truncated_explanation[-1] + "..." + + # Append useful message to explanation + truncated_line_count = len(input_lines) - len(truncated_explanation) + truncated_line_count += 1 # Account for the part-truncated final line + msg = "...Full output truncated" + if truncated_line_count == 1: + msg += f" ({truncated_line_count} line hidden)" + else: + msg += f" ({truncated_line_count} lines hidden)" + msg += f", {USAGE_MSG}" + truncated_explanation.extend(["", str(msg)]) + return truncated_explanation + + +def _truncate_by_char_count(input_lines: List[str], max_chars: int) -> List[str]: + # Check if truncation required + if len("".join(input_lines)) <= max_chars: + return input_lines + + # Find point at which input length exceeds total allowed length + iterated_char_count = 0 + for iterated_index, input_line in enumerate(input_lines): + if iterated_char_count + len(input_line) > max_chars: + break + iterated_char_count += len(input_line) + + # Create truncated explanation with modified final line + truncated_result = input_lines[:iterated_index] + final_line = input_lines[iterated_index] + if final_line: + final_line_truncate_point = max_chars - iterated_char_count + final_line = final_line[:final_line_truncate_point] + truncated_result.append(final_line) + return truncated_result diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/util.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/util.py new file mode 100644 index 0000000..da1ffd1 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/assertion/util.py @@ -0,0 +1,477 @@ +"""Utilities for assertion debugging.""" +import collections.abc +import pprint +from typing import AbstractSet +from typing import Any +from typing import Callable +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence + +import _pytest._code +from _pytest import outcomes +from _pytest._io.saferepr import _pformat_dispatch +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr + +# The _reprcompare attribute on the util module is used by the new assertion +# interpretation code and assertion rewriter to detect this plugin was +# loaded and in turn call the hooks defined here as part of the +# DebugInterpreter. +_reprcompare: Optional[Callable[[str, object, object], Optional[str]]] = None + +# Works similarly as _reprcompare attribute. Is populated with the hook call +# when pytest_runtest_setup is called. +_assertion_pass: Optional[Callable[[int, str, str], None]] = None + + +def format_explanation(explanation: str) -> str: + r"""Format an explanation. + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + lines = _split_explanation(explanation) + result = _format_lines(lines) + return "\n".join(result) + + +def _split_explanation(explanation: str) -> List[str]: + r"""Return a list of individual lines in the explanation. + + This will return a list of lines split on '\n{', '\n}' and '\n~'. + Any other newlines will be escaped and appear in the line as the + literal '\n' characters. + """ + raw_lines = (explanation or "").split("\n") + lines = [raw_lines[0]] + for values in raw_lines[1:]: + if values and values[0] in ["{", "}", "~", ">"]: + lines.append(values) + else: + lines[-1] += "\\n" + values + return lines + + +def _format_lines(lines: Sequence[str]) -> List[str]: + """Format the individual lines. + + This will replace the '{', '}' and '~' characters of our mini formatting + language with the proper 'where ...', 'and ...' and ' + ...' text, taking + care of indentation along the way. + + Return a list of formatted lines. + """ + result = list(lines[:1]) + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith("{"): + if stackcnt[-1]: + s = "and " + else: + s = "where " + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) + elif line.startswith("}"): + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line[0] in ["~", ">"] + stack[-1] += 1 + indent = len(stack) if line.startswith("~") else len(stack) - 1 + result.append(" " * indent + line[1:]) + assert len(stack) == 1 + return result + + +def issequence(x: Any) -> bool: + return isinstance(x, collections.abc.Sequence) and not isinstance(x, str) + + +def istext(x: Any) -> bool: + return isinstance(x, str) + + +def isdict(x: Any) -> bool: + return isinstance(x, dict) + + +def isset(x: Any) -> bool: + return isinstance(x, (set, frozenset)) + + +def isnamedtuple(obj: Any) -> bool: + return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None + + +def isdatacls(obj: Any) -> bool: + return getattr(obj, "__dataclass_fields__", None) is not None + + +def isattrs(obj: Any) -> bool: + return getattr(obj, "__attrs_attrs__", None) is not None + + +def isiterable(obj: Any) -> bool: + try: + iter(obj) + return not istext(obj) + except TypeError: + return False + + +def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[str]]: + """Return specialised explanations for some operators/operands.""" + verbose = config.getoption("verbose") + if verbose > 1: + left_repr = safeformat(left) + right_repr = safeformat(right) + else: + # XXX: "15 chars indentation" is wrong + # ("E AssertionError: assert "); should use term width. + maxsize = ( + 80 - 15 - len(op) - 2 + ) // 2 # 15 chars indentation, 1 space around op + left_repr = saferepr(left, maxsize=maxsize) + right_repr = saferepr(right, maxsize=maxsize) + + summary = f"{left_repr} {op} {right_repr}" + + explanation = None + try: + if op == "==": + explanation = _compare_eq_any(left, right, verbose) + elif op == "not in": + if istext(left) and istext(right): + explanation = _notin_text(left, right, verbose) + except outcomes.Exit: + raise + except Exception: + explanation = [ + "(pytest_assertion plugin: representation of details failed: {}.".format( + _pytest._code.ExceptionInfo.from_current()._getreprcrash() + ), + " Probably an object has a faulty __repr__.)", + ] + + if not explanation: + return None + + return [summary] + explanation + + +def _compare_eq_any(left: Any, right: Any, verbose: int = 0) -> List[str]: + explanation = [] + if istext(left) and istext(right): + explanation = _diff_text(left, right, verbose) + else: + if type(left) == type(right) and ( + isdatacls(left) or isattrs(left) or isnamedtuple(left) + ): + # Note: unlike dataclasses/attrs, namedtuples compare only the + # field values, not the type or field names. But this branch + # intentionally only handles the same-type case, which was often + # used in older code bases before dataclasses/attrs were available. + explanation = _compare_eq_cls(left, right, verbose) + elif issequence(left) and issequence(right): + explanation = _compare_eq_sequence(left, right, verbose) + elif isset(left) and isset(right): + explanation = _compare_eq_set(left, right, verbose) + elif isdict(left) and isdict(right): + explanation = _compare_eq_dict(left, right, verbose) + elif verbose > 0: + explanation = _compare_eq_verbose(left, right) + if isiterable(left) and isiterable(right): + expl = _compare_eq_iterable(left, right, verbose) + explanation.extend(expl) + return explanation + + +def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: + """Return the explanation for the diff between text. + + Unless --verbose is used this will skip leading and trailing + characters which are identical to keep the diff minimal. + """ + from difflib import ndiff + + explanation: List[str] = [] + + if verbose < 1: + i = 0 # just in case left or right has zero length + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + break + if i > 42: + i -= 10 # Provide some context + explanation = [ + "Skipping %s identical leading characters in diff, use -v to show" % i + ] + left = left[i:] + right = right[i:] + if len(left) == len(right): + for i in range(len(left)): + if left[-i] != right[-i]: + break + if i > 42: + i -= 10 # Provide some context + explanation += [ + "Skipping {} identical trailing " + "characters in diff, use -v to show".format(i) + ] + left = left[:-i] + right = right[:-i] + keepends = True + if left.isspace() or right.isspace(): + left = repr(str(left)) + right = repr(str(right)) + explanation += ["Strings contain only whitespace, escaping them using repr()"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation += [ + line.strip("\n") + for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) + ] + return explanation + + +def _compare_eq_verbose(left: Any, right: Any) -> List[str]: + keepends = True + left_lines = repr(left).splitlines(keepends) + right_lines = repr(right).splitlines(keepends) + + explanation: List[str] = [] + explanation += ["+" + line for line in left_lines] + explanation += ["-" + line for line in right_lines] + + return explanation + + +def _surrounding_parens_on_own_lines(lines: List[str]) -> None: + """Move opening/closing parenthesis/bracket to own lines.""" + opening = lines[0][:1] + if opening in ["(", "[", "{"]: + lines[0] = " " + lines[0][1:] + lines[:] = [opening] + lines + closing = lines[-1][-1:] + if closing in [")", "]", "}"]: + lines[-1] = lines[-1][:-1] + "," + lines[:] = lines + [closing] + + +def _compare_eq_iterable( + left: Iterable[Any], right: Iterable[Any], verbose: int = 0 +) -> List[str]: + if not verbose: + return ["Use -v to get the full diff"] + # dynamic import to speedup pytest + import difflib + + left_formatting = pprint.pformat(left).splitlines() + right_formatting = pprint.pformat(right).splitlines() + + # Re-format for different output lengths. + lines_left = len(left_formatting) + lines_right = len(right_formatting) + if lines_left != lines_right: + left_formatting = _pformat_dispatch(left).splitlines() + right_formatting = _pformat_dispatch(right).splitlines() + + if lines_left > 1 or lines_right > 1: + _surrounding_parens_on_own_lines(left_formatting) + _surrounding_parens_on_own_lines(right_formatting) + + explanation = ["Full diff:"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + line.rstrip() for line in difflib.ndiff(right_formatting, left_formatting) + ) + return explanation + + +def _compare_eq_sequence( + left: Sequence[Any], right: Sequence[Any], verbose: int = 0 +) -> List[str]: + comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes) + explanation: List[str] = [] + len_left = len(left) + len_right = len(right) + for i in range(min(len_left, len_right)): + if left[i] != right[i]: + if comparing_bytes: + # when comparing bytes, we want to see their ascii representation + # instead of their numeric values (#5260) + # using a slice gives us the ascii representation: + # >>> s = b'foo' + # >>> s[0] + # 102 + # >>> s[0:1] + # b'f' + left_value = left[i : i + 1] + right_value = right[i : i + 1] + else: + left_value = left[i] + right_value = right[i] + + explanation += [f"At index {i} diff: {left_value!r} != {right_value!r}"] + break + + if comparing_bytes: + # when comparing bytes, it doesn't help to show the "sides contain one or more + # items" longer explanation, so skip it + + return explanation + + len_diff = len_left - len_right + if len_diff: + if len_diff > 0: + dir_with_more = "Left" + extra = saferepr(left[len_right]) + else: + len_diff = 0 - len_diff + dir_with_more = "Right" + extra = saferepr(right[len_left]) + + if len_diff == 1: + explanation += [f"{dir_with_more} contains one more item: {extra}"] + else: + explanation += [ + "%s contains %d more items, first extra item: %s" + % (dir_with_more, len_diff, extra) + ] + return explanation + + +def _compare_eq_set( + left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0 +) -> List[str]: + explanation = [] + diff_left = left - right + diff_right = right - left + if diff_left: + explanation.append("Extra items in the left set:") + for item in diff_left: + explanation.append(saferepr(item)) + if diff_right: + explanation.append("Extra items in the right set:") + for item in diff_right: + explanation.append(saferepr(item)) + return explanation + + +def _compare_eq_dict( + left: Mapping[Any, Any], right: Mapping[Any, Any], verbose: int = 0 +) -> List[str]: + explanation: List[str] = [] + set_left = set(left) + set_right = set(right) + common = set_left.intersection(set_right) + same = {k: left[k] for k in common if left[k] == right[k]} + if same and verbose < 2: + explanation += ["Omitting %s identical items, use -vv to show" % len(same)] + elif same: + explanation += ["Common items:"] + explanation += pprint.pformat(same).splitlines() + diff = {k for k in common if left[k] != right[k]} + if diff: + explanation += ["Differing items:"] + for k in diff: + explanation += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})] + extra_left = set_left - set_right + len_extra_left = len(extra_left) + if len_extra_left: + explanation.append( + "Left contains %d more item%s:" + % (len_extra_left, "" if len_extra_left == 1 else "s") + ) + explanation.extend( + pprint.pformat({k: left[k] for k in extra_left}).splitlines() + ) + extra_right = set_right - set_left + len_extra_right = len(extra_right) + if len_extra_right: + explanation.append( + "Right contains %d more item%s:" + % (len_extra_right, "" if len_extra_right == 1 else "s") + ) + explanation.extend( + pprint.pformat({k: right[k] for k in extra_right}).splitlines() + ) + return explanation + + +def _compare_eq_cls(left: Any, right: Any, verbose: int) -> List[str]: + if isdatacls(left): + all_fields = left.__dataclass_fields__ + fields_to_check = [field for field, info in all_fields.items() if info.compare] + elif isattrs(left): + all_fields = left.__attrs_attrs__ + fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] + elif isnamedtuple(left): + fields_to_check = left._fields + else: + assert False + + indent = " " + same = [] + diff = [] + for field in fields_to_check: + if getattr(left, field) == getattr(right, field): + same.append(field) + else: + diff.append(field) + + explanation = [] + if same or diff: + explanation += [""] + if same and verbose < 2: + explanation.append("Omitting %s identical items, use -vv to show" % len(same)) + elif same: + explanation += ["Matching attributes:"] + explanation += pprint.pformat(same).splitlines() + if diff: + explanation += ["Differing attributes:"] + explanation += pprint.pformat(diff).splitlines() + for field in diff: + field_left = getattr(left, field) + field_right = getattr(right, field) + explanation += [ + "", + "Drill down into differing attribute %s:" % field, + ("%s%s: %r != %r") % (indent, field, field_left, field_right), + ] + explanation += [ + indent + line + for line in _compare_eq_any(field_left, field_right, verbose) + ] + return explanation + + +def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]: + index = text.find(term) + head = text[:index] + tail = text[index + len(term) :] + correct_text = head + tail + diff = _diff_text(text, correct_text, verbose) + newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)] + for line in diff: + if line.startswith("Skipping"): + continue + if line.startswith("- "): + continue + if line.startswith("+ "): + newdiff.append(" " + line[2:]) + else: + newdiff.append(line) + return newdiff diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/cacheprovider.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/cacheprovider.py new file mode 100644 index 0000000..03acd03 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/cacheprovider.py @@ -0,0 +1,575 @@ +"""Implementation of the cache provider.""" +# This plugin was not named "cache" to avoid conflicts with the external +# pytest-cache version. +import json +import os +from pathlib import Path +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Set +from typing import Union + +import attr +import py + +from .pathlib import resolve_from_str +from .pathlib import rm_rf +from .reports import CollectReport +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.compat import final +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.python import Module +from _pytest.python import Package +from _pytest.reports import TestReport + + +README_CONTENT = """\ +# pytest cache directory # + +This directory contains data from the pytest's cache plugin, +which provides the `--lf` and `--ff` options, as well as the `cache` fixture. + +**Do not** commit this to version control. + +See [the docs](https://docs.pytest.org/en/stable/cache.html) for more information. +""" + +CACHEDIR_TAG_CONTENT = b"""\ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# http://www.bford.info/cachedir/spec.html +""" + + +@final +@attr.s(init=False) +class Cache: + _cachedir = attr.ib(type=Path, repr=False) + _config = attr.ib(type=Config, repr=False) + + # sub-directory under cache-dir for directories created by "makedir" + _CACHE_PREFIX_DIRS = "d" + + # sub-directory under cache-dir for values created by "set" + _CACHE_PREFIX_VALUES = "v" + + def __init__( + self, cachedir: Path, config: Config, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._cachedir = cachedir + self._config = config + + @classmethod + def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache": + """Create the Cache instance for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + cachedir = cls.cache_dir_from_config(config, _ispytest=True) + if config.getoption("cacheclear") and cachedir.is_dir(): + cls.clear_cache(cachedir, _ispytest=True) + return cls(cachedir, config, _ispytest=True) + + @classmethod + def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: + """Clear the sub-directories used to hold cached directories and values. + + :meta private: + """ + check_ispytest(_ispytest) + for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): + d = cachedir / prefix + if d.is_dir(): + rm_rf(d) + + @staticmethod + def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: + """Get the path to the cache directory for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + return resolve_from_str(config.getini("cache_dir"), config.rootpath) + + def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: + """Issue a cache warning. + + :meta private: + """ + check_ispytest(_ispytest) + import warnings + from _pytest.warning_types import PytestCacheWarning + + warnings.warn( + PytestCacheWarning(fmt.format(**args) if args else fmt), + self._config.hook, + stacklevel=3, + ) + + def makedir(self, name: str) -> py.path.local: + """Return a directory path object with the given name. + + If the directory does not yet exist, it will be created. You can use + it to manage files to e.g. store/retrieve database dumps across test + sessions. + + :param name: + Must be a string not containing a ``/`` separator. + Make sure the name contains your plugin or application + identifiers to prevent clashes with other cache users. + """ + path = Path(name) + if len(path.parts) > 1: + raise ValueError("name is not allowed to contain path separators") + res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) + res.mkdir(exist_ok=True, parents=True) + return py.path.local(res) + + def _getvaluepath(self, key: str) -> Path: + return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) + + def get(self, key: str, default): + """Return the cached value for the given key. + + If no value was yet cached or the value cannot be read, the specified + default is returned. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param default: + The value to return in case of a cache-miss or invalid cache value. + """ + path = self._getvaluepath(key) + try: + with path.open("r") as f: + return json.load(f) + except (ValueError, OSError): + return default + + def set(self, key: str, value: object) -> None: + """Save value for the given key. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param value: + Must be of any combination of basic python types, + including nested types like lists of dictionaries. + """ + path = self._getvaluepath(key) + try: + if path.parent.is_dir(): + cache_dir_exists_already = True + else: + cache_dir_exists_already = self._cachedir.exists() + path.parent.mkdir(exist_ok=True, parents=True) + except OSError: + self.warn("could not create cache path {path}", path=path, _ispytest=True) + return + if not cache_dir_exists_already: + self._ensure_supporting_files() + data = json.dumps(value, indent=2, sort_keys=True) + try: + f = path.open("w") + except OSError: + self.warn("cache could not write path {path}", path=path, _ispytest=True) + else: + with f: + f.write(data) + + def _ensure_supporting_files(self) -> None: + """Create supporting files in the cache dir that are not really part of the cache.""" + readme_path = self._cachedir / "README.md" + readme_path.write_text(README_CONTENT) + + gitignore_path = self._cachedir.joinpath(".gitignore") + msg = "# Created by pytest automatically.\n*\n" + gitignore_path.write_text(msg, encoding="UTF-8") + + cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") + cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) + + +class LFPluginCollWrapper: + def __init__(self, lfplugin: "LFPlugin") -> None: + self.lfplugin = lfplugin + self._collected_at_least_one_failure = False + + @hookimpl(hookwrapper=True) + def pytest_make_collect_report(self, collector: nodes.Collector): + if isinstance(collector, Session): + out = yield + res: CollectReport = out.get_result() + + # Sort any lf-paths to the beginning. + lf_paths = self.lfplugin._last_failed_paths + res.result = sorted( + res.result, key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1, + ) + return + + elif isinstance(collector, Module): + if Path(str(collector.fspath)) in self.lfplugin._last_failed_paths: + out = yield + res = out.get_result() + result = res.result + lastfailed = self.lfplugin.lastfailed + + # Only filter with known failures. + if not self._collected_at_least_one_failure: + if not any(x.nodeid in lastfailed for x in result): + return + self.lfplugin.config.pluginmanager.register( + LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip" + ) + self._collected_at_least_one_failure = True + + session = collector.session + result[:] = [ + x + for x in result + if x.nodeid in lastfailed + # Include any passed arguments (not trivial to filter). + or session.isinitpath(x.fspath) + # Keep all sub-collectors. + or isinstance(x, nodes.Collector) + ] + return + yield + + +class LFPluginCollSkipfiles: + def __init__(self, lfplugin: "LFPlugin") -> None: + self.lfplugin = lfplugin + + @hookimpl + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> Optional[CollectReport]: + # Packages are Modules, but _last_failed_paths only contains + # test-bearing paths and doesn't try to include the paths of their + # packages, so don't filter them. + if isinstance(collector, Module) and not isinstance(collector, Package): + if Path(str(collector.fspath)) not in self.lfplugin._last_failed_paths: + self.lfplugin._skipped_files += 1 + + return CollectReport( + collector.nodeid, "passed", longrepr=None, result=[] + ) + return None + + +class LFPlugin: + """Plugin which implements the --lf (run last-failing) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + active_keys = "lf", "failedfirst" + self.active = any(config.getoption(key) for key in active_keys) + assert config.cache + self.lastfailed: Dict[str, bool] = config.cache.get("cache/lastfailed", {}) + self._previously_failed_count: Optional[int] = None + self._report_status: Optional[str] = None + self._skipped_files = 0 # count skipped files during collection due to --lf + + if config.getoption("lf"): + self._last_failed_paths = self.get_last_failed_paths() + config.pluginmanager.register( + LFPluginCollWrapper(self), "lfplugin-collwrapper" + ) + + def get_last_failed_paths(self) -> Set[Path]: + """Return a set with all Paths()s of the previously failed nodeids.""" + rootpath = self.config.rootpath + result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} + return {x for x in result if x.exists()} + + def pytest_report_collectionfinish(self) -> Optional[str]: + if self.active and self.config.getoption("verbose") >= 0: + return "run-last-failure: %s" % self._report_status + return None + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if (report.when == "call" and report.passed) or report.skipped: + self.lastfailed.pop(report.nodeid, None) + elif report.failed: + self.lastfailed[report.nodeid] = True + + def pytest_collectreport(self, report: CollectReport) -> None: + passed = report.outcome in ("passed", "skipped") + if passed: + if report.nodeid in self.lastfailed: + self.lastfailed.pop(report.nodeid) + self.lastfailed.update((item.nodeid, True) for item in report.result) + else: + self.lastfailed[report.nodeid] = True + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, config: Config, items: List[nodes.Item] + ) -> Generator[None, None, None]: + yield + + if not self.active: + return + + if self.lastfailed: + previously_failed = [] + previously_passed = [] + for item in items: + if item.nodeid in self.lastfailed: + previously_failed.append(item) + else: + previously_passed.append(item) + self._previously_failed_count = len(previously_failed) + + if not previously_failed: + # Running a subset of all tests with recorded failures + # only outside of it. + self._report_status = "%d known failures not in selected tests" % ( + len(self.lastfailed), + ) + else: + if self.config.getoption("lf"): + items[:] = previously_failed + config.hook.pytest_deselected(items=previously_passed) + else: # --failedfirst + items[:] = previously_failed + previously_passed + + noun = "failure" if self._previously_failed_count == 1 else "failures" + suffix = " first" if self.config.getoption("failedfirst") else "" + self._report_status = "rerun previous {count} {noun}{suffix}".format( + count=self._previously_failed_count, suffix=suffix, noun=noun + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += " (skipped {files} {files_noun})".format( + files=self._skipped_files, files_noun=files_noun + ) + else: + self._report_status = "no previously failed tests, " + if self.config.getoption("last_failed_no_failures") == "none": + self._report_status += "deselecting all items." + config.hook.pytest_deselected(items=items[:]) + items[:] = [] + else: + self._report_status += "not deselecting items." + + def pytest_sessionfinish(self, session: Session) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + assert config.cache is not None + saved_lastfailed = config.cache.get("cache/lastfailed", {}) + if saved_lastfailed != self.lastfailed: + config.cache.set("cache/lastfailed", self.lastfailed) + + +class NFPlugin: + """Plugin which implements the --nf (run new-first) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + self.active = config.option.newfirst + assert config.cache is not None + self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, items: List[nodes.Item] + ) -> Generator[None, None, None]: + yield + + if self.active: + new_items: Dict[str, nodes.Item] = {} + other_items: Dict[str, nodes.Item] = {} + for item in items: + if item.nodeid not in self.cached_nodeids: + new_items[item.nodeid] = item + else: + other_items[item.nodeid] = item + + items[:] = self._get_increasing_order( + new_items.values() + ) + self._get_increasing_order(other_items.values()) + self.cached_nodeids.update(new_items) + else: + self.cached_nodeids.update(item.nodeid for item in items) + + def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: + return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) # type: ignore[no-any-return] + + def pytest_sessionfinish(self) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + if config.getoption("collectonly"): + return + + assert config.cache is not None + config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--lf", + "--last-failed", + action="store_true", + dest="lf", + help="rerun only the tests that failed " + "at the last run (or all if none failed)", + ) + group.addoption( + "--ff", + "--failed-first", + action="store_true", + dest="failedfirst", + help="run all tests, but run the last failures first.\n" + "This may re-order tests and thus lead to " + "repeated fixture setup/teardown.", + ) + group.addoption( + "--nf", + "--new-first", + action="store_true", + dest="newfirst", + help="run tests from new files first, then the rest of the tests " + "sorted by file mtime", + ) + group.addoption( + "--cache-show", + action="append", + nargs="?", + dest="cacheshow", + help=( + "show cache contents, don't perform collection or tests. " + "Optional argument: glob (default: '*')." + ), + ) + group.addoption( + "--cache-clear", + action="store_true", + dest="cacheclear", + help="remove all cache contents at start of test run.", + ) + cache_dir_default = ".pytest_cache" + if "TOX_ENV_DIR" in os.environ: + cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) + parser.addini("cache_dir", default=cache_dir_default, help="cache directory path.") + group.addoption( + "--lfnf", + "--last-failed-no-failures", + action="store", + dest="last_failed_no_failures", + choices=("all", "none"), + default="all", + help="which tests to run with no previously (known) failures.", + ) + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.cacheshow: + from _pytest.main import wrap_session + + return wrap_session(config, cacheshow) + return None + + +@hookimpl(tryfirst=True) +def pytest_configure(config: Config) -> None: + config.cache = Cache.for_config(config, _ispytest=True) + config.pluginmanager.register(LFPlugin(config), "lfplugin") + config.pluginmanager.register(NFPlugin(config), "nfplugin") + + +@fixture +def cache(request: FixtureRequest) -> Cache: + """Return a cache object that can persist state between testing sessions. + + cache.get(key, default) + cache.set(key, value) + + Keys must be ``/`` separated strings, where the first part is usually the + name of your plugin or application to avoid clashes with other cache users. + + Values can be any object handled by the json stdlib module. + """ + assert request.config.cache is not None + return request.config.cache + + +def pytest_report_header(config: Config) -> Optional[str]: + """Display cachedir with --cache-show and if non-default.""" + if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + assert config.cache is not None + cachedir = config.cache._cachedir + # TODO: evaluate generating upward relative paths + # starting with .., ../.. if sensible + + try: + displaypath = cachedir.relative_to(config.rootpath) + except ValueError: + displaypath = cachedir + return f"cachedir: {displaypath}" + return None + + +def cacheshow(config: Config, session: Session) -> int: + from pprint import pformat + + assert config.cache is not None + + tw = TerminalWriter() + tw.line("cachedir: " + str(config.cache._cachedir)) + if not config.cache._cachedir.is_dir(): + tw.line("cache is empty") + return 0 + + glob = config.option.cacheshow[0] + if glob is None: + glob = "*" + + dummy = object() + basedir = config.cache._cachedir + vdir = basedir / Cache._CACHE_PREFIX_VALUES + tw.sep("-", "cache values for %r" % glob) + for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): + key = str(valpath.relative_to(vdir)) + val = config.cache.get(key, dummy) + if val is dummy: + tw.line("%s contains unreadable content, will be ignored" % key) + else: + tw.line("%s contains:" % key) + for line in pformat(val).splitlines(): + tw.line(" " + line) + + ddir = basedir / Cache._CACHE_PREFIX_DIRS + if ddir.is_dir(): + contents = sorted(ddir.rglob(glob)) + tw.sep("-", "cache directories for %r" % glob) + for p in contents: + # if p.check(dir=1): + # print("%s/" % p.relto(basedir)) + if p.is_file(): + key = str(p.relative_to(basedir)) + tw.line(f"{key} is a file of length {p.stat().st_size:d}") + return 0 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/capture.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/capture.py new file mode 100644 index 0000000..0863026 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/capture.py @@ -0,0 +1,967 @@ +"""Per-test stdout/stderr capturing mechanism.""" +import contextlib +import functools +import io +import os +import sys +from io import UnsupportedOperation +from tempfile import TemporaryFile +from typing import Any +from typing import AnyStr +from typing import Generator +from typing import Generic +from typing import Iterator +from typing import Optional +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from _pytest.compat import final +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import SubRequest +from _pytest.nodes import Collector +from _pytest.nodes import File +from _pytest.nodes import Item + +if TYPE_CHECKING: + from typing_extensions import Literal + + _CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( + "--capture", + action="store", + default="fd", + metavar="method", + choices=["fd", "sys", "no", "tee-sys"], + help="per-test capturing method: one of fd|sys|no|tee-sys.", + ) + group._addoption( + "-s", + action="store_const", + const="no", + dest="capture", + help="shortcut for --capture=no.", + ) + + +def _colorama_workaround() -> None: + """Ensure colorama is imported so that it attaches to the correct stdio + handles on Windows. + + colorama uses the terminal on import time. So if something does the + first import of colorama while I/O capture is active, colorama will + fail in various ways. + """ + if sys.platform.startswith("win32"): + try: + import colorama # noqa: F401 + except ImportError: + pass + + +def _readline_workaround() -> None: + """Ensure readline is imported so that it attaches to the correct stdio + handles on Windows. + + Pdb uses readline support where available--when not running from the Python + prompt, the readline module is not imported until running the pdb REPL. If + running pytest with the --pdb option this means the readline module is not + imported until after I/O capture has been started. + + This is a problem for pyreadline, which is often used to implement readline + support on Windows, as it does not attach to the correct handles for stdout + and/or stdin if they have been redirected by the FDCapture mechanism. This + workaround ensures that readline is imported before I/O capture is setup so + that it can attach to the actual stdin/out for the console. + + See https://github.com/pytest-dev/pytest/pull/1281. + """ + if sys.platform.startswith("win32"): + try: + import readline # noqa: F401 + except ImportError: + pass + + +def _py36_windowsconsoleio_workaround(stream: TextIO) -> None: + """Workaround for Windows Unicode console handling on Python>=3.6. + + Python 3.6 implemented Unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + :param stream: + In practice ``sys.stdout`` or ``sys.stderr``, but given + here as parameter for unittesting purposes. + + See https://github.com/pytest-dev/py/issues/103. + """ + if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): + return + + # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). + if not hasattr(stream, "buffer"): # type: ignore[unreachable] + return + + buffered = hasattr(stream.buffer, "raw") + raw_stdout = stream.buffer.raw if buffered else stream.buffer # type: ignore[attr-defined] + + if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined] + return + + def _reopen_stdio(f, mode): + if not buffered and mode[0] == "w": + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), # type: ignore[arg-type] + f.encoding, + f.errors, + f.newlines, + f.line_buffering, + ) + + sys.stdin = _reopen_stdio(sys.stdin, "rb") + sys.stdout = _reopen_stdio(sys.stdout, "wb") + sys.stderr = _reopen_stdio(sys.stderr, "wb") + + +@hookimpl(hookwrapper=True) +def pytest_load_initial_conftests(early_config: Config): + ns = early_config.known_args_namespace + if ns.capture == "fd": + _py36_windowsconsoleio_workaround(sys.stdout) + _colorama_workaround() + _readline_workaround() + pluginmanager = early_config.pluginmanager + capman = CaptureManager(ns.capture) + pluginmanager.register(capman, "capturemanager") + + # Make sure that capturemanager is properly reset at final shutdown. + early_config.add_cleanup(capman.stop_global_capturing) + + # Finally trigger conftest loading but while capturing (issue #93). + capman.start_global_capturing() + outcome = yield + capman.suspend_global_capture() + if outcome.excinfo is not None: + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + + +# IO Helpers. + + +class EncodedFile(io.TextIOWrapper): + __slots__ = () + + @property + def name(self) -> str: + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 + return repr(self.buffer) + + @property + def mode(self) -> str: + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. + return self.buffer.mode.replace("b", "") + + +class CaptureIO(io.TextIOWrapper): + def __init__(self) -> None: + super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + + def getvalue(self) -> str: + assert isinstance(self.buffer, io.BytesIO) + return self.buffer.getvalue().decode("UTF-8") + + +class TeeCaptureIO(CaptureIO): + def __init__(self, other: TextIO) -> None: + self._other = other + super().__init__() + + def write(self, s: str) -> int: + super().write(s) + return self._other.write(s) + + +class DontReadFromInput: + encoding = None + + def read(self, *args): + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + readline = read + readlines = read + __next__ = read + + def __iter__(self): + return self + + def fileno(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + + def isatty(self) -> bool: + return False + + def close(self) -> None: + pass + + @property + def buffer(self): + return self + + +# Capture classes. + + +patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} + + +class NoCapture: + EMPTY_BUFFER = None + __init__ = start = done = suspend = resume = lambda *args: None + + +class SysCaptureBinary: + + EMPTY_BUFFER = b"" + + def __init__(self, fd: int, tmpfile=None, *, tee: bool = False) -> None: + name = patchsysdict[fd] + self._old = getattr(sys, name) + self.name = name + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) + self.tmpfile = tmpfile + self._state = "initialized" + + def repr(self, class_name: str) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + class_name, + self.name, + hasattr(self, "_old") and repr(self._old) or "", + self._state, + self.tmpfile, + ) + + def __repr__(self) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.name, + hasattr(self, "_old") and repr(self._old) or "", + self._state, + self.tmpfile, + ) + + def _assert_state(self, op: str, states: Tuple[str, ...]) -> None: + assert ( + self._state in states + ), "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + + def start(self) -> None: + self._assert_state("start", ("initialized",)) + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + def snap(self): + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def done(self) -> None: + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + setattr(sys, self.name, self._old) + del self._old + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + setattr(sys, self.name, self._old) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + def writeorg(self, data) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() + + +class SysCapture(SysCaptureBinary): + EMPTY_BUFFER = "" # type: ignore[assignment] + + def snap(self): + res = self.tmpfile.getvalue() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data): + self._assert_state("writeorg", ("started", "suspended")) + self._old.write(data) + self._old.flush() + + +class FDCaptureBinary: + """Capture IO to/from a given OS-level file descriptor. + + snap() produces `bytes`. + """ + + EMPTY_BUFFER = b"" + + def __init__(self, targetfd: int) -> None: + self.targetfd = targetfd + + try: + os.fstat(targetfd) + except OSError: + # FD capturing is conceptually simple -- create a temporary file, + # redirect the FD to it, redirect back when done. But when the + # target FD is invalid it throws a wrench into this loveley scheme. + # + # Tests themselves shouldn't care if the FD is valid, FD capturing + # should work regardless of external circumstances. So falling back + # to just sys capturing is not a good option. + # + # Further complications are the need to support suspend() and the + # possibility of FD reuse (e.g. the tmpfile getting the very same + # target FD). The following approach is robust, I believe. + self.targetfd_invalid: Optional[int] = os.open(os.devnull, os.O_RDWR) + os.dup2(self.targetfd_invalid, targetfd) + else: + self.targetfd_invalid = None + self.targetfd_save = os.dup(targetfd) + + if targetfd == 0: + self.tmpfile = open(os.devnull) + self.syscapture = SysCapture(targetfd) + else: + self.tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + newline="", + write_through=True, + ) + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, self.tmpfile) + else: + self.syscapture = NoCapture() + + self._state = "initialized" + + def __repr__(self) -> str: + return "<{} {} oldfd={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.targetfd, + self.targetfd_save, + self._state, + self.tmpfile, + ) + + def _assert_state(self, op: str, states: Tuple[str, ...]) -> None: + assert ( + self._state in states + ), "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + + def start(self) -> None: + """Start capturing on targetfd using memorized tmpfile.""" + self._assert_state("start", ("initialized",)) + os.dup2(self.tmpfile.fileno(), self.targetfd) + self.syscapture.start() + self._state = "started" + + def snap(self): + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def done(self) -> None: + """Stop capturing, restore streams, return original capture file, + seeked to position zero.""" + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + os.dup2(self.targetfd_save, self.targetfd) + os.close(self.targetfd_save) + if self.targetfd_invalid is not None: + if self.targetfd_invalid != self.targetfd: + os.close(self.targetfd) + os.close(self.targetfd_invalid) + self.syscapture.done() + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + if self._state == "suspended": + return + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + self.syscapture.resume() + os.dup2(self.tmpfile.fileno(), self.targetfd) + self._state = "started" + + def writeorg(self, data): + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + os.write(self.targetfd_save, data) + + +class FDCapture(FDCaptureBinary): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces text. + """ + + # Ignore type because it doesn't match the type in the superclass (bytes). + EMPTY_BUFFER = "" # type: ignore + + def snap(self): + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data): + """Write to original file descriptor.""" + super().writeorg(data.encode("utf-8")) # XXX use encoding of original stream + + +# MultiCapture + + +# This class was a namedtuple, but due to mypy limitation[0] it could not be +# made generic, so was replaced by a regular class which tries to emulate the +# pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can +# make it a namedtuple again. +# [0]: https://github.com/python/mypy/issues/685 +@final +@functools.total_ordering +class CaptureResult(Generic[AnyStr]): + """The result of :method:`CaptureFixture.readouterr`.""" + + __slots__ = ("out", "err") + + def __init__(self, out: AnyStr, err: AnyStr) -> None: + self.out: AnyStr = out + self.err: AnyStr = err + + def __len__(self) -> int: + return 2 + + def __iter__(self) -> Iterator[AnyStr]: + return iter((self.out, self.err)) + + def __getitem__(self, item: int) -> AnyStr: + return tuple(self)[item] + + def _replace( + self, *, out: Optional[AnyStr] = None, err: Optional[AnyStr] = None + ) -> "CaptureResult[AnyStr]": + return CaptureResult( + out=self.out if out is None else out, err=self.err if err is None else err + ) + + def count(self, value: AnyStr) -> int: + return tuple(self).count(value) + + def index(self, value) -> int: + return tuple(self).index(value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, (CaptureResult, tuple)): + return NotImplemented + return tuple(self) == tuple(other) + + def __hash__(self) -> int: + return hash(tuple(self)) + + def __lt__(self, other: object) -> bool: + if not isinstance(other, (CaptureResult, tuple)): + return NotImplemented + return tuple(self) < tuple(other) + + def __repr__(self) -> str: + return f"CaptureResult(out={self.out!r}, err={self.err!r})" + + +class MultiCapture(Generic[AnyStr]): + _state = None + _in_suspended = False + + def __init__(self, in_, out, err) -> None: + self.in_ = in_ + self.out = out + self.err = err + + def __repr__(self) -> str: + return "".format( + self.out, self.err, self.in_, self._state, self._in_suspended, + ) + + def start_capturing(self) -> None: + self._state = "started" + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def pop_outerr_to_orig(self) -> Tuple[AnyStr, AnyStr]: + """Pop current snapshot out/err capture and flush to orig streams.""" + out, err = self.readouterr() + if out: + self.out.writeorg(out) + if err: + self.err.writeorg(err) + return out, err + + def suspend_capturing(self, in_: bool = False) -> None: + self._state = "suspended" + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True + + def resume_capturing(self) -> None: + self._state = "started" + if self.out: + self.out.resume() + if self.err: + self.err.resume() + if self._in_suspended: + self.in_.resume() + self._in_suspended = False + + def stop_capturing(self) -> None: + """Stop capturing and reset capturing streams.""" + if self._state == "stopped": + raise ValueError("was already stopped") + self._state = "stopped" + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + + def is_started(self) -> bool: + """Whether actively capturing -- not suspended or stopped.""" + return self._state == "started" + + def readouterr(self) -> CaptureResult[AnyStr]: + if self.out: + out = self.out.snap() + else: + out = "" + if self.err: + err = self.err.snap() + else: + err = "" + return CaptureResult(out, err) + + +def _get_multicapture(method: "_CaptureMethod") -> MultiCapture[str]: + if method == "fd": + return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) + elif method == "sys": + return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) + elif method == "no": + return MultiCapture(in_=None, out=None, err=None) + elif method == "tee-sys": + return MultiCapture( + in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) + ) + raise ValueError(f"unknown capturing method: {method!r}") + + +# CaptureManager and CaptureFixture + + +class CaptureManager: + """The capture plugin. + + Manages that the appropriate capture method is enabled/disabled during + collection and each test phase (setup, call, teardown). After each of + those points, the captured output is obtained and attached to the + collection/runtest report. + + There are two levels of capture: + + * global: enabled by default and can be suppressed by the ``-s`` + option. This is always enabled/disabled during collection and each test + phase. + + * fixture: when a test function or one of its fixture depend on the + ``capsys`` or ``capfd`` fixtures. In this case special handling is + needed to ensure the fixtures take precedence over the global capture. + """ + + def __init__(self, method: "_CaptureMethod") -> None: + self._method = method + self._global_capturing: Optional[MultiCapture[str]] = None + self._capture_fixture: Optional[CaptureFixture[Any]] = None + + def __repr__(self) -> str: + return "".format( + self._method, self._global_capturing, self._capture_fixture + ) + + def is_capturing(self) -> Union[str, bool]: + if self.is_globally_capturing(): + return "global" + if self._capture_fixture: + return "fixture %s" % self._capture_fixture.request.fixturename + return False + + # Global capturing control + + def is_globally_capturing(self) -> bool: + return self._method != "no" + + def start_global_capturing(self) -> None: + assert self._global_capturing is None + self._global_capturing = _get_multicapture(self._method) + self._global_capturing.start_capturing() + + def stop_global_capturing(self) -> None: + if self._global_capturing is not None: + self._global_capturing.pop_outerr_to_orig() + self._global_capturing.stop_capturing() + self._global_capturing = None + + def resume_global_capture(self) -> None: + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() + + def suspend_global_capture(self, in_: bool = False) -> None: + if self._global_capturing is not None: + self._global_capturing.suspend_capturing(in_=in_) + + def suspend(self, in_: bool = False) -> None: + # Need to undo local capsys-et-al if it exists before disabling global capture. + self.suspend_fixture() + self.suspend_global_capture(in_) + + def resume(self) -> None: + self.resume_global_capture() + self.resume_fixture() + + def read_global_capture(self) -> CaptureResult[str]: + assert self._global_capturing is not None + return self._global_capturing.readouterr() + + # Fixture Control + + def set_fixture(self, capture_fixture: "CaptureFixture[Any]") -> None: + if self._capture_fixture: + current_fixture = self._capture_fixture.request.fixturename + requested_fixture = capture_fixture.request.fixturename + capture_fixture.request.raiseerror( + "cannot use {} and {} at the same time".format( + requested_fixture, current_fixture + ) + ) + self._capture_fixture = capture_fixture + + def unset_fixture(self) -> None: + self._capture_fixture = None + + def activate_fixture(self) -> None: + """If the current item is using ``capsys`` or ``capfd``, activate + them so they take precedence over the global capture.""" + if self._capture_fixture: + self._capture_fixture._start() + + def deactivate_fixture(self) -> None: + """Deactivate the ``capsys`` or ``capfd`` fixture of this item, if any.""" + if self._capture_fixture: + self._capture_fixture.close() + + def suspend_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._suspend() + + def resume_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._resume() + + # Helper context managers + + @contextlib.contextmanager + def global_and_fixture_disabled(self) -> Generator[None, None, None]: + """Context manager to temporarily disable global and current fixture capturing.""" + do_fixture = self._capture_fixture and self._capture_fixture._is_started() + if do_fixture: + self.suspend_fixture() + do_global = self._global_capturing and self._global_capturing.is_started() + if do_global: + self.suspend_global_capture() + try: + yield + finally: + if do_global: + self.resume_global_capture() + if do_fixture: + self.resume_fixture() + + @contextlib.contextmanager + def item_capture(self, when: str, item: Item) -> Generator[None, None, None]: + self.resume_global_capture() + self.activate_fixture() + try: + yield + finally: + self.deactivate_fixture() + self.suspend_global_capture(in_=False) + + out, err = self.read_global_capture() + item.add_report_section(when, "stdout", out) + item.add_report_section(when, "stderr", err) + + # Hooks + + @hookimpl(hookwrapper=True) + def pytest_make_collect_report(self, collector: Collector): + if isinstance(collector, File): + self.resume_global_capture() + outcome = yield + self.suspend_global_capture() + out, err = self.read_global_capture() + rep = outcome.get_result() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("setup", item): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("call", item): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("teardown", item): + yield + + @hookimpl(tryfirst=True) + def pytest_keyboard_interrupt(self) -> None: + self.stop_global_capturing() + + @hookimpl(tryfirst=True) + def pytest_internalerror(self) -> None: + self.stop_global_capturing() + + +class CaptureFixture(Generic[AnyStr]): + """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, + :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" + + def __init__( + self, captureclass, request: SubRequest, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self.captureclass = captureclass + self.request = request + self._capture: Optional[MultiCapture[AnyStr]] = None + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + + def _start(self) -> None: + if self._capture is None: + self._capture = MultiCapture( + in_=None, out=self.captureclass(1), err=self.captureclass(2), + ) + self._capture.start_capturing() + + def close(self) -> None: + if self._capture is not None: + out, err = self._capture.pop_outerr_to_orig() + self._captured_out += out + self._captured_err += err + self._capture.stop_capturing() + self._capture = None + + def readouterr(self) -> CaptureResult[AnyStr]: + """Read and return the captured output so far, resetting the internal + buffer. + + :returns: + The captured content as a namedtuple with ``out`` and ``err`` + string attributes. + """ + captured_out, captured_err = self._captured_out, self._captured_err + if self._capture is not None: + out, err = self._capture.readouterr() + captured_out += out + captured_err += err + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + return CaptureResult(captured_out, captured_err) + + def _suspend(self) -> None: + """Suspend this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.suspend_capturing() + + def _resume(self) -> None: + """Resume this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.resume_capturing() + + def _is_started(self) -> bool: + """Whether actively capturing -- not disabled or closed.""" + if self._capture is not None: + return self._capture.is_started() + return False + + @contextlib.contextmanager + def disabled(self) -> Generator[None, None, None]: + """Temporarily disable capturing while inside the ``with`` block.""" + capmanager = self.request.config.pluginmanager.getplugin("capturemanager") + with capmanager.global_and_fixture_disabled(): + yield + + +# The fixtures. + + +@fixture +def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: + """Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[str](SysCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]: + """Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsysbinary.readouterr()`` + method calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``bytes`` objects. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[bytes](SysCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: + """Enable text capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[str](FDCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]: + """Enable bytes capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``byte`` objects. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[bytes](FDCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/compat.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/compat.py new file mode 100644 index 0000000..c7f86ea --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/compat.py @@ -0,0 +1,400 @@ +"""Python version compatibility code.""" +import enum +import functools +import inspect +import re +import sys +from contextlib import contextmanager +from inspect import Parameter +from inspect import signature +from pathlib import Path +from typing import Any +from typing import Callable +from typing import Generic +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +from _pytest.outcomes import fail +from _pytest.outcomes import TEST_OUTCOME + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Final + + +_T = TypeVar("_T") +_S = TypeVar("_S") + + +# fmt: off +# Singleton type for NOTSET, as described in: +# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions +class NotSetType(enum.Enum): + token = 0 +NOTSET: "Final" = NotSetType.token # noqa: E305 +# fmt: on + +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata +else: + import importlib_metadata # noqa: F401 + + +def _format_args(func: Callable[..., Any]) -> str: + return str(signature(func)) + + +# The type of re.compile objects is not exposed in Python. +REGEX_TYPE = type(re.compile("")) + + +def is_generator(func: object) -> bool: + genfunc = inspect.isgeneratorfunction(func) + return genfunc and not iscoroutinefunction(func) + + +def iscoroutinefunction(func: object) -> bool: + """Return True if func is a coroutine function (a function defined with async + def syntax, and doesn't contain yield), or a function decorated with + @asyncio.coroutine. + + Note: copied and modified from Python 3.5's builtin couroutines.py to avoid + importing asyncio directly, which in turns also initializes the "logging" + module as a side-effect (see issue #8). + """ + return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) + + +def is_async_function(func: object) -> bool: + """Return True if the given function seems to be an async function or + an async generator.""" + return iscoroutinefunction(func) or inspect.isasyncgenfunction(func) + + +def getlocation(function, curdir: Optional[str] = None) -> str: + function = get_real_func(function) + fn = Path(inspect.getfile(function)) + lineno = function.__code__.co_firstlineno + if curdir is not None: + try: + relfn = fn.relative_to(curdir) + except ValueError: + pass + else: + return "%s:%d" % (relfn, lineno + 1) + return "%s:%d" % (fn, lineno + 1) + + +def num_mock_patch_args(function) -> int: + """Return number of arguments used up by mock arguments (if any).""" + patchings = getattr(function, "patchings", None) + if not patchings: + return 0 + + mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) + ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) + + return len( + [ + p + for p in patchings + if not p.attribute_name + and (p.new is mock_sentinel or p.new is ut_mock_sentinel) + ] + ) + + +def getfuncargnames( + function: Callable[..., Any], + *, + name: str = "", + is_method: bool = False, + cls: Optional[type] = None, +) -> Tuple[str, ...]: + """Return the names of a function's mandatory arguments. + + Should return the names of all function arguments that: + * Aren't bound to an instance or type as in instance or class methods. + * Don't have default values. + * Aren't bound with functools.partial. + * Aren't replaced with mocks. + + The is_method and cls arguments indicate that the function should + be treated as a bound method even though it's not unless, only in + the case of cls, the function is a static method. + + The name parameter should be the original name in which the function was collected. + """ + # TODO(RonnyPfannschmidt): This function should be refactored when we + # revisit fixtures. The fixture mechanism should ask the node for + # the fixture names, and not try to obtain directly from the + # function object well after collection has occurred. + + # The parameters attribute of a Signature object contains an + # ordered mapping of parameter names to Parameter instances. This + # creates a tuple of the names of the parameters that don't have + # defaults. + try: + parameters = signature(function).parameters + except (ValueError, TypeError) as e: + fail( + f"Could not determine arguments of {function!r}: {e}", pytrace=False, + ) + + arg_names = tuple( + p.name + for p in parameters.values() + if ( + p.kind is Parameter.POSITIONAL_OR_KEYWORD + or p.kind is Parameter.KEYWORD_ONLY + ) + and p.default is Parameter.empty + ) + if not name: + name = function.__name__ + + # If this function should be treated as a bound method even though + # it's passed as an unbound method or function, remove the first + # parameter name. + if is_method or ( + cls and not isinstance(cls.__dict__.get(name, None), staticmethod) + ): + arg_names = arg_names[1:] + # Remove any names that will be replaced with mocks. + if hasattr(function, "__wrapped__"): + arg_names = arg_names[num_mock_patch_args(function) :] + return arg_names + + +if sys.version_info < (3, 7): + + @contextmanager + def nullcontext(): + yield + + +else: + from contextlib import nullcontext as nullcontext # noqa: F401 + + +def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: + # Note: this code intentionally mirrors the code at the beginning of + # getfuncargnames, to get the arguments which were excluded from its result + # because they had default values. + return tuple( + p.name + for p in signature(function).parameters.values() + if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) + and p.default is not Parameter.empty + ) + + +_non_printable_ascii_translate_table = { + i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) +} +_non_printable_ascii_translate_table.update( + {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} +) + + +def _translate_non_printable(s: str) -> str: + return s.translate(_non_printable_ascii_translate_table) + + +STRING_TYPES = bytes, str + + +def _bytes_to_ascii(val: bytes) -> str: + return val.decode("ascii", "backslashreplace") + + +def ascii_escaped(val: Union[bytes, str]) -> str: + r"""If val is pure ASCII, return it as an str, otherwise, escape + bytes objects into a sequence of escaped bytes: + + b'\xc3\xb4\xc5\xd6' -> r'\xc3\xb4\xc5\xd6' + + and escapes unicode objects into a sequence of escaped unicode + ids, e.g.: + + r'4\nV\U00043efa\x0eMXWB\x1e\u3028\u15fd\xcd\U0007d944' + + Note: + The obvious "v.decode('unicode-escape')" will return + valid UTF-8 unicode if it finds them in bytes, but we + want to return escaped bytes for any byte, even if they match + a UTF-8 string. + """ + if isinstance(val, bytes): + ret = _bytes_to_ascii(val) + else: + ret = val.encode("unicode_escape").decode("ascii") + return _translate_non_printable(ret) + + +@attr.s +class _PytestWrapper: + """Dummy wrapper around a function object for internal use only. + + Used to correctly unwrap the underlying function object when we are + creating fixtures, because we wrap the function object ourselves with a + decorator to issue warnings when the fixture function is called directly. + """ + + obj = attr.ib() + + +def get_real_func(obj): + """Get the real function object of the (possibly) wrapped object by + functools.wraps or functools.partial.""" + start_obj = obj + for i in range(100): + # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function + # to trigger a warning if it gets called directly instead of by pytest: we don't + # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) + new_obj = getattr(obj, "__pytest_wrapped__", None) + if isinstance(new_obj, _PytestWrapper): + obj = new_obj.obj + break + new_obj = getattr(obj, "__wrapped__", None) + if new_obj is None: + break + obj = new_obj + else: + from _pytest._io.saferepr import saferepr + + raise ValueError( + ("could not find real function of {start}\nstopped at {current}").format( + start=saferepr(start_obj), current=saferepr(obj) + ) + ) + if isinstance(obj, functools.partial): + obj = obj.func + return obj + + +def get_real_method(obj, holder): + """Attempt to obtain the real function object that might be wrapping + ``obj``, while at the same time returning a bound method to ``holder`` if + the original object was a bound method.""" + try: + is_method = hasattr(obj, "__func__") + obj = get_real_func(obj) + except Exception: # pragma: no cover + return obj + if is_method and hasattr(obj, "__get__") and callable(obj.__get__): + obj = obj.__get__(holder) + return obj + + +def getimfunc(func): + try: + return func.__func__ + except AttributeError: + return func + + +def safe_getattr(object: Any, name: str, default: Any) -> Any: + """Like getattr but return default upon any Exception or any OutcomeException. + + Attribute access can potentially fail for 'evil' Python objects. + See issue #214. + It catches OutcomeException because of #2490 (issue #580), new outcomes + are derived from BaseException instead of Exception (for more details + check #2707). + """ + try: + return getattr(object, name, default) + except TEST_OUTCOME: + return default + + +def safe_isclass(obj: object) -> bool: + """Ignore any exception via isinstance on Python 3.""" + try: + return inspect.isclass(obj) + except Exception: + return False + + +if TYPE_CHECKING: + if sys.version_info >= (3, 8): + from typing import final as final + else: + from typing_extensions import final as final +elif sys.version_info >= (3, 8): + from typing import final as final +else: + + def final(f): + return f + + +if sys.version_info >= (3, 8): + from functools import cached_property as cached_property +else: + from typing import overload + from typing import Type + + class cached_property(Generic[_S, _T]): + __slots__ = ("func", "__doc__") + + def __init__(self, func: Callable[[_S], _T]) -> None: + self.func = func + self.__doc__ = func.__doc__ + + @overload + def __get__( + self, instance: None, owner: Optional[Type[_S]] = ... + ) -> "cached_property[_S, _T]": + ... + + @overload + def __get__(self, instance: _S, owner: Optional[Type[_S]] = ...) -> _T: + ... + + def __get__(self, instance, owner=None): + if instance is None: + return self + value = instance.__dict__[self.func.__name__] = self.func(instance) + return value + + +# Perform exhaustiveness checking. +# +# Consider this example: +# +# MyUnion = Union[int, str] +# +# def handle(x: MyUnion) -> int { +# if isinstance(x, int): +# return 1 +# elif isinstance(x, str): +# return 2 +# else: +# raise Exception('unreachable') +# +# Now suppose we add a new variant: +# +# MyUnion = Union[int, str, bytes] +# +# After doing this, we must remember ourselves to go and update the handle +# function to handle the new variant. +# +# With `assert_never` we can do better: +# +# // raise Exception('unreachable') +# return assert_never(x) +# +# Now, if we forget to handle the new variant, the type-checker will emit a +# compile-time error, instead of the runtime error we would have gotten +# previously. +# +# This also work for Enums (if you use `is` to compare) and Literals. +def assert_never(value: "NoReturn") -> "NoReturn": + assert False, "Unhandled value: {} ({})".format(value, type(value).__name__) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__init__.py new file mode 100644 index 0000000..bd9e288 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__init__.py @@ -0,0 +1,1606 @@ +"""Command line options, ini-file and conftest.py processing.""" +import argparse +import collections.abc +import contextlib +import copy +import enum +import inspect +import os +import re +import shlex +import sys +import types +import warnings +from functools import lru_cache +from pathlib import Path +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Set +from typing import TextIO +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import attr +import py +from pluggy import HookimplMarker +from pluggy import HookspecMarker +from pluggy import PluginManager + +import _pytest._code +import _pytest.deprecated +import _pytest.hookspec +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import determine_setup +from _pytest._code import ExceptionInfo +from _pytest._code import filter_traceback +from _pytest._io import TerminalWriter +from _pytest.compat import final +from _pytest.compat import importlib_metadata +from _pytest.outcomes import fail +from _pytest.outcomes import Skipped +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportMode +from _pytest.store import Store +from _pytest.warning_types import PytestConfigWarning + +if TYPE_CHECKING: + + from _pytest._code.code import _TracebackStyle + from _pytest.terminal import TerminalReporter + from .argparsing import Argument + + +_PluggyPlugin = object +"""A type to represent plugin objects. + +Plugins can be any namespace, so we can't narrow it down much, but we use an +alias to make the intent clear. + +Ideally this type would be provided by pluggy itself. +""" + + +hookimpl = HookimplMarker("pytest") +hookspec = HookspecMarker("pytest") + + +@final +class ExitCode(enum.IntEnum): + """Encodes the valid exit codes by pytest. + + Currently users and plugins may supply other exit codes as well. + + .. versionadded:: 5.0 + """ + + #: Tests passed. + OK = 0 + #: Tests failed. + TESTS_FAILED = 1 + #: pytest was interrupted. + INTERRUPTED = 2 + #: An internal error got in the way. + INTERNAL_ERROR = 3 + #: pytest was misused. + USAGE_ERROR = 4 + #: pytest couldn't find tests. + NO_TESTS_COLLECTED = 5 + + +class ConftestImportFailure(Exception): + def __init__( + self, + path: py.path.local, + excinfo: Tuple[Type[Exception], Exception, TracebackType], + ) -> None: + super().__init__(path, excinfo) + self.path = path + self.excinfo = excinfo + + def __str__(self) -> str: + return "{}: {} (from {})".format( + self.excinfo[0].__name__, self.excinfo[1], self.path + ) + + +def filter_traceback_for_conftest_import_failure( + entry: _pytest._code.TracebackEntry, +) -> bool: + """Filter tracebacks entries which point to pytest internals or importlib. + + Make a special case for importlib because we use it to import test modules and conftest files + in _pytest.pathlib.import_path. + """ + return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) + + +def main( + args: Optional[Union[List[str], py.path.local]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> Union[int, ExitCode]: + """Perform an in-process test run. + + :param args: List of command line arguments. + :param plugins: List of plugin objects to be auto-registered during initialization. + + :returns: An exit code. + """ + try: + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure as e: + exc_info = ExceptionInfo(e.excinfo) + tw = TerminalWriter(sys.stderr) + tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) + exc_info.traceback = exc_info.traceback.filter( + filter_traceback_for_conftest_import_failure + ) + exc_repr = ( + exc_info.getrepr(style="short", chain=False) + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + for line in formatted_tb.splitlines(): + tw.line(line.rstrip(), red=True) + return ExitCode.USAGE_ERROR + else: + try: + ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( + config=config + ) + try: + return ExitCode(ret) + except ValueError: + return ret + finally: + config._ensure_unconfigure() + except UsageError as e: + tw = TerminalWriter(sys.stderr) + for msg in e.args: + tw.line(f"ERROR: {msg}\n", red=True) + return ExitCode.USAGE_ERROR + + +def console_main() -> int: + """The CLI entry point of pytest. + + This function is not meant for programmable use; use `main()` instead. + """ + # https://docs.python.org/3/library/signal.html#note-on-sigpipe + try: + code = main() + sys.stdout.flush() + return code + except BrokenPipeError: + # Python flushes standard streams on exit; redirect remaining output + # to devnull to avoid another BrokenPipeError at shutdown + devnull = os.open(os.devnull, os.O_WRONLY) + os.dup2(devnull, sys.stdout.fileno()) + return 1 # Python exits with error code 1 on EPIPE + + +class cmdline: # compatibility namespace + main = staticmethod(main) + + +def filename_arg(path: str, optname: str) -> str: + """Argparse type validator for filename arguments. + + :path: Path of filename. + :optname: Name of the option. + """ + if os.path.isdir(path): + raise UsageError(f"{optname} must be a filename, given: {path}") + return path + + +def directory_arg(path: str, optname: str) -> str: + """Argparse type validator for directory arguments. + + :path: Path of directory. + :optname: Name of the option. + """ + if not os.path.isdir(path): + raise UsageError(f"{optname} must be a directory, given: {path}") + return path + + +# Plugins that cannot be disabled via "-p no:X" currently. +essential_plugins = ( + "mark", + "main", + "runner", + "fixtures", + "helpconfig", # Provides -p. +) + +default_plugins = essential_plugins + ( + "python", + "terminal", + "debugging", + "unittest", + "capture", + "skipping", + "tmpdir", + "monkeypatch", + "recwarn", + "pastebin", + "nose", + "assertion", + "junitxml", + "doctest", + "cacheprovider", + "freeze_support", + "setuponly", + "setupplan", + "stepwise", + "warnings", + "logging", + "reports", + *(["unraisableexception", "threadexception"] if sys.version_info >= (3, 8) else []), + "faulthandler", +) + +builtin_plugins = set(default_plugins) +builtin_plugins.add("pytester") +builtin_plugins.add("pytester_assertions") + + +def get_config( + args: Optional[List[str]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> "Config": + # subsequent calls to main will create a fresh instance + pluginmanager = PytestPluginManager() + config = Config( + pluginmanager, + invocation_params=Config.InvocationParams( + args=args or (), plugins=plugins, dir=Path.cwd(), + ), + ) + + if args is not None: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(args, exclude_only=True) + + for spec in default_plugins: + pluginmanager.import_plugin(spec) + + return config + + +def get_plugin_manager() -> "PytestPluginManager": + """Obtain a new instance of the + :py:class:`_pytest.config.PytestPluginManager`, with default plugins + already loaded. + + This function can be used by integration with other tools, like hooking + into pytest to run tests into an IDE. + """ + return get_config().pluginmanager + + +def _prepareconfig( + args: Optional[Union[py.path.local, List[str]]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> "Config": + if args is None: + args = sys.argv[1:] + elif isinstance(args, py.path.local): + args = [str(args)] + elif not isinstance(args, list): + msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})" + raise TypeError(msg.format(args, type(args))) + + config = get_config(args, plugins) + pluginmanager = config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, str): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + config = pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args + ) + return config + except BaseException: + config._ensure_unconfigure() + raise + + +@final +class PytestPluginManager(PluginManager): + """A :py:class:`pluggy.PluginManager ` with + additional pytest-specific functionality: + + * Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and + ``pytest_plugins`` global variables found in plugins being loaded. + * ``conftest.py`` loading during start-up. + """ + + def __init__(self) -> None: + import _pytest.assertion + + super().__init__("pytest") + # The objects are module objects, only used generically. + self._conftest_plugins: Set[types.ModuleType] = set() + + # State related to local conftest plugins. + self._dirpath2confmods: Dict[py.path.local, List[types.ModuleType]] = {} + self._conftestpath2mod: Dict[Path, types.ModuleType] = {} + self._confcutdir: Optional[py.path.local] = None + self._noconftest = False + self._duplicatepaths: Set[py.path.local] = set() + + # plugins that were explicitly skipped with pytest.skip + # list of (module name, skip reason) + # previously we would issue a warning when a plugin was skipped, but + # since we refactored warnings as first citizens of Config, they are + # just stored here to be used later. + self.skipped_plugins: List[Tuple[str, str]] = [] + + self.add_hookspecs(_pytest.hookspec) + self.register(self) + if os.environ.get("PYTEST_DEBUG"): + err: IO[str] = sys.stderr + encoding: str = getattr(err, "encoding", "utf8") + try: + err = open( + os.dup(err.fileno()), mode=err.mode, buffering=1, encoding=encoding, + ) + except Exception: + pass + self.trace.root.setwriter(err.write) + self.enable_tracing() + + # Config._consider_importhook will set a real object if required. + self.rewrite_hook = _pytest.assertion.DummyRewriteHook() + # Used to know when we are importing conftests after the pytest_configure stage. + self._configured = False + + def parse_hookimpl_opts(self, plugin: _PluggyPlugin, name: str): + # pytest hooks are always prefixed with "pytest_", + # so we avoid accessing possibly non-readable attributes + # (see issue #1073). + if not name.startswith("pytest_"): + return + # Ignore names which can not be hooks. + if name == "pytest_plugins": + return + + method = getattr(plugin, name) + opts = super().parse_hookimpl_opts(plugin, name) + + # Consider only actual functions for hooks (#3775). + if not inspect.isroutine(method): + return + + # Collect unmarked hooks as long as they have the `pytest_' prefix. + if opts is None and name.startswith("pytest_"): + opts = {} + if opts is not None: + # TODO: DeprecationWarning, people should use hookimpl + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} + + for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): + opts.setdefault(name, hasattr(method, name) or name in known_marks) + return opts + + def parse_hookspec_opts(self, module_or_class, name: str): + opts = super().parse_hookspec_opts(module_or_class, name) + if opts is None: + method = getattr(module_or_class, name) + + if name.startswith("pytest_"): + # todo: deprecate hookspec hacks + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} + opts = { + "firstresult": hasattr(method, "firstresult") + or "firstresult" in known_marks, + "historic": hasattr(method, "historic") + or "historic" in known_marks, + } + return opts + + def register( + self, plugin: _PluggyPlugin, name: Optional[str] = None + ) -> Optional[str]: + if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: + warnings.warn( + PytestConfigWarning( + "{} plugin has been merged into the core, " + "please remove it from your requirements.".format( + name.replace("_", "-") + ) + ) + ) + return None + ret: Optional[str] = super().register(plugin, name) + if ret: + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict(plugin=plugin, manager=self) + ) + + if isinstance(plugin, types.ModuleType): + self.consider_module(plugin) + return ret + + def getplugin(self, name: str): + # Support deprecated naming because plugins (xdist e.g.) use it. + plugin: Optional[_PluggyPlugin] = self.get_plugin(name) + return plugin + + def hasplugin(self, name: str) -> bool: + """Return whether a plugin with the given name is registered.""" + return bool(self.get_plugin(name)) + + def pytest_configure(self, config: "Config") -> None: + """:meta private:""" + # XXX now that the pluginmanager exposes hookimpl(tryfirst...) + # we should remove tryfirst/trylast as markers. + config.addinivalue_line( + "markers", + "tryfirst: mark a hook implementation function such that the " + "plugin machinery will try to call it first/as early as possible.", + ) + config.addinivalue_line( + "markers", + "trylast: mark a hook implementation function such that the " + "plugin machinery will try to call it last/as late as possible.", + ) + self._configured = True + + # + # Internal API for local conftest plugin handling. + # + def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: + """Load initial conftest files given a preparsed "namespace". + + As conftest files may add their own command line options which have + arguments ('--my-opt somepath') we might get some false positives. + All builtin and 3rd party plugins will have been loaded, however, so + common options will not confuse our logic here. + """ + current = py.path.local() + self._confcutdir = ( + current.join(namespace.confcutdir, abs=True) + if namespace.confcutdir + else None + ) + self._noconftest = namespace.noconftest + self._using_pyargs = namespace.pyargs + testpaths = namespace.file_or_dir + foundanchor = False + for testpath in testpaths: + path = str(testpath) + # remove node-id syntax + i = path.find("::") + if i != -1: + path = path[:i] + anchor = current.join(path, abs=1) + if anchor.exists(): # we found some file object + self._try_load_conftest(anchor, namespace.importmode) + foundanchor = True + if not foundanchor: + self._try_load_conftest(current, namespace.importmode) + + def _try_load_conftest( + self, anchor: py.path.local, importmode: Union[str, ImportMode] + ) -> None: + self._getconftestmodules(anchor, importmode) + # let's also consider test* subdirs + if anchor.check(dir=1): + for x in anchor.listdir("test*"): + if x.check(dir=1): + self._getconftestmodules(x, importmode) + + @lru_cache(maxsize=128) + def _getconftestmodules( + self, path: py.path.local, importmode: Union[str, ImportMode], + ) -> List[types.ModuleType]: + if self._noconftest: + return [] + + if path.isfile(): + directory = path.dirpath() + else: + directory = path + + # XXX these days we may rather want to use config.rootpath + # and allow users to opt into looking into the rootdir parent + # directories instead of requiring to specify confcutdir. + clist = [] + for parent in directory.parts(): + if self._confcutdir and self._confcutdir.relto(parent): + continue + conftestpath = parent.join("conftest.py") + if conftestpath.isfile(): + mod = self._importconftest(conftestpath, importmode) + clist.append(mod) + self._dirpath2confmods[directory] = clist + return clist + + def _rget_with_confmod( + self, name: str, path: py.path.local, importmode: Union[str, ImportMode], + ) -> Tuple[types.ModuleType, Any]: + modules = self._getconftestmodules(path, importmode) + for mod in reversed(modules): + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def _importconftest( + self, conftestpath: py.path.local, importmode: Union[str, ImportMode], + ) -> types.ModuleType: + # Use a resolved Path object as key to avoid loading the same conftest + # twice with build systems that create build directories containing + # symlinks to actual files. + # Using Path().resolve() is better than py.path.realpath because + # it resolves to the correct path/drive in case-insensitive file systems (#5792) + key = Path(str(conftestpath)).resolve() + + with contextlib.suppress(KeyError): + return self._conftestpath2mod[key] + + pkgpath = conftestpath.pypkgpath() + if pkgpath is None: + _ensure_removed_sysmodule(conftestpath.purebasename) + + try: + mod = import_path(conftestpath, mode=importmode) + except Exception as e: + assert e.__traceback__ is not None + exc_info = (type(e), e, e.__traceback__) + raise ConftestImportFailure(conftestpath, exc_info) from e + + self._check_non_top_pytest_plugins(mod, conftestpath) + + self._conftest_plugins.add(mod) + self._conftestpath2mod[key] = mod + dirpath = conftestpath.dirpath() + if dirpath in self._dirpath2confmods: + for path, mods in self._dirpath2confmods.items(): + if path and path.relto(dirpath) or path == dirpath: + assert mod not in mods + mods.append(mod) + self.trace(f"loading conftestmodule {mod!r}") + self.consider_conftest(mod) + return mod + + def _check_non_top_pytest_plugins( + self, mod: types.ModuleType, conftestpath: py.path.local, + ) -> None: + if ( + hasattr(mod, "pytest_plugins") + and self._configured + and not self._using_pyargs + ): + msg = ( + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" + "It affects the entire test suite instead of just below the conftest as expected.\n" + " {}\n" + "Please move it to a top level conftest file at the rootdir:\n" + " {}\n" + "For more information, visit:\n" + " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" + ) + fail(msg.format(conftestpath, self._confcutdir), pytrace=False) + + # + # API for bootstrapping plugin loading + # + # + + def consider_preparse( + self, args: Sequence[str], *, exclude_only: bool = False + ) -> None: + i = 0 + n = len(args) + while i < n: + opt = args[i] + i += 1 + if isinstance(opt, str): + if opt == "-p": + try: + parg = args[i] + except IndexError: + return + i += 1 + elif opt.startswith("-p"): + parg = opt[2:] + else: + continue + if exclude_only and not parg.startswith("no:"): + continue + self.consider_pluginarg(parg) + + def consider_pluginarg(self, arg: str) -> None: + if arg.startswith("no:"): + name = arg[3:] + if name in essential_plugins: + raise UsageError("plugin %s cannot be disabled" % name) + + # PR #4304: remove stepwise if cacheprovider is blocked. + if name == "cacheprovider": + self.set_blocked("stepwise") + self.set_blocked("pytest_stepwise") + + self.set_blocked(name) + if not name.startswith("pytest_"): + self.set_blocked("pytest_" + name) + else: + name = arg + # Unblock the plugin. None indicates that it has been blocked. + # There is no interface with pluggy for this. + if self._name2plugin.get(name, -1) is None: + del self._name2plugin[name] + if not name.startswith("pytest_"): + if self._name2plugin.get("pytest_" + name, -1) is None: + del self._name2plugin["pytest_" + name] + self.import_plugin(arg, consider_entry_points=True) + + def consider_conftest(self, conftestmodule: types.ModuleType) -> None: + self.register(conftestmodule, name=conftestmodule.__file__) + + def consider_env(self) -> None: + self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + + def consider_module(self, mod: types.ModuleType) -> None: + self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) + + def _import_plugin_specs( + self, spec: Union[None, types.ModuleType, str, Sequence[str]] + ) -> None: + plugins = _get_plugin_specs_as_list(spec) + for import_spec in plugins: + self.import_plugin(import_spec) + + def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: + """Import a plugin with ``modname``. + + If ``consider_entry_points`` is True, entry point names are also + considered to find a plugin. + """ + # Most often modname refers to builtin modules, e.g. "pytester", + # "terminal" or "capture". Those plugins are registered under their + # basename for historic purposes but must be imported with the + # _pytest prefix. + assert isinstance(modname, str), ( + "module name as text required, got %r" % modname + ) + if self.is_blocked(modname) or self.get_plugin(modname) is not None: + return + + importspec = "_pytest." + modname if modname in builtin_plugins else modname + self.rewrite_hook.mark_rewrite(importspec) + + if consider_entry_points: + loaded = self.load_setuptools_entrypoints("pytest11", name=modname) + if loaded: + return + + try: + __import__(importspec) + except ImportError as e: + raise ImportError( + 'Error importing plugin "{}": {}'.format(modname, str(e.args[0])) + ).with_traceback(e.__traceback__) from e + + except Skipped as e: + self.skipped_plugins.append((modname, e.msg or "")) + else: + mod = sys.modules[importspec] + self.register(mod, modname) + + +def _get_plugin_specs_as_list( + specs: Union[None, types.ModuleType, str, Sequence[str]] +) -> List[str]: + """Parse a plugins specification into a list of plugin names.""" + # None means empty. + if specs is None: + return [] + # Workaround for #3899 - a submodule which happens to be called "pytest_plugins". + if isinstance(specs, types.ModuleType): + return [] + # Comma-separated list. + if isinstance(specs, str): + return specs.split(",") if specs else [] + # Direct specification. + if isinstance(specs, collections.abc.Sequence): + return list(specs) + raise UsageError( + "Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: %r" + % specs + ) + + +def _ensure_removed_sysmodule(modname: str) -> None: + try: + del sys.modules[modname] + except KeyError: + pass + + +class Notset: + def __repr__(self): + return "" + + +notset = Notset() + + +def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: + """Given an iterable of file names in a source distribution, return the "names" that should + be marked for assertion rewrite. + + For example the package "pytest_mock/__init__.py" should be added as "pytest_mock" in + the assertion rewrite mechanism. + + This function has to deal with dist-info based distributions and egg based distributions + (which are still very much in use for "editable" installs). + + Here are the file names as seen in a dist-info based distribution: + + pytest_mock/__init__.py + pytest_mock/_version.py + pytest_mock/plugin.py + pytest_mock.egg-info/PKG-INFO + + Here are the file names as seen in an egg based distribution: + + src/pytest_mock/__init__.py + src/pytest_mock/_version.py + src/pytest_mock/plugin.py + src/pytest_mock.egg-info/PKG-INFO + LICENSE + setup.py + + We have to take in account those two distribution flavors in order to determine which + names should be considered for assertion rewriting. + + More information: + https://github.com/pytest-dev/pytest-mock/issues/167 + """ + package_files = list(package_files) + seen_some = False + for fn in package_files: + is_simple_module = "/" not in fn and fn.endswith(".py") + is_package = fn.count("/") == 1 and fn.endswith("__init__.py") + if is_simple_module: + module_name, _ = os.path.splitext(fn) + # we ignore "setup.py" at the root of the distribution + if module_name != "setup": + seen_some = True + yield module_name + elif is_package: + package_name = os.path.dirname(fn) + seen_some = True + yield package_name + + if not seen_some: + # At this point we did not find any packages or modules suitable for assertion + # rewriting, so we try again by stripping the first path component (to account for + # "src" based source trees for example). + # This approach lets us have the common case continue to be fast, as egg-distributions + # are rarer. + new_package_files = [] + for fn in package_files: + parts = fn.split("/") + new_fn = "/".join(parts[1:]) + if new_fn: + new_package_files.append(new_fn) + if new_package_files: + yield from _iter_rewritable_modules(new_package_files) + + +def _args_converter(args: Iterable[str]) -> Tuple[str, ...]: + return tuple(args) + + +@final +class Config: + """Access to configuration values, pluginmanager and plugin hooks. + + :param PytestPluginManager pluginmanager: + + :param InvocationParams invocation_params: + Object containing parameters regarding the :func:`pytest.main` + invocation. + """ + + @final + @attr.s(frozen=True) + class InvocationParams: + """Holds parameters passed during :func:`pytest.main`. + + The object attributes are read-only. + + .. versionadded:: 5.1 + + .. note:: + + Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` + ini option are handled by pytest, not being included in the ``args`` attribute. + + Plugins accessing ``InvocationParams`` must be aware of that. + """ + + args = attr.ib(type=Tuple[str, ...], converter=_args_converter) + """The command-line arguments as passed to :func:`pytest.main`. + + :type: Tuple[str, ...] + """ + plugins = attr.ib(type=Optional[Sequence[Union[str, _PluggyPlugin]]]) + """Extra plugins, might be `None`. + + :type: Optional[Sequence[Union[str, plugin]]] + """ + dir = attr.ib(type=Path) + """The directory from which :func:`pytest.main` was invoked. + + :type: pathlib.Path + """ + + def __init__( + self, + pluginmanager: PytestPluginManager, + *, + invocation_params: Optional[InvocationParams] = None, + ) -> None: + from .argparsing import Parser, FILE_OR_DIR + + if invocation_params is None: + invocation_params = self.InvocationParams( + args=(), plugins=None, dir=Path.cwd() + ) + + self.option = argparse.Namespace() + """Access to command line option as attributes. + + :type: argparse.Namespace + """ + + self.invocation_params = invocation_params + """The parameters with which pytest was invoked. + + :type: InvocationParams + """ + + _a = FILE_OR_DIR + self._parser = Parser( + usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", + processopt=self._processopt, + ) + self.pluginmanager = pluginmanager + """The plugin manager handles plugin registration and hook invocation. + + :type: PytestPluginManager + """ + + self.trace = self.pluginmanager.trace.root.get("config") + self.hook = self.pluginmanager.hook + self._inicache: Dict[str, Any] = {} + self._override_ini: Sequence[str] = () + self._opt2dest: Dict[str, str] = {} + self._cleanup: List[Callable[[], None]] = [] + # A place where plugins can store information on the config for their + # own use. Currently only intended for internal plugins. + self._store = Store() + self.pluginmanager.register(self, "pytestconfig") + self._configured = False + self.hook.pytest_addoption.call_historic( + kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) + ) + + if TYPE_CHECKING: + from _pytest.cacheprovider import Cache + + self.cache: Optional[Cache] = None + + @property + def invocation_dir(self) -> py.path.local: + """The directory from which pytest was invoked. + + Prefer to use :attr:`invocation_params.dir `, + which is a :class:`pathlib.Path`. + + :type: py.path.local + """ + return py.path.local(str(self.invocation_params.dir)) + + @property + def rootpath(self) -> Path: + """The path to the :ref:`rootdir `. + + :type: pathlib.Path + + .. versionadded:: 6.1 + """ + return self._rootpath + + @property + def rootdir(self) -> py.path.local: + """The path to the :ref:`rootdir `. + + Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. + + :type: py.path.local + """ + return py.path.local(str(self.rootpath)) + + @property + def inipath(self) -> Optional[Path]: + """The path to the :ref:`configfile `. + + :type: Optional[pathlib.Path] + + .. versionadded:: 6.1 + """ + return self._inipath + + @property + def inifile(self) -> Optional[py.path.local]: + """The path to the :ref:`configfile `. + + Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. + + :type: Optional[py.path.local] + """ + return py.path.local(str(self.inipath)) if self.inipath else None + + def add_cleanup(self, func: Callable[[], None]) -> None: + """Add a function to be called when the config object gets out of + use (usually coninciding with pytest_unconfigure).""" + self._cleanup.append(func) + + def _do_configure(self) -> None: + assert not self._configured + self._configured = True + with warnings.catch_warnings(): + warnings.simplefilter("default") + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) + + def _ensure_unconfigure(self) -> None: + if self._configured: + self._configured = False + self.hook.pytest_unconfigure(config=self) + self.hook.pytest_configure._call_history = [] + while self._cleanup: + fin = self._cleanup.pop() + fin() + + def get_terminal_writer(self) -> TerminalWriter: + terminalreporter: TerminalReporter = self.pluginmanager.get_plugin( + "terminalreporter" + ) + return terminalreporter._tw + + def pytest_cmdline_parse( + self, pluginmanager: PytestPluginManager, args: List[str] + ) -> "Config": + try: + self.parse(args) + except UsageError: + + # Handle --version and --help here in a minimal fashion. + # This gets done via helpconfig normally, but its + # pytest_cmdline_main is not called in case of errors. + if getattr(self.option, "version", False) or "--version" in args: + from _pytest.helpconfig import showversion + + showversion(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser._getparser().print_help() + sys.stdout.write( + "\nNOTE: displaying only minimal help due to UsageError.\n\n" + ) + + raise + + return self + + def notify_exception( + self, + excinfo: ExceptionInfo[BaseException], + option: Optional[argparse.Namespace] = None, + ) -> None: + if option and getattr(option, "fulltrace", False): + style: _TracebackStyle = "long" + else: + style = "native" + excrepr = excinfo.getrepr( + funcargs=True, showlocals=getattr(option, "showlocals", False), style=style + ) + res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) + if not any(res): + for line in str(excrepr).split("\n"): + sys.stderr.write("INTERNALERROR> %s\n" % line) + sys.stderr.flush() + + def cwd_relative_nodeid(self, nodeid: str) -> str: + # nodeid's are relative to the rootpath, compute relative to cwd. + if self.invocation_params.dir != self.rootpath: + fullpath = self.rootpath / nodeid + nodeid = bestrelpath(self.invocation_params.dir, fullpath) + return nodeid + + @classmethod + def fromdictargs(cls, option_dict, args) -> "Config": + """Constructor usable for subprocesses.""" + config = get_config(args) + config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) + for x in config.option.plugins: + config.pluginmanager.consider_pluginarg(x) + return config + + def _processopt(self, opt: "Argument") -> None: + for name in opt._short_opts + opt._long_opts: + self._opt2dest[name] = opt.dest + + if hasattr(opt, "default"): + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + + @hookimpl(trylast=True) + def pytest_load_initial_conftests(self, early_config: "Config") -> None: + self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) + + def _initini(self, args: Sequence[str]) -> None: + ns, unknown_args = self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + rootpath, inipath, inicfg = determine_setup( + ns.inifilename, + ns.file_or_dir + unknown_args, + rootdir_cmd_arg=ns.rootdir or None, + config=self, + ) + self._rootpath = rootpath + self._inipath = inipath + self.inicfg = inicfg + self._parser.extra_info["rootdir"] = str(self.rootpath) + self._parser.extra_info["inifile"] = str(self.inipath) + self._parser.addini("addopts", "extra command line options", "args") + self._parser.addini("minversion", "minimally required pytest version") + self._parser.addini( + "required_plugins", + "plugins that must be present for pytest to run", + type="args", + default=[], + ) + self._override_ini = ns.override_ini or () + + def _consider_importhook(self, args: Sequence[str]) -> None: + """Install the PEP 302 import hook if using assertion rewriting. + + Needs to parse the --assert= option from the commandline + and find all the installed plugins to mark them for rewriting + by the importhook. + """ + ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + mode = getattr(ns, "assertmode", "plain") + if mode == "rewrite": + import _pytest.assertion + + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = "plain" + else: + self._mark_plugins_for_rewrite(hook) + self._warn_about_missing_assertion(mode) + + def _mark_plugins_for_rewrite(self, hook) -> None: + """Given an importhook, mark for rewrite any top-level + modules or packages in the distribution package for + all pytest plugins.""" + self.pluginmanager.rewrite_hook = hook + + if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # We don't autoload from setuptools entry points, no need to continue. + return + + package_files = ( + str(file) + for dist in importlib_metadata.distributions() + if any(ep.group == "pytest11" for ep in dist.entry_points) + for file in dist.files or [] + ) + + for name in _iter_rewritable_modules(package_files): + hook.mark_rewrite(name) + + def _validate_args(self, args: List[str], via: str) -> List[str]: + """Validate known args.""" + self._parser._config_source_hint = via # type: ignore + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + del self._parser._config_source_hint # type: ignore + + return args + + def _preparse(self, args: List[str], addopts: bool = True) -> None: + if addopts: + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = ( + self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + + args + ) + self._initini(args) + if addopts: + args[:] = ( + self._validate_args(self.getini("addopts"), "via addopts config") + args + ) + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.option) + ) + self._checkversion() + self._consider_importhook(args) + self.pluginmanager.consider_preparse(args, exclude_only=False) + if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # Don't autoload from setuptools entry point. Only explicitly specified + # plugins are going to be loaded. + self.pluginmanager.load_setuptools_entrypoints("pytest11") + self.pluginmanager.consider_env() + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.known_args_namespace) + ) + + self._validate_plugins() + self._warn_about_skipped_plugins() + + if self.known_args_namespace.strict: + self.issue_config_time_warning( + _pytest.deprecated.STRICT_OPTION, stacklevel=2 + ) + + if self.known_args_namespace.confcutdir is None and self.inipath is not None: + confcutdir = str(self.inipath.parent) + self.known_args_namespace.confcutdir = confcutdir + try: + self.hook.pytest_load_initial_conftests( + early_config=self, args=args, parser=self._parser + ) + except ConftestImportFailure as e: + if self.known_args_namespace.help or self.known_args_namespace.version: + # we don't want to prevent --help/--version to work + # so just let is pass and print a warning at the end + self.issue_config_time_warning( + PytestConfigWarning(f"could not load initial conftests: {e.path}"), + stacklevel=2, + ) + else: + raise + + @hookimpl(hookwrapper=True) + def pytest_collection(self) -> Generator[None, None, None]: + """Validate invalid ini keys after collection is done so we take in account + options added by late-loading conftest files.""" + yield + self._validate_config_options() + + def _checkversion(self) -> None: + import pytest + + minver = self.inicfg.get("minversion", None) + if minver: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if not isinstance(minver, str): + raise pytest.UsageError( + "%s: 'minversion' must be a single value" % self.inipath + ) + + if Version(minver) > Version(pytest.__version__): + raise pytest.UsageError( + "%s: 'minversion' requires pytest-%s, actual pytest-%s'" + % (self.inipath, minver, pytest.__version__,) + ) + + def _validate_config_options(self) -> None: + for key in sorted(self._get_unknown_ini_keys()): + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + + def _validate_plugins(self) -> None: + required_plugins = sorted(self.getini("required_plugins")) + if not required_plugins: + return + + # Imported lazily to improve start-up time. + from packaging.version import Version + from packaging.requirements import InvalidRequirement, Requirement + + plugin_info = self.pluginmanager.list_plugin_distinfo() + plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} + + missing_plugins = [] + for required_plugin in required_plugins: + try: + spec = Requirement(required_plugin) + except InvalidRequirement: + missing_plugins.append(required_plugin) + continue + + if spec.name not in plugin_dist_info: + missing_plugins.append(required_plugin) + elif Version(plugin_dist_info[spec.name]) not in spec.specifier: + missing_plugins.append(required_plugin) + + if missing_plugins: + raise UsageError( + "Missing required plugins: {}".format(", ".join(missing_plugins)), + ) + + def _warn_or_fail_if_strict(self, message: str) -> None: + if self.known_args_namespace.strict_config: + raise UsageError(message) + + self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) + + def _get_unknown_ini_keys(self) -> List[str]: + parser_inicfg = self._parser._inidict + return [name for name in self.inicfg if name not in parser_inicfg] + + def parse(self, args: List[str], addopts: bool = True) -> None: + # Parse given cmdline arguments into this config object. + assert not hasattr( + self, "args" + ), "can only parse cmdline args at most once per Config object" + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager) + ) + self._preparse(args, addopts=addopts) + # XXX deprecated hook: + self.hook.pytest_cmdline_preparse(config=self, args=args) + self._parser.after_preparse = True # type: ignore + try: + args = self._parser.parse_setoption( + args, self.option, namespace=self.option + ) + if not args: + if self.invocation_params.dir == self.rootpath: + args = self.getini("testpaths") + if not args: + args = [str(self.invocation_params.dir)] + self.args = args + except PrintHelp: + pass + + def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: + """Issue and handle a warning during the "configure" stage. + + During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` + function because it is not possible to have hookwrappers around ``pytest_configure``. + + This function is mainly intended for plugins that need to issue warnings during + ``pytest_configure`` (or similar stages). + + :param warning: The warning instance. + :param stacklevel: stacklevel forwarded to warnings.warn. + """ + if self.pluginmanager.is_blocked("warnings"): + return + + cmdline_filters = self.known_args_namespace.pythonwarnings or [] + config_filters = self.getini("filterwarnings") + + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always", type(warning)) + apply_warning_filters(config_filters, cmdline_filters) + warnings.warn(warning, stacklevel=stacklevel) + + if records: + frame = sys._getframe(stacklevel - 1) + location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + self.hook.pytest_warning_captured.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + item=None, + location=location, + ) + ) + self.hook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + nodeid="", + location=location, + ) + ) + + def addinivalue_line(self, name: str, line: str) -> None: + """Add a line to an ini-file option. The option must have been + declared but might not yet be set in which case the line becomes + the first line in its value.""" + x = self.getini(name) + assert isinstance(x, list) + x.append(line) # modifies the cached list inline + + def getini(self, name: str): + """Return configuration value from an :ref:`ini file `. + + If the specified name hasn't been registered through a prior + :py:func:`parser.addini <_pytest.config.argparsing.Parser.addini>` + call (usually from a plugin), a ValueError is raised. + """ + try: + return self._inicache[name] + except KeyError: + self._inicache[name] = val = self._getini(name) + return val + + def _getini(self, name: str): + try: + description, type, default = self._parser._inidict[name] + except KeyError as e: + raise ValueError(f"unknown configuration value: {name!r}") from e + override_value = self._get_override_ini_value(name) + if override_value is None: + try: + value = self.inicfg[name] + except KeyError: + if default is not None: + return default + if type is None: + return "" + return [] + else: + value = override_value + # Coerce the values based on types. + # + # Note: some coercions are only required if we are reading from .ini files, because + # the file format doesn't contain type information, but when reading from toml we will + # get either str or list of str values (see _parse_ini_config_from_pyproject_toml). + # For example: + # + # ini: + # a_line_list = "tests acceptance" + # in this case, we need to split the string to obtain a list of strings. + # + # toml: + # a_line_list = ["tests", "acceptance"] + # in this case, we already have a list ready to use. + # + if type == "pathlist": + # TODO: This assert is probably not valid in all cases. + assert self.inipath is not None + dp = self.inipath.parent + input_values = shlex.split(value) if isinstance(value, str) else value + return [py.path.local(str(dp / x)) for x in input_values] + elif type == "args": + return shlex.split(value) if isinstance(value, str) else value + elif type == "linelist": + if isinstance(value, str): + return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] + else: + return value + elif type == "bool": + return _strtobool(str(value).strip()) + else: + assert type in [None, "string"] + return value + + def _getconftest_pathlist( + self, name: str, path: py.path.local + ) -> Optional[List[py.path.local]]: + try: + mod, relroots = self.pluginmanager._rget_with_confmod( + name, path, self.getoption("importmode") + ) + except KeyError: + return None + modpath = py.path.local(mod.__file__).dirpath() + values: List[py.path.local] = [] + for relroot in relroots: + if not isinstance(relroot, py.path.local): + relroot = relroot.replace("/", os.sep) + relroot = modpath.join(relroot, abs=True) + values.append(relroot) + return values + + def _get_override_ini_value(self, name: str) -> Optional[str]: + value = None + # override_ini is a list of "ini=value" options. + # Always use the last item if multiple values are set for same ini-name, + # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. + for ini_config in self._override_ini: + try: + key, user_ini_value = ini_config.split("=", 1) + except ValueError as e: + raise UsageError( + "-o/--override-ini expects option=value style (got: {!r}).".format( + ini_config + ) + ) from e + else: + if key == name: + value = user_ini_value + return value + + def getoption(self, name: str, default=notset, skip: bool = False): + """Return command line option value. + + :param name: Name of the option. You may also specify + the literal ``--OPT`` option instead of the "dest" option name. + :param default: Default value if no option of that name exists. + :param skip: If True, raise pytest.skip if option does not exists + or has a None value. + """ + name = self._opt2dest.get(name, name) + try: + val = getattr(self.option, name) + if val is None and skip: + raise AttributeError(name) + return val + except AttributeError as e: + if default is not notset: + return default + if skip: + import pytest + + pytest.skip(f"no {name!r} option found") + raise ValueError(f"no option named {name!r}") from e + + def getvalue(self, name: str, path=None): + """Deprecated, use getoption() instead.""" + return self.getoption(name) + + def getvalueorskip(self, name: str, path=None): + """Deprecated, use getoption(skip=True) instead.""" + return self.getoption(name, skip=True) + + def _warn_about_missing_assertion(self, mode: str) -> None: + if not _assertion_supported(): + if mode == "plain": + warning_text = ( + "ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?" + ) + else: + warning_text = ( + "assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n" + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), stacklevel=3, + ) + + def _warn_about_skipped_plugins(self) -> None: + for module_name, msg in self.pluginmanager.skipped_plugins: + self.issue_config_time_warning( + PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), + stacklevel=2, + ) + + +def _assertion_supported() -> bool: + try: + assert False + except AssertionError: + return True + else: + return False # type: ignore[unreachable] + + +def create_terminal_writer( + config: Config, file: Optional[TextIO] = None +) -> TerminalWriter: + """Create a TerminalWriter instance configured according to the options + in the config object. + + Every code which requires a TerminalWriter object and has access to a + config object should use this function. + """ + tw = TerminalWriter(file=file) + + if config.option.color == "yes": + tw.hasmarkup = True + elif config.option.color == "no": + tw.hasmarkup = False + + if config.option.code_highlight == "yes": + tw.code_highlight = True + elif config.option.code_highlight == "no": + tw.code_highlight = False + + return tw + + +def _strtobool(val: str) -> bool: + """Convert a string representation of truth to True or False. + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + + .. note:: Copied from distutils.util. + """ + val = val.lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return True + elif val in ("n", "no", "f", "false", "off", "0"): + return False + else: + raise ValueError(f"invalid truth value {val!r}") + + +@lru_cache(maxsize=50) +def parse_warning_filter( + arg: str, *, escape: bool +) -> Tuple[str, str, Type[Warning], str, int]: + """Parse a warnings filter string. + + This is copied from warnings._setoption, but does not apply the filter, + only parses it, and makes the escaping optional. + """ + parts = arg.split(":") + if len(parts) > 5: + raise warnings._OptionError(f"too many fields (max 5): {arg!r}") + while len(parts) < 5: + parts.append("") + action_, message, category_, module, lineno_ = [s.strip() for s in parts] + action: str = warnings._getaction(action_) # type: ignore[attr-defined] + category: Type[Warning] = warnings._getcategory(category_) # type: ignore[attr-defined] + if message and escape: + message = re.escape(message) + if module and escape: + module = re.escape(module) + r"\Z" + if lineno_: + try: + lineno = int(lineno_) + if lineno < 0: + raise ValueError + except (ValueError, OverflowError) as e: + raise warnings._OptionError(f"invalid lineno {lineno_!r}") from e + else: + lineno = 0 + return action, message, category, module, lineno + + +def apply_warning_filters( + config_filters: Iterable[str], cmdline_filters: Iterable[str] +) -> None: + """Applies pytest-configured filters to the warnings module""" + # Filters should have this precedence: cmdline options, config. + # Filters should be applied in the inverse order of precedence. + for arg in config_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + for arg in cmdline_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b972993f4e5c6841d50c168c155eba66a5ebc6fc GIT binary patch literal 44768 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

    *$mM_X%}N;3*S1 z$NiX%9gdA|!-jh2Wx7MUGlR%KW;UNSXS@kFS z`UszBlFMq`;zEr1SW76#n4C;Yy@3Lc8TKIf*r4v%{AS-s<2|E$o6u>dOT)2&=eA=u z+~aEpq4M3}xu+X7AG6DD4{@$H!p;Sq*`SBqzF?#l*o430y1yKYIN5=`-i$Ehlq3 z&CrDl5XEw=xX_&$)yl-irgYX^=aFv?(vB?LZ;?uzpIo(WzU}5g7uU(wp_e3mUeQTa zekQi=gQx-E1wj`stQ~wF?*z2KUOaXWuQa>DJ}imN5}@7N*`(OZZUP~bW1H7|^(8IeC;B`W@y)f=96aJ2@?mMwOgn}( z7M7|RANP{Ot!mb^r`U86ngqU)YtQ${vZN3HK9#^$)`H@CmipqTRFpJB zaLh_frR}h)f;Y)vjF1>^TH3t8#^0L&cFi#;Le?crB(SAViLqcc2A1KcZTnk+K~x)t4zRYTco~mbI3e zp4g~&D>}bwH;Z{}lgbdT^^x%hyM0%5P1UJah8uOXklg*jP8SOuI7;rV>s$wfM%I8( z2Y^2P59sKI(37-5;Yln^Q*a=wm}Tp*5PsU)p%^ zbc;S zO!-?55tt#3u@qu>NIKiJX7f0fD=rSlSBOx00n#WjS*{ObvOKxcWZ4L&+~j%0T+U@W zd{KQ7^@5GOXaMJ3IfTw^>QcEAmTSO?vY12|w9r?%Khvd49&7{>g9N)a^KSFh`ga-- z(YLZclItwG+|hVoRux<|G^-%v2oKxPaGHgNg|x-v!aItti^Al0rmU5hBMz6@Okf3# z)x~wlzUr1X4z^C<>+e+`2vK@%nI?@7`~1H1viQVZIeIp2FG#pNaM`^D(EeZ>Xwd1sf)O z!XK$unR4@506gPRok#YKm|87oPbV>M@SyM&Ty2-t3+2yU{NWCEW=RWkDAEh`JC{4<}o_ z2M)yPgo^J+zHt;w(dsv3$JL>Ueet@*+4lifOrc!hoG!- zP76Dyn*KX3iS@2g-!>^$!)i1Y{vaj)MALi^30zsAqz7`?{ds-f0Hy(X_sNL91^}`J zuXRTZwec!RBEMRDfPuqQ6RHyqv#*2!*+b>|9ODx$alv|tNU89nHq+r&pLjBLLK&dV zlu2OD?%B*PWr(4iL7PcH?OCjQm(t=t@tn-cb256hnUl{8{zl9#I!gG$X3s2INWX6> zztu&t(QhKFvi4uJwckHk>YXM+soA`oBrX>=Xd^`5 zUw46box0&=6@Nt+O=eicC5wP4sIcmA-Wj6ikUbZ zH`Kx{>Z2hpEFds&?&8`F3Qwu!$)B_iJnVY%BniPF+IMpYR5V{%>IwhIvRH6yvA=N@ zoXr7u!_v14KVWbPB>)H&m6{Ij`UC`CyP&;D4

    LFV_|6g}X67r_6X`!&1CCs?50 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/argparsing.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/argparsing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef5d0cf141e7a4da2e41b9fc6928b13416d2c054 GIT binary patch literal 17371 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f562812bceb49bf678ed0406d88e1b4ab860a577 GIT binary patch literal 5879 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/argparsing.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/argparsing.py new file mode 100644 index 0000000..9a48196 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/argparsing.py @@ -0,0 +1,522 @@ +import argparse +import sys +import warnings +from gettext import gettext +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import py + +import _pytest._io +from _pytest.compat import final +from _pytest.config.exceptions import UsageError + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Literal + +FILE_OR_DIR = "file_or_dir" + + +@final +class Parser: + """Parser for command line arguments and ini-file values. + + :ivar extra_info: Dict of generic param -> value to display in case + there's an error processing the command line arguments. + """ + + prog: Optional[str] = None + + def __init__( + self, + usage: Optional[str] = None, + processopt: Optional[Callable[["Argument"], None]] = None, + ) -> None: + self._anonymous = OptionGroup("custom options", parser=self) + self._groups: List[OptionGroup] = [] + self._processopt = processopt + self._usage = usage + self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {} + self._ininames: List[str] = [] + self.extra_info: Dict[str, Any] = {} + + def processoption(self, option: "Argument") -> None: + if self._processopt: + if option.dest: + self._processopt(option) + + def getgroup( + self, name: str, description: str = "", after: Optional[str] = None + ) -> "OptionGroup": + """Get (or create) a named option Group. + + :name: Name of the option group. + :description: Long description for --help output. + :after: Name of another group, used for ordering --help output. + + The returned group object has an ``addoption`` method with the same + signature as :py:func:`parser.addoption + <_pytest.config.argparsing.Parser.addoption>` but will be shown in the + respective group in the output of ``pytest. --help``. + """ + for group in self._groups: + if group.name == name: + return group + group = OptionGroup(name, description, parser=self) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i + 1, group) + return group + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Register a command line option. + + :opts: Option names, can be short or long options. + :attrs: Same attributes which the ``add_argument()`` function of the + `argparse library `_ + accepts. + + After command line parsing, options are available on the pytest config + object via ``config.option.NAME`` where ``NAME`` is usually set + by passing a ``dest`` attribute, for example + ``addoption("--long", dest="NAME", ...)``. + """ + self._anonymous.addoption(*opts, **attrs) + + def parse( + self, + args: Sequence[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + from _pytest._argcomplete import try_argcomplete + + self.optparser = self._getparser() + try_argcomplete(self.optparser) + strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + return self.optparser.parse_args(strargs, namespace=namespace) + + def _getparser(self) -> "MyOptionParser": + from _pytest._argcomplete import filescompleter + + optparser = MyOptionParser(self, self.extra_info, prog=self.prog) + groups = self._groups + [self._anonymous] + for group in groups: + if group.options: + desc = group.description or group.name + arggroup = optparser.add_argument_group(desc) + for option in group.options: + n = option.names() + a = option.attrs() + arggroup.add_argument(*n, **a) + file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") + # bash like autocompletion for dirs (appending '/') + # Type ignored because typeshed doesn't know about argcomplete. + file_or_dir_arg.completer = filescompleter # type: ignore + return optparser + + def parse_setoption( + self, + args: Sequence[Union[str, py.path.local]], + option: argparse.Namespace, + namespace: Optional[argparse.Namespace] = None, + ) -> List[str]: + parsedoption = self.parse(args, namespace=namespace) + for name, value in parsedoption.__dict__.items(): + setattr(option, name, value) + return cast(List[str], getattr(parsedoption, FILE_OR_DIR)) + + def parse_known_args( + self, + args: Sequence[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + """Parse and return a namespace object with known arguments at this point.""" + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] + + def parse_known_and_unknown_args( + self, + args: Sequence[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> Tuple[argparse.Namespace, List[str]]: + """Parse and return a namespace object with known arguments, and + the remaining arguments unknown at this point.""" + optparser = self._getparser() + strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + return optparser.parse_known_args(strargs, namespace=namespace) + + def addini( + self, + name: str, + help: str, + type: Optional[ + "Literal['string', 'pathlist', 'args', 'linelist', 'bool']" + ] = None, + default=None, + ) -> None: + """Register an ini-file option. + + :name: Name of the ini-variable. + :type: Type of the variable, can be ``string``, ``pathlist``, ``args``, + ``linelist`` or ``bool``. Defaults to ``string`` if ``None`` or + not passed. + :default: Default value if no ini-file option exists but is queried. + + The value of ini-variables can be retrieved via a call to + :py:func:`config.getini(name) <_pytest.config.Config.getini>`. + """ + assert type in (None, "string", "pathlist", "args", "linelist", "bool") + self._inidict[name] = (help, type, default) + self._ininames.append(name) + + +class ArgumentError(Exception): + """Raised if an Argument instance is created with invalid or + inconsistent arguments.""" + + def __init__(self, msg: str, option: Union["Argument", str]) -> None: + self.msg = msg + self.option_id = str(option) + + def __str__(self) -> str: + if self.option_id: + return f"option {self.option_id}: {self.msg}" + else: + return self.msg + + +class Argument: + """Class that mimics the necessary behaviour of optparse.Option. + + It's currently a least effort implementation and ignoring choices + and integer prefixes. + + https://docs.python.org/3/library/optparse.html#optparse-standard-option-types + """ + + _typ_map = {"int": int, "string": str, "float": float, "complex": complex} + + def __init__(self, *names: str, **attrs: Any) -> None: + """Store parms in private vars for use in add_argument.""" + self._attrs = attrs + self._short_opts: List[str] = [] + self._long_opts: List[str] = [] + if "%default" in (attrs.get("help") or ""): + warnings.warn( + 'pytest now uses argparse. "%default" should be' + ' changed to "%(default)s" ', + DeprecationWarning, + stacklevel=3, + ) + try: + typ = attrs["type"] + except KeyError: + pass + else: + # This might raise a keyerror as well, don't want to catch that. + if isinstance(typ, str): + if typ == "choice": + warnings.warn( + "`type` argument to addoption() is the string %r." + " For choices this is optional and can be omitted, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: %s)" % (typ, names), + DeprecationWarning, + stacklevel=4, + ) + # argparse expects a type here take it from + # the type of the first element + attrs["type"] = type(attrs["choices"][0]) + else: + warnings.warn( + "`type` argument to addoption() is the string %r, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: %s)" % (typ, names), + DeprecationWarning, + stacklevel=4, + ) + attrs["type"] = Argument._typ_map[typ] + # Used in test_parseopt -> test_parse_defaultgetter. + self.type = attrs["type"] + else: + self.type = typ + try: + # Attribute existence is tested in Config._processopt. + self.default = attrs["default"] + except KeyError: + pass + self._set_opt_strings(names) + dest: Optional[str] = attrs.get("dest") + if dest: + self.dest = dest + elif self._long_opts: + self.dest = self._long_opts[0][2:].replace("-", "_") + else: + try: + self.dest = self._short_opts[0][1:] + except IndexError as e: + self.dest = "???" # Needed for the error repr. + raise ArgumentError("need a long or short option", self) from e + + def names(self) -> List[str]: + return self._short_opts + self._long_opts + + def attrs(self) -> Mapping[str, Any]: + # Update any attributes set by processopt. + attrs = "default dest help".split() + attrs.append(self.dest) + for attr in attrs: + try: + self._attrs[attr] = getattr(self, attr) + except AttributeError: + pass + if self._attrs.get("help"): + a = self._attrs["help"] + a = a.replace("%default", "%(default)s") + # a = a.replace('%prog', '%(prog)s') + self._attrs["help"] = a + return self._attrs + + def _set_opt_strings(self, opts: Sequence[str]) -> None: + """Directly from optparse. + + Might not be necessary as this is passed to argparse later on. + """ + for opt in opts: + if len(opt) < 2: + raise ArgumentError( + "invalid option string %r: " + "must be at least two characters long" % opt, + self, + ) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise ArgumentError( + "invalid short option string %r: " + "must be of the form -x, (x any non-dash char)" % opt, + self, + ) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise ArgumentError( + "invalid long option string %r: " + "must start with --, followed by non-dash" % opt, + self, + ) + self._long_opts.append(opt) + + def __repr__(self) -> str: + args: List[str] = [] + if self._short_opts: + args += ["_short_opts: " + repr(self._short_opts)] + if self._long_opts: + args += ["_long_opts: " + repr(self._long_opts)] + args += ["dest: " + repr(self.dest)] + if hasattr(self, "type"): + args += ["type: " + repr(self.type)] + if hasattr(self, "default"): + args += ["default: " + repr(self.default)] + return "Argument({})".format(", ".join(args)) + + +class OptionGroup: + def __init__( + self, name: str, description: str = "", parser: Optional[Parser] = None + ) -> None: + self.name = name + self.description = description + self.options: List[Argument] = [] + self.parser = parser + + def addoption(self, *optnames: str, **attrs: Any) -> None: + """Add an option to this group. + + If a shortened version of a long option is specified, it will + be suppressed in the help. addoption('--twowords', '--two-words') + results in help showing '--two-words' only, but --twowords gets + accepted **and** the automatic destination is in args.twowords. + """ + conflict = set(optnames).intersection( + name for opt in self.options for name in opt.names() + ) + if conflict: + raise ValueError("option names %s already added" % conflict) + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *optnames: str, **attrs: Any) -> None: + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None: + if not shortupper: + for opt in option._short_opts: + if opt[0] == "-" and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") + if self.parser: + self.parser.processoption(option) + self.options.append(option) + + +class MyOptionParser(argparse.ArgumentParser): + def __init__( + self, + parser: Parser, + extra_info: Optional[Dict[str, Any]] = None, + prog: Optional[str] = None, + ) -> None: + self._parser = parser + argparse.ArgumentParser.__init__( + self, + prog=prog, + usage=parser._usage, + add_help=False, + formatter_class=DropShorterLongHelpFormatter, + allow_abbrev=False, + ) + # extra_info is a dict of (param -> value) to display if there's + # an usage error to provide more contextual information to the user. + self.extra_info = extra_info if extra_info else {} + + def error(self, message: str) -> "NoReturn": + """Transform argparse error message into UsageError.""" + msg = f"{self.prog}: error: {message}" + + if hasattr(self._parser, "_config_source_hint"): + # Type ignored because the attribute is set dynamically. + msg = f"{msg} ({self._parser._config_source_hint})" # type: ignore + + raise UsageError(self.format_usage() + msg) + + # Type ignored because typeshed has a very complex type in the superclass. + def parse_args( # type: ignore + self, + args: Optional[Sequence[str]] = None, + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + """Allow splitting of positional arguments.""" + parsed, unrecognized = self.parse_known_args(args, namespace) + if unrecognized: + for arg in unrecognized: + if arg and arg[0] == "-": + lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))] + for k, v in sorted(self.extra_info.items()): + lines.append(f" {k}: {v}") + self.error("\n".join(lines)) + getattr(parsed, FILE_OR_DIR).extend(unrecognized) + return parsed + + if sys.version_info[:2] < (3, 9): # pragma: no cover + # Backport of https://github.com/python/cpython/pull/14316 so we can + # disable long --argument abbreviations without breaking short flags. + def _parse_optional( + self, arg_string: str + ) -> Optional[Tuple[Optional[argparse.Action], str, Optional[str]]]: + if not arg_string: + return None + if not arg_string[0] in self.prefix_chars: + return None + if arg_string in self._option_string_actions: + action = self._option_string_actions[arg_string] + return action, arg_string, None + if len(arg_string) == 1: + return None + if "=" in arg_string: + option_string, explicit_arg = arg_string.split("=", 1) + if option_string in self._option_string_actions: + action = self._option_string_actions[option_string] + return action, option_string, explicit_arg + if self.allow_abbrev or not arg_string.startswith("--"): + option_tuples = self._get_option_tuples(arg_string) + if len(option_tuples) > 1: + msg = gettext( + "ambiguous option: %(option)s could match %(matches)s" + ) + options = ", ".join(option for _, option, _ in option_tuples) + self.error(msg % {"option": arg_string, "matches": options}) + elif len(option_tuples) == 1: + (option_tuple,) = option_tuples + return option_tuple + if self._negative_number_matcher.match(arg_string): + if not self._has_negative_number_optionals: + return None + if " " in arg_string: + return None + return None, arg_string, None + + +class DropShorterLongHelpFormatter(argparse.HelpFormatter): + """Shorten help for long options that differ only in extra hyphens. + + - Collapse **long** options that are the same except for extra hyphens. + - Shortcut if there are only two options and one of them is a short one. + - Cache result on the action object as this is called at least 2 times. + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # Use more accurate terminal width. + if "width" not in kwargs: + kwargs["width"] = _pytest._io.get_terminal_width() + super().__init__(*args, **kwargs) + + def _format_action_invocation(self, action: argparse.Action) -> str: + orgstr = argparse.HelpFormatter._format_action_invocation(self, action) + if orgstr and orgstr[0] != "-": # only optional arguments + return orgstr + res: Optional[str] = getattr(action, "_formatted_action_invocation", None) + if res: + return res + options = orgstr.split(", ") + if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): + # a shortcut for '-h, --help' or '--abc', '-a' + action._formatted_action_invocation = orgstr # type: ignore + return orgstr + return_list = [] + short_long: Dict[str, str] = {} + for option in options: + if len(option) == 2 or option[2] == " ": + continue + if not option.startswith("--"): + raise ArgumentError( + 'long optional argument without "--": [%s]' % (option), option + ) + xxoption = option[2:] + shortened = xxoption.replace("-", "") + if shortened not in short_long or len(short_long[shortened]) < len( + xxoption + ): + short_long[shortened] = xxoption + # now short_long has been filled out to the longest with dashes + # **and** we keep the right option ordering from add_argument + for option in options: + if len(option) == 2 or option[2] == " ": + return_list.append(option) + if option[2:] == short_long.get(option.replace("-", "")): + return_list.append(option.replace(" ", "=", 1)) + formatted_action_invocation = ", ".join(return_list) + action._formatted_action_invocation = formatted_action_invocation # type: ignore + return formatted_action_invocation + + def _split_lines(self, text, width): + """Wrap lines after splitting on original newlines. + + This allows to have explicit line breaks in the help text. + """ + import textwrap + + lines = [] + for line in text.splitlines(): + lines.extend(textwrap.wrap(line.strip(), width)) + return lines diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/exceptions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/exceptions.py new file mode 100644 index 0000000..4f1320e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/exceptions.py @@ -0,0 +1,11 @@ +from _pytest.compat import final + + +@final +class UsageError(Exception): + """Error in pytest usage or invocation.""" + + +class PrintHelp(Exception): + """Raised when pytest should print its help to skip the rest of the + argument parsing and validation.""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/findpaths.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/findpaths.py new file mode 100644 index 0000000..2edf545 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/config/findpaths.py @@ -0,0 +1,211 @@ +import os +from pathlib import Path +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import iniconfig + +from .exceptions import UsageError +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.pathlib import commonpath + +if TYPE_CHECKING: + from . import Config + + +def _parse_ini_config(path: Path) -> iniconfig.IniConfig: + """Parse the given generic '.ini' file using legacy IniConfig parser, returning + the parsed object. + + Raise UsageError if the file cannot be parsed. + """ + try: + return iniconfig.IniConfig(str(path)) + except iniconfig.ParseError as exc: + raise UsageError(str(exc)) from exc + + +def load_config_dict_from_file( + filepath: Path, +) -> Optional[Dict[str, Union[str, List[str]]]]: + """Load pytest configuration from the given file path, if supported. + + Return None if the file does not contain valid pytest configuration. + """ + + # Configuration from ini files are obtained from the [pytest] section, if present. + if filepath.suffix == ".ini": + iniconfig = _parse_ini_config(filepath) + + if "pytest" in iniconfig: + return dict(iniconfig["pytest"].items()) + else: + # "pytest.ini" files are always the source of configuration, even if empty. + if filepath.name == "pytest.ini": + return {} + + # '.cfg' files are considered if they contain a "[tool:pytest]" section. + elif filepath.suffix == ".cfg": + iniconfig = _parse_ini_config(filepath) + + if "tool:pytest" in iniconfig.sections: + return dict(iniconfig["tool:pytest"].items()) + elif "pytest" in iniconfig.sections: + # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that + # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). + fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) + + # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. + elif filepath.suffix == ".toml": + import toml + + config = toml.load(str(filepath)) + + result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) + if result is not None: + # TOML supports richer data types than ini files (strings, arrays, floats, ints, etc), + # however we need to convert all scalar values to str for compatibility with the rest + # of the configuration system, which expects strings only. + def make_scalar(v: object) -> Union[str, List[str]]: + return v if isinstance(v, list) else str(v) + + return {k: make_scalar(v) for k, v in result.items()} + + return None + + +def locate_config( + args: Iterable[Path], +) -> Tuple[ + Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]], +]: + """Search in the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict).""" + config_names = [ + "pytest.ini", + "pyproject.toml", + "tox.ini", + "setup.cfg", + ] + args = [x for x in args if not str(x).startswith("-")] + if not args: + args = [Path.cwd()] + for arg in args: + argpath = absolutepath(arg) + for base in (argpath, *argpath.parents): + for config_name in config_names: + p = base / config_name + if p.is_file(): + ini_config = load_config_dict_from_file(p) + if ini_config is not None: + return base, p, ini_config + return None, None, {} + + +def get_common_ancestor(paths: Iterable[Path]) -> Path: + common_ancestor: Optional[Path] = None + for path in paths: + if not path.exists(): + continue + if common_ancestor is None: + common_ancestor = path + else: + if common_ancestor in path.parents or path == common_ancestor: + continue + elif path in common_ancestor.parents: + common_ancestor = path + else: + shared = commonpath(path, common_ancestor) + if shared is not None: + common_ancestor = shared + if common_ancestor is None: + common_ancestor = Path.cwd() + elif common_ancestor.is_file(): + common_ancestor = common_ancestor.parent + return common_ancestor + + +def get_dirs_from_args(args: Iterable[str]) -> List[Path]: + def is_option(x: str) -> bool: + return x.startswith("-") + + def get_file_part_from_node_id(x: str) -> str: + return x.split("::")[0] + + def get_dir_from_path(path: Path) -> Path: + if path.is_dir(): + return path + return path.parent + + def safe_exists(path: Path) -> bool: + # This can throw on paths that contain characters unrepresentable at the OS level, + # or with invalid syntax on Windows (https://bugs.python.org/issue35306) + try: + return path.exists() + except OSError: + return False + + # These look like paths but may not exist + possible_paths = ( + absolutepath(get_file_part_from_node_id(arg)) + for arg in args + if not is_option(arg) + ) + + return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] + + +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + +def determine_setup( + inifile: Optional[str], + args: Sequence[str], + rootdir_cmd_arg: Optional[str] = None, + config: Optional["Config"] = None, +) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]: + rootdir = None + dirs = get_dirs_from_args(args) + if inifile: + inipath_ = absolutepath(inifile) + inipath: Optional[Path] = inipath_ + inicfg = load_config_dict_from_file(inipath_) or {} + if rootdir_cmd_arg is None: + rootdir = get_common_ancestor(dirs) + else: + ancestor = get_common_ancestor(dirs) + rootdir, inipath, inicfg = locate_config([ancestor]) + if rootdir is None and rootdir_cmd_arg is None: + for possible_rootdir in (ancestor, *ancestor.parents): + if (possible_rootdir / "setup.py").is_file(): + rootdir = possible_rootdir + break + else: + if dirs != [ancestor]: + rootdir, inipath, inicfg = locate_config(dirs) + if rootdir is None: + if config is not None: + cwd = config.invocation_params.dir + else: + cwd = Path.cwd() + rootdir = get_common_ancestor([cwd, ancestor]) + is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/" + if is_fs_root: + rootdir = ancestor + if rootdir_cmd_arg: + rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) + if not rootdir.is_dir(): + raise UsageError( + "Directory '{}' not found. Check your '--rootdir' option.".format( + rootdir + ) + ) + assert rootdir is not None + return rootdir, inipath, inicfg or {} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/debugging.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/debugging.py new file mode 100644 index 0000000..d3a5c61 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/debugging.py @@ -0,0 +1,388 @@ +"""Interactive debugging with PDB, the Python Debugger.""" +import argparse +import functools +import sys +import types +from typing import Any +from typing import Callable +from typing import Generator +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from _pytest import outcomes +from _pytest._code import ExceptionInfo +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.config.exceptions import UsageError +from _pytest.nodes import Node +from _pytest.reports import BaseReport + +if TYPE_CHECKING: + from _pytest.capture import CaptureManager + from _pytest.runner import CallInfo + + +def _validate_usepdb_cls(value: str) -> Tuple[str, str]: + """Validate syntax of --pdbcls option.""" + try: + modname, classname = value.split(":") + except ValueError as e: + raise argparse.ArgumentTypeError( + f"{value!r} is not in the format 'modname:classname'" + ) from e + return (modname, classname) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( + "--pdb", + dest="usepdb", + action="store_true", + help="start the interactive Python debugger on errors or KeyboardInterrupt.", + ) + group._addoption( + "--pdbcls", + dest="usepdb_cls", + metavar="modulename:classname", + type=_validate_usepdb_cls, + help="start a custom interactive Python debugger on errors. " + "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", + ) + group._addoption( + "--trace", + dest="trace", + action="store_true", + help="Immediately break when running each test.", + ) + + +def pytest_configure(config: Config) -> None: + import pdb + + if config.getvalue("trace"): + config.pluginmanager.register(PdbTrace(), "pdbtrace") + if config.getvalue("usepdb"): + config.pluginmanager.register(PdbInvoke(), "pdbinvoke") + + pytestPDB._saved.append( + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + ) + pdb.set_trace = pytestPDB.set_trace + pytestPDB._pluginmanager = config.pluginmanager + pytestPDB._config = config + + # NOTE: not using pytest_unconfigure, since it might get called although + # pytest_configure was not (if another plugin raises UsageError). + def fin() -> None: + ( + pdb.set_trace, + pytestPDB._pluginmanager, + pytestPDB._config, + ) = pytestPDB._saved.pop() + + config._cleanup.append(fin) + + +class pytestPDB: + """Pseudo PDB that defers to the real pdb.""" + + _pluginmanager: Optional[PytestPluginManager] = None + _config: Optional[Config] = None + _saved: List[ + Tuple[Callable[..., None], Optional[PytestPluginManager], Optional[Config]] + ] = [] + _recursive_debug = 0 + _wrapped_pdb_cls: Optional[Tuple[Type[Any], Type[Any]]] = None + + @classmethod + def _is_capturing(cls, capman: Optional["CaptureManager"]) -> Union[str, bool]: + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman: Optional["CaptureManager"]): + if not cls._config: + import pdb + + # Happens when using pytest.set_trace outside of a test. + return pdb.Pdb + + usepdb_cls = cls._config.getvalue("usepdb_cls") + + if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: + return cls._wrapped_pdb_cls[1] + + if usepdb_cls: + modname, classname = usepdb_cls + + try: + __import__(modname) + mod = sys.modules[modname] + + # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). + parts = classname.split(".") + pdb_cls = getattr(mod, parts[0]) + for part in parts[1:]: + pdb_cls = getattr(pdb_cls, part) + except Exception as exc: + value = ":".join((modname, classname)) + raise UsageError( + f"--pdbcls: could not import {value!r}: {exc}" + ) from exc + else: + import pdb + + pdb_cls = pdb.Pdb + + wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) + cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) + return wrapped_cls + + @classmethod + def _get_pdb_wrapper_class(cls, pdb_cls, capman: Optional["CaptureManager"]): + import _pytest.config + + # Type ignored because mypy doesn't support "dynamic" + # inheritance like this. + class PytestPdbWrapper(pdb_cls): # type: ignore[valid-type,misc] + _pytest_capman = capman + _continued = False + + def do_debug(self, arg): + cls._recursive_debug += 1 + ret = super().do_debug(arg) + cls._recursive_debug -= 1 + return ret + + def do_continue(self, arg): + ret = super().do_continue(arg) + if cls._recursive_debug == 0: + assert cls._config is not None + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + capman = self._pytest_capman + capturing = pytestPDB._is_capturing(capman) + if capturing: + if capturing == "global": + tw.sep(">", "PDB continue (IO-capturing resumed)") + else: + tw.sep( + ">", + "PDB continue (IO-capturing resumed for %s)" + % capturing, + ) + assert capman is not None + capman.resume() + else: + tw.sep(">", "PDB continue") + assert cls._pluginmanager is not None + cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) + self._continued = True + return ret + + do_c = do_cont = do_continue + + def do_quit(self, arg): + """Raise Exit outcome when quit command is used in pdb. + + This is a bit of a hack - it would be better if BdbQuit + could be handled, but this would require to wrap the + whole pytest run, and adjust the report etc. + """ + ret = super().do_quit(arg) + + if cls._recursive_debug == 0: + outcomes.exit("Quitting debugger") + + return ret + + do_q = do_quit + do_exit = do_quit + + def setup(self, f, tb): + """Suspend on setup(). + + Needed after do_continue resumed, and entering another + breakpoint again. + """ + ret = super().setup(f, tb) + if not ret and self._continued: + # pdb.setup() returns True if the command wants to exit + # from the interaction: do not suspend capturing then. + if self._pytest_capman: + self._pytest_capman.suspend_global_capture(in_=True) + return ret + + def get_stack(self, f, t): + stack, i = super().get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return stack, i + + return PytestPdbWrapper + + @classmethod + def _init_pdb(cls, method, *args, **kwargs): + """Initialize PDB debugging, dropping any IO capturing.""" + import _pytest.config + + if cls._pluginmanager is None: + capman: Optional[CaptureManager] = None + else: + capman = cls._pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend(in_=True) + + if cls._config: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + if cls._recursive_debug == 0: + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + else: + capturing = cls._is_capturing(capman) + if capturing == "global": + tw.sep(">", f"PDB {method} (IO-capturing turned off)") + elif capturing: + tw.sep( + ">", + "PDB %s (IO-capturing turned off for %s)" + % (method, capturing), + ) + else: + tw.sep(">", f"PDB {method}") + + _pdb = cls._import_pdb_cls(capman)(**kwargs) + + if cls._pluginmanager: + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) + return _pdb + + @classmethod + def set_trace(cls, *args, **kwargs) -> None: + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + frame = sys._getframe().f_back + _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb.set_trace(frame) + + +class PdbInvoke: + def pytest_exception_interact( + self, node: Node, call: "CallInfo[Any]", report: BaseReport + ) -> None: + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stdout.write(err) + assert call.excinfo is not None + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: + tb = _postmortem_traceback(excinfo) + post_mortem(tb) + + +class PdbTrace: + @hookimpl(hookwrapper=True) + def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, None, None]: + wrap_pytest_function_for_tracing(pyfuncitem) + yield + + +def wrap_pytest_function_for_tracing(pyfuncitem): + """Change the Python function object of the given Function item by a + wrapper which actually enters pdb before calling the python function + itself, effectively leaving the user in the pdb prompt in the first + statement of the function.""" + _pdb = pytestPDB._init_pdb("runcall") + testfunction = pyfuncitem.obj + + # we can't just return `partial(pdb.runcall, testfunction)` because (on + # python < 3.7.4) runcall's first param is `func`, which means we'd get + # an exception if one of the kwargs to testfunction was called `func`. + @functools.wraps(testfunction) + def wrapper(*args, **kwargs): + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper + + +def maybe_wrap_pytest_function_for_tracing(pyfuncitem): + """Wrap the given pytestfunct item for tracing support if --trace was given in + the command line.""" + if pyfuncitem.config.getvalue("trace"): + wrap_pytest_function_for_tracing(pyfuncitem) + + +def _enter_pdb( + node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport +) -> BaseReport: + # XXX we re-use the TerminalReporter's terminalwriter + # because this seems to avoid some encoding related troubles + # for not completely clear reasons. + tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw.line() + + showcapture = node.config.option.showcapture + + for sectionname, content in ( + ("stdout", rep.capstdout), + ("stderr", rep.capstderr), + ("log", rep.caplog), + ): + if showcapture in (sectionname, "all") and content: + tw.sep(">", "captured " + sectionname) + if content[-1:] == "\n": + content = content[:-1] + tw.line(content) + + tw.sep(">", "traceback") + rep.toterminal(tw) + tw.sep(">", "entering PDB") + tb = _postmortem_traceback(excinfo) + rep._pdbshown = True # type: ignore[attr-defined] + post_mortem(tb) + return rep + + +def _postmortem_traceback(excinfo: ExceptionInfo[BaseException]) -> types.TracebackType: + from doctest import UnexpectedException + + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.excinfo[2] + else: + assert excinfo._excinfo is not None + return excinfo._excinfo[2] + + +def post_mortem(t: types.TracebackType) -> None: + p = pytestPDB._init_pdb("post_mortem") + p.reset() + p.interaction(None, t) + if p.quitting: + outcomes.exit("Quitting debugger") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/deprecated.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/deprecated.py new file mode 100644 index 0000000..19b31d6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/deprecated.py @@ -0,0 +1,87 @@ +"""Deprecation messages and bits of code used elsewhere in the codebase that +is planned to be removed in the next pytest release. + +Keeping it in a central location makes it easy to track what is deprecated and should +be removed when the time comes. + +All constants defined in this module should be either instances of +:class:`PytestWarning`, or :class:`UnformattedWarning` +in case of warnings which need to format their messages. +""" +from warnings import warn + +from _pytest.warning_types import PytestDeprecationWarning +from _pytest.warning_types import UnformattedWarning + +# set of plugins which have been integrated into the core; we use this list to ignore +# them during registration to avoid conflicts +DEPRECATED_EXTERNAL_PLUGINS = { + "pytest_catchlog", + "pytest_capturelog", + "pytest_faulthandler", +} + + +FILLFUNCARGS = UnformattedWarning( + PytestDeprecationWarning, + "{name} is deprecated, use " + "function._request._fillfixtures() instead if you cannot avoid reaching into internals.", +) + +PYTEST_COLLECT_MODULE = UnformattedWarning( + PytestDeprecationWarning, + "pytest.collect.{name} was moved to pytest.{name}\n" + "Please update to the new name.", +) + +YIELD_FIXTURE = PytestDeprecationWarning( + "@pytest.yield_fixture is deprecated.\n" + "Use @pytest.fixture instead; they are the same." +) + +MINUS_K_DASH = PytestDeprecationWarning( + "The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead." +) + +MINUS_K_COLON = PytestDeprecationWarning( + "The `-k 'expr:'` syntax to -k is deprecated.\n" + "Please open an issue if you use this and want a replacement." +) + +WARNING_CAPTURED_HOOK = PytestDeprecationWarning( + "The pytest_warning_captured is deprecated and will be removed in a future release.\n" + "Please use pytest_warning_recorded instead." +) + +FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestDeprecationWarning( + "The gethookproxy() and isinitpath() methods of FSCollector and Package are deprecated; " + "use self.session.gethookproxy() and self.session.isinitpath() instead. " +) + +STRICT_OPTION = PytestDeprecationWarning( + "The --strict option is deprecated, use --strict-markers instead." +) + +PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") + + +# You want to make some `__init__` or function "private". +# +# def my_private_function(some, args): +# ... +# +# Do this: +# +# def my_private_function(some, args, *, _ispytest: bool = False): +# check_ispytest(_ispytest) +# ... +# +# Change all internal/allowed calls to +# +# my_private_function(some, args, _ispytest=True) +# +# All other calls will get the default _ispytest=False and trigger +# the warning (possibly error in the future). +def check_ispytest(ispytest: bool) -> None: + if not ispytest: + warn(PRIVATE, stacklevel=3) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/doctest.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/doctest.py new file mode 100644 index 0000000..64e8f0e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/doctest.py @@ -0,0 +1,724 @@ +"""Discover and run doctests in modules and test files.""" +import bdb +import inspect +import platform +import sys +import traceback +import types +import warnings +from contextlib import contextmanager +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Pattern +from typing import Sequence +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import py.path + +import pytest +from _pytest import outcomes +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import safe_getattr +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureRequest +from _pytest.nodes import Collector +from _pytest.outcomes import OutcomeException +from _pytest.pathlib import import_path +from _pytest.python_api import approx +from _pytest.warning_types import PytestWarning + +if TYPE_CHECKING: + import doctest + +DOCTEST_REPORT_CHOICE_NONE = "none" +DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" +DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" +DOCTEST_REPORT_CHOICE_UDIFF = "udiff" +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure" + +DOCTEST_REPORT_CHOICES = ( + DOCTEST_REPORT_CHOICE_NONE, + DOCTEST_REPORT_CHOICE_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF, + DOCTEST_REPORT_CHOICE_UDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, +) + +# Lazy definition of runner class +RUNNER_CLASS = None +# Lazy definition of output checker class +CHECKER_CLASS: Optional[Type["doctest.OutputChecker"]] = None + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "doctest_optionflags", + "option flags for doctests", + type="args", + default=["ELLIPSIS"], + ) + parser.addini( + "doctest_encoding", "encoding used for doctest files", default="utf-8" + ) + group = parser.getgroup("collect") + group.addoption( + "--doctest-modules", + action="store_true", + default=False, + help="run doctests in all .py modules", + dest="doctestmodules", + ) + group.addoption( + "--doctest-report", + type=str.lower, + default="udiff", + help="choose another output format for diffs on doctest failure", + choices=DOCTEST_REPORT_CHOICES, + dest="doctestreport", + ) + group.addoption( + "--doctest-glob", + action="append", + default=[], + metavar="pat", + help="doctests file matching pattern, default: test*.txt", + dest="doctestglob", + ) + group.addoption( + "--doctest-ignore-import-errors", + action="store_true", + default=False, + help="ignore doctest ImportErrors", + dest="doctest_ignore_import_errors", + ) + group.addoption( + "--doctest-continue-on-failure", + action="store_true", + default=False, + help="for a given doctest, continue to run after the first failure", + dest="doctest_continue_on_failure", + ) + + +def pytest_unconfigure() -> None: + global RUNNER_CLASS + + RUNNER_CLASS = None + + +def pytest_collect_file( + path: py.path.local, parent: Collector, +) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: + config = parent.config + if path.ext == ".py": + if config.option.doctestmodules and not _is_setup_py(path): + mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path) + return mod + elif _is_doctest(config, path, parent): + txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path) + return txt + return None + + +def _is_setup_py(path: py.path.local) -> bool: + if path.basename != "setup.py": + return False + contents = path.read_binary() + return b"setuptools" in contents or b"distutils" in contents + + +def _is_doctest(config: Config, path: py.path.local, parent) -> bool: + if path.ext in (".txt", ".rst") and parent.session.isinitpath(path): + return True + globs = config.getoption("doctestglob") or ["test*.txt"] + for glob in globs: + if path.check(fnmatch=glob): + return True + return False + + +class ReprFailDoctest(TerminalRepr): + def __init__( + self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]] + ) -> None: + self.reprlocation_lines = reprlocation_lines + + def toterminal(self, tw: TerminalWriter) -> None: + for reprlocation, lines in self.reprlocation_lines: + for line in lines: + tw.line(line) + reprlocation.toterminal(tw) + + +class MultipleDoctestFailures(Exception): + def __init__(self, failures: Sequence["doctest.DocTestFailure"]) -> None: + super().__init__() + self.failures = failures + + +def _init_runner_class() -> Type["doctest.DocTestRunner"]: + import doctest + + class PytestDoctestRunner(doctest.DebugRunner): + """Runner to collect failures. + + Note that the out variable in this case is a list instead of a + stdout-like object. + """ + + def __init__( + self, + checker: Optional["doctest.OutputChecker"] = None, + verbose: Optional[bool] = None, + optionflags: int = 0, + continue_on_failure: bool = True, + ) -> None: + doctest.DebugRunner.__init__( + self, checker=checker, verbose=verbose, optionflags=optionflags + ) + self.continue_on_failure = continue_on_failure + + def report_failure( + self, out, test: "doctest.DocTest", example: "doctest.Example", got: str, + ) -> None: + failure = doctest.DocTestFailure(test, example, got) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + def report_unexpected_exception( + self, + out, + test: "doctest.DocTest", + example: "doctest.Example", + exc_info: Tuple[Type[BaseException], BaseException, types.TracebackType], + ) -> None: + if isinstance(exc_info[1], OutcomeException): + raise exc_info[1] + if isinstance(exc_info[1], bdb.BdbQuit): + outcomes.exit("Quitting debugger") + failure = doctest.UnexpectedException(test, example, exc_info) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + return PytestDoctestRunner + + +def _get_runner( + checker: Optional["doctest.OutputChecker"] = None, + verbose: Optional[bool] = None, + optionflags: int = 0, + continue_on_failure: bool = True, +) -> "doctest.DocTestRunner": + # We need this in order to do a lazy import on doctest + global RUNNER_CLASS + if RUNNER_CLASS is None: + RUNNER_CLASS = _init_runner_class() + # Type ignored because the continue_on_failure argument is only defined on + # PytestDoctestRunner, which is lazily defined so can't be used as a type. + return RUNNER_CLASS( # type: ignore + checker=checker, + verbose=verbose, + optionflags=optionflags, + continue_on_failure=continue_on_failure, + ) + + +class DoctestItem(pytest.Item): + def __init__( + self, + name: str, + parent: "Union[DoctestTextfile, DoctestModule]", + runner: Optional["doctest.DocTestRunner"] = None, + dtest: Optional["doctest.DocTest"] = None, + ) -> None: + super().__init__(name, parent) + self.runner = runner + self.dtest = dtest + self.obj = None + self.fixture_request: Optional[FixtureRequest] = None + + @classmethod + def from_parent( # type: ignore + cls, + parent: "Union[DoctestTextfile, DoctestModule]", + *, + name: str, + runner: "doctest.DocTestRunner", + dtest: "doctest.DocTest", + ): + # incompatible signature due to to imposed limits on sublcass + """The public named constructor.""" + return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) + + def setup(self) -> None: + if self.dtest is not None: + self.fixture_request = _setup_fixtures(self) + globs = dict(getfixture=self.fixture_request.getfixturevalue) + for name, value in self.fixture_request.getfixturevalue( + "doctest_namespace" + ).items(): + globs[name] = value + self.dtest.globs.update(globs) + + def runtest(self) -> None: + assert self.dtest is not None + assert self.runner is not None + _check_all_skipped(self.dtest) + self._disable_output_capturing_for_darwin() + failures: List["doctest.DocTestFailure"] = [] + # Type ignored because we change the type of `out` from what + # doctest expects. + self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] + if failures: + raise MultipleDoctestFailures(failures) + + def _disable_output_capturing_for_darwin(self) -> None: + """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" + if platform.system() != "Darwin": + return + capman = self.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException], + ) -> Union[str, TerminalRepr]: + import doctest + + failures: Optional[ + Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] + ] = (None) + if isinstance( + excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) + ): + failures = [excinfo.value] + elif isinstance(excinfo.value, MultipleDoctestFailures): + failures = excinfo.value.failures + + if failures is not None: + reprlocation_lines = [] + for failure in failures: + example = failure.example + test = failure.test + filename = test.filename + if test.lineno is None: + lineno = None + else: + lineno = test.lineno + example.lineno + 1 + message = type(failure).__name__ + # TODO: ReprFileLocation doesn't expect a None lineno. + reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] + checker = _get_checker() + report_choice = _get_report_choice( + self.config.getoption("doctestreport") + ) + if lineno is not None: + assert failure.test.docstring is not None + lines = failure.test.docstring.splitlines(False) + # add line numbers to the left of the error message + assert test.lineno is not None + lines = [ + "%03d %s" % (i + test.lineno + 1, x) + for (i, x) in enumerate(lines) + ] + # trim docstring error lines to 10 + lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] + else: + lines = [ + "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" + ] + indent = ">>>" + for line in example.source.splitlines(): + lines.append(f"??? {indent} {line}") + indent = "..." + if isinstance(failure, doctest.DocTestFailure): + lines += checker.output_difference( + example, failure.got, report_choice + ).split("\n") + else: + inner_excinfo = ExceptionInfo(failure.exc_info) + lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] + lines += [ + x.strip("\n") + for x in traceback.format_exception(*failure.exc_info) + ] + reprlocation_lines.append((reprlocation, lines)) + return ReprFailDoctest(reprlocation_lines) + else: + return super().repr_failure(excinfo) + + def reportinfo(self): + assert self.dtest is not None + return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name + + +def _get_flag_lookup() -> Dict[str, int]: + import doctest + + return dict( + DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1, + DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE, + NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE, + ELLIPSIS=doctest.ELLIPSIS, + IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL, + COMPARISON_FLAGS=doctest.COMPARISON_FLAGS, + ALLOW_UNICODE=_get_allow_unicode_flag(), + ALLOW_BYTES=_get_allow_bytes_flag(), + NUMBER=_get_number_flag(), + ) + + +def get_optionflags(parent): + optionflags_str = parent.config.getini("doctest_optionflags") + flag_lookup_table = _get_flag_lookup() + flag_acc = 0 + for flag in optionflags_str: + flag_acc |= flag_lookup_table[flag] + return flag_acc + + +def _get_continue_on_failure(config): + continue_on_failure = config.getvalue("doctest_continue_on_failure") + if continue_on_failure: + # We need to turn off this if we use pdb since we should stop at + # the first failure. + if config.getvalue("usepdb"): + continue_on_failure = False + return continue_on_failure + + +class DoctestTextfile(pytest.Module): + obj = None + + def collect(self) -> Iterable[DoctestItem]: + import doctest + + # Inspired by doctest.testfile; ideally we would use it directly, + # but it doesn't support passing a custom checker. + encoding = self.config.getini("doctest_encoding") + text = self.fspath.read_text(encoding) + filename = str(self.fspath) + name = self.fspath.basename + globs = {"__name__": "__main__"} + + optionflags = get_optionflags(self) + + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + parser = doctest.DocTestParser() + test = parser.get_doctest(text, globs, name, filename, 0) + if test.examples: + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _check_all_skipped(test: "doctest.DocTest") -> None: + """Raise pytest.skip() if all examples in the given DocTest have the SKIP + option set.""" + import doctest + + all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) + if all_skipped: + pytest.skip("all tests skipped by +SKIP option") + + +def _is_mocked(obj: object) -> bool: + """Return if an object is possibly a mock object by checking the + existence of a highly improbable attribute.""" + return ( + safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) + is not None + ) + + +@contextmanager +def _patch_unwrap_mock_aware() -> Generator[None, None, None]: + """Context manager which replaces ``inspect.unwrap`` with a version + that's aware of mock objects and doesn't recurse into them.""" + real_unwrap = inspect.unwrap + + def _mock_aware_unwrap( + func: Callable[..., Any], *, stop: Optional[Callable[[Any], Any]] = None + ) -> Any: + try: + if stop is None or stop is _is_mocked: + return real_unwrap(func, stop=_is_mocked) + _stop = stop + return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) + except Exception as e: + warnings.warn( + "Got %r when unwrapping %r. This is usually caused " + "by a violation of Python's object protocol; see e.g. " + "https://github.com/pytest-dev/pytest/issues/5080" % (e, func), + PytestWarning, + ) + raise + + inspect.unwrap = _mock_aware_unwrap + try: + yield + finally: + inspect.unwrap = real_unwrap + + +class DoctestModule(pytest.Module): + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. + + https://github.com/pytest-dev/pytest/issues/3456 + https://bugs.python.org/issue25532 + """ + + def _find_lineno(self, obj, source_lines): + """Doctest code does not take into account `@property`, this + is a hackish way to fix it. + + https://bugs.python.org/issue17446 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + # Type ignored because this is a private function. + return doctest.DocTestFinder._find_lineno( # type: ignore + self, obj, source_lines, + ) + + def _find( + self, tests, obj, name, module, source_lines, globs, seen + ) -> None: + if _is_mocked(obj): + return + with _patch_unwrap_mock_aware(): + + # Type ignored because this is a private function. + doctest.DocTestFinder._find( # type: ignore + self, tests, obj, name, module, source_lines, globs, seen + ) + + if self.fspath.basename == "conftest.py": + module = self.config.pluginmanager._importconftest( + self.fspath, self.config.getoption("importmode") + ) + else: + try: + module = import_path(self.fspath) + except ImportError: + if self.config.getvalue("doctest_ignore_import_errors"): + pytest.skip("unable to import module %r" % self.fspath) + else: + raise + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self) + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _setup_fixtures(doctest_item: DoctestItem) -> FixtureRequest: + """Used by DoctestTextfile and DoctestItem to setup fixture information.""" + + def func() -> None: + pass + + doctest_item.funcargs = {} # type: ignore[attr-defined] + fm = doctest_item.session._fixturemanager + doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined] + node=doctest_item, func=func, cls=None, funcargs=False + ) + fixture_request = FixtureRequest(doctest_item, _ispytest=True) + fixture_request._fillfixtures() + return fixture_request + + +def _init_checker_class() -> Type["doctest.OutputChecker"]: + import doctest + import re + + class LiteralsOutputChecker(doctest.OutputChecker): + # Based on doctest_nose_plugin.py from the nltk project + # (https://github.com/nltk/nltk) and on the "numtest" doctest extension + # by Sebastien Boisgerault (https://github.com/boisgera/numtest). + + _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) + _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE) + _number_re = re.compile( + r""" + (?P + (?P + (?P [+-]?\d*)\.(?P\d+) + | + (?P [+-]?\d+)\. + ) + (?: + [Ee] + (?P [+-]?\d+) + )? + | + (?P [+-]?\d+) + (?: + [Ee] + (?P [+-]?\d+) + ) + ) + """, + re.VERBOSE, + ) + + def check_output(self, want: str, got: str, optionflags: int) -> bool: + if doctest.OutputChecker.check_output(self, want, got, optionflags): + return True + + allow_unicode = optionflags & _get_allow_unicode_flag() + allow_bytes = optionflags & _get_allow_bytes_flag() + allow_number = optionflags & _get_number_flag() + + if not allow_unicode and not allow_bytes and not allow_number: + return False + + def remove_prefixes(regex: Pattern[str], txt: str) -> str: + return re.sub(regex, r"\1\2", txt) + + if allow_unicode: + want = remove_prefixes(self._unicode_literal_re, want) + got = remove_prefixes(self._unicode_literal_re, got) + + if allow_bytes: + want = remove_prefixes(self._bytes_literal_re, want) + got = remove_prefixes(self._bytes_literal_re, got) + + if allow_number: + got = self._remove_unwanted_precision(want, got) + + return doctest.OutputChecker.check_output(self, want, got, optionflags) + + def _remove_unwanted_precision(self, want: str, got: str) -> str: + wants = list(self._number_re.finditer(want)) + gots = list(self._number_re.finditer(got)) + if len(wants) != len(gots): + return got + offset = 0 + for w, g in zip(wants, gots): + fraction: Optional[str] = w.group("fraction") + exponent: Optional[str] = w.group("exponent1") + if exponent is None: + exponent = w.group("exponent2") + if fraction is None: + precision = 0 + else: + precision = len(fraction) + if exponent is not None: + precision -= int(exponent) + if float(w.group()) == approx(float(g.group()), abs=10 ** -precision): + # They're close enough. Replace the text we actually + # got with the text we want, so that it will match when we + # check the string literally. + got = ( + got[: g.start() + offset] + w.group() + got[g.end() + offset :] + ) + offset += w.end() - w.start() - (g.end() - g.start()) + return got + + return LiteralsOutputChecker + + +def _get_checker() -> "doctest.OutputChecker": + """Return a doctest.OutputChecker subclass that supports some + additional options: + + * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b'' + prefixes (respectively) in string literals. Useful when the same + doctest should run in Python 2 and Python 3. + + * NUMBER to ignore floating-point differences smaller than the + precision of the literal number in the doctest. + + An inner class is used to avoid importing "doctest" at the module + level. + """ + global CHECKER_CLASS + if CHECKER_CLASS is None: + CHECKER_CLASS = _init_checker_class() + return CHECKER_CLASS() + + +def _get_allow_unicode_flag() -> int: + """Register and return the ALLOW_UNICODE flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_UNICODE") + + +def _get_allow_bytes_flag() -> int: + """Register and return the ALLOW_BYTES flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_BYTES") + + +def _get_number_flag() -> int: + """Register and return the NUMBER flag.""" + import doctest + + return doctest.register_optionflag("NUMBER") + + +def _get_report_choice(key: str) -> int: + """Return the actual `doctest` module flag value. + + We want to do it as late as possible to avoid importing `doctest` and all + its dependencies when parsing options, as it adds overhead and breaks tests. + """ + import doctest + + return { + DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, + DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, + DOCTEST_REPORT_CHOICE_NONE: 0, + }[key] + + +@pytest.fixture(scope="session") +def doctest_namespace() -> Dict[str, Any]: + """Fixture that returns a :py:class:`dict` that will be injected into the + namespace of doctests.""" + return dict() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/faulthandler.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/faulthandler.py new file mode 100644 index 0000000..ff673b5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/faulthandler.py @@ -0,0 +1,116 @@ +import io +import os +import sys +from typing import Generator +from typing import TextIO + +import pytest +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.store import StoreKey + + +fault_handler_stderr_key = StoreKey[TextIO]() + + +def pytest_addoption(parser: Parser) -> None: + help = ( + "Dump the traceback of all threads if a test takes " + "more than TIMEOUT seconds to finish." + ) + parser.addini("faulthandler_timeout", help, default=0.0) + + +def pytest_configure(config: Config) -> None: + import faulthandler + + if not faulthandler.is_enabled(): + # faulthhandler is not enabled, so install plugin that does the actual work + # of enabling faulthandler before each test executes. + config.pluginmanager.register(FaultHandlerHooks(), "faulthandler-hooks") + else: + # Do not handle dumping to stderr if faulthandler is already enabled, so warn + # users that the option is being ignored. + timeout = FaultHandlerHooks.get_timeout_config_value(config) + if timeout > 0: + config.issue_config_time_warning( + pytest.PytestConfigWarning( + "faulthandler module enabled before pytest configuration step, " + "'faulthandler_timeout' option ignored" + ), + stacklevel=2, + ) + + +class FaultHandlerHooks: + """Implements hooks that will actually install fault handler before tests execute, + as well as correctly handle pdb and internal errors.""" + + def pytest_configure(self, config: Config) -> None: + import faulthandler + + stderr_fd_copy = os.dup(self._get_stderr_fileno()) + config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + faulthandler.enable(file=config._store[fault_handler_stderr_key]) + + def pytest_unconfigure(self, config: Config) -> None: + import faulthandler + + faulthandler.disable() + # close our dup file installed during pytest_configure + # re-enable the faulthandler, attaching it to the default sys.stderr + # so we can see crashes after pytest has finished, usually during + # garbage collection during interpreter shutdown + config._store[fault_handler_stderr_key].close() + del config._store[fault_handler_stderr_key] + faulthandler.enable(file=self._get_stderr_fileno()) + + @staticmethod + def _get_stderr_fileno(): + try: + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno + except (AttributeError, io.UnsupportedOperation): + # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. + # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors + # This is potentially dangerous, but the best we can do. + return sys.__stderr__.fileno() + + @staticmethod + def get_timeout_config_value(config): + return float(config.getini("faulthandler_timeout") or 0.0) + + @pytest.hookimpl(hookwrapper=True, trylast=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: + timeout = self.get_timeout_config_value(item.config) + stderr = item.config._store[fault_handler_stderr_key] + if timeout > 0 and stderr is not None: + import faulthandler + + faulthandler.dump_traceback_later(timeout, file=stderr) + try: + yield + finally: + faulthandler.cancel_dump_traceback_later() + else: + yield + + @pytest.hookimpl(tryfirst=True) + def pytest_enter_pdb(self) -> None: + """Cancel any traceback dumping due to timeout before entering pdb.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() + + @pytest.hookimpl(tryfirst=True) + def pytest_exception_interact(self) -> None: + """Cancel any traceback dumping due to an interactive exception being + raised.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/fixtures.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/fixtures.py new file mode 100644 index 0000000..273bcaf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/fixtures.py @@ -0,0 +1,1680 @@ +import functools +import inspect +import os +import sys +import warnings +from collections import defaultdict +from collections import deque +from types import TracebackType +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generator +from typing import Generic +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr +import py + +import _pytest +from _pytest import nodes +from _pytest._code import getfslineno +from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import _format_args +from _pytest.compat import _PytestWrapper +from _pytest.compat import assert_never +from _pytest.compat import final +from _pytest.compat import get_real_func +from _pytest.compat import get_real_method +from _pytest.compat import getfuncargnames +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import is_generator +from _pytest.compat import NOTSET +from _pytest.compat import safe_getattr +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import FILLFUNCARGS +from _pytest.deprecated import YIELD_FIXTURE +from _pytest.mark import Mark +from _pytest.mark import ParameterSet +from _pytest.mark.structures import MarkDecorator +from _pytest.outcomes import fail +from _pytest.outcomes import TEST_OUTCOME +from _pytest.pathlib import absolutepath +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from typing import Deque + from typing import NoReturn + from typing_extensions import Literal + + from _pytest.main import Session + from _pytest.python import CallSpec2 + from _pytest.python import Function + from _pytest.python import Metafunc + + _Scope = Literal["session", "package", "module", "class", "function"] + + +# The value of the fixture -- return/yield of the fixture function (type variable). +_FixtureValue = TypeVar("_FixtureValue") +# The type of the fixture function (type variable). +_FixtureFunction = TypeVar("_FixtureFunction", bound=Callable[..., object]) +# The type of a fixture function (type alias generic in fixture value). +_FixtureFunc = Union[ + Callable[..., _FixtureValue], Callable[..., Generator[_FixtureValue, None, None]] +] +# The type of FixtureDef.cached_result (type alias generic in fixture value). +_FixtureCachedResult = Union[ + Tuple[ + # The result. + _FixtureValue, + # Cache key. + object, + None, + ], + Tuple[ + None, + # Cache key. + object, + # Exc info if raised. + Tuple[Type[BaseException], BaseException, TracebackType], + ], +] + + +@attr.s(frozen=True) +class PseudoFixtureDef(Generic[_FixtureValue]): + cached_result = attr.ib(type="_FixtureCachedResult[_FixtureValue]") + scope = attr.ib(type="_Scope") + + +def pytest_sessionstart(session: "Session") -> None: + session._fixturemanager = FixtureManager(session) + + +def get_scope_package(node, fixturedef: "FixtureDef[object]"): + import pytest + + cls = pytest.Package + current = node + fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py") + while current and ( + type(current) is not cls or fixture_package_name != current.nodeid + ): + current = current.parent + if current is None: + return node.session + return current + + +def get_scope_node( + node: nodes.Node, scope: "_Scope" +) -> Optional[Union[nodes.Item, nodes.Collector]]: + import _pytest.python + + if scope == "function": + return node.getparent(nodes.Item) + elif scope == "class": + return node.getparent(_pytest.python.Class) + elif scope == "module": + return node.getparent(_pytest.python.Module) + elif scope == "package": + return node.getparent(_pytest.python.Package) + elif scope == "session": + return node.getparent(_pytest.main.Session) + else: + assert_never(scope) + + +# Used for storing artificial fixturedefs for direct parametrization. +name2pseudofixturedef_key = StoreKey[Dict[str, "FixtureDef[Any]"]]() + + +def add_funcarg_pseudo_fixture_def( + collector: nodes.Collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" +) -> None: + # This function will transform all collected calls to functions + # if they use direct funcargs (i.e. direct parametrization) + # because we want later test execution to be able to rely on + # an existing FixtureDef structure for all arguments. + # XXX we can probably avoid this algorithm if we modify CallSpec2 + # to directly care for creating the fixturedefs within its methods. + if not metafunc._calls[0].funcargs: + # This function call does not have direct parametrization. + return + # Collect funcargs of all callspecs into a list of values. + arg2params: Dict[str, List[object]] = {} + arg2scope: Dict[str, _Scope] = {} + for callspec in metafunc._calls: + for argname, argvalue in callspec.funcargs.items(): + assert argname not in callspec.params + callspec.params[argname] = argvalue + arg2params_list = arg2params.setdefault(argname, []) + callspec.indices[argname] = len(arg2params_list) + arg2params_list.append(argvalue) + if argname not in arg2scope: + scopenum = callspec._arg2scopenum.get(argname, scopenum_function) + arg2scope[argname] = scopes[scopenum] + callspec.funcargs.clear() + + # Register artificial FixtureDef's so that later at test execution + # time we can rely on a proper FixtureDef to exist for fixture setup. + arg2fixturedefs = metafunc._arg2fixturedefs + for argname, valuelist in arg2params.items(): + # If we have a scope that is higher than function, we need + # to make sure we only ever create an according fixturedef on + # a per-scope basis. We thus store and cache the fixturedef on the + # node related to the scope. + scope = arg2scope[argname] + node = None + if scope != "function": + node = get_scope_node(collector, scope) + if node is None: + assert scope == "class" and isinstance(collector, _pytest.python.Module) + # Use module-level collector for class-scope (for now). + node = collector + if node is None: + name2pseudofixturedef = None + else: + default: Dict[str, FixtureDef[Any]] = {} + name2pseudofixturedef = node._store.setdefault( + name2pseudofixturedef_key, default + ) + if name2pseudofixturedef is not None and argname in name2pseudofixturedef: + arg2fixturedefs[argname] = [name2pseudofixturedef[argname]] + else: + fixturedef = FixtureDef( + fixturemanager=fixturemanager, + baseid="", + argname=argname, + func=get_direct_param_fixture_func, + scope=arg2scope[argname], + params=valuelist, + unittest=False, + ids=None, + ) + arg2fixturedefs[argname] = [fixturedef] + if name2pseudofixturedef is not None: + name2pseudofixturedef[argname] = fixturedef + + +def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: + """Return fixturemarker or None if it doesn't exist or raised + exceptions.""" + try: + fixturemarker: Optional[FixtureFunctionMarker] = getattr( + obj, "_pytestfixturefunction", None + ) + except TEST_OUTCOME: + # some objects raise errors like request (from flask import request) + # we don't expect them to be fixture functions + return None + return fixturemarker + + +# Parametrized fixture key, helper alias for code below. +_Key = Tuple[object, ...] + + +def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_Key]: + """Return list of keys for all parametrized arguments which match + the specified scope. """ + assert scopenum < scopenum_function # function + try: + callspec = item.callspec # type: ignore[attr-defined] + except AttributeError: + pass + else: + cs: CallSpec2 = callspec + # cs.indices.items() is random order of argnames. Need to + # sort this so that different calls to + # get_parametrized_fixture_keys will be deterministic. + for argname, param_index in sorted(cs.indices.items()): + if cs._arg2scopenum[argname] != scopenum: + continue + if scopenum == 0: # session + key: _Key = (argname, param_index) + elif scopenum == 1: # package + key = (argname, param_index, item.fspath.dirpath()) + elif scopenum == 2: # module + key = (argname, param_index, item.fspath) + elif scopenum == 3: # class + item_cls = item.cls # type: ignore[attr-defined] + key = (argname, param_index, item.fspath, item_cls) + yield key + + +# Algorithm for sorting on a per-parametrized resource setup basis. +# It is called for scopenum==0 (session) first and performs sorting +# down to the lower scopes such as to minimize number of "high scope" +# setups and teardowns. + + +def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]] = {} + items_by_argkey: Dict[int, Dict[_Key, Deque[nodes.Item]]] = {} + for scopenum in range(0, scopenum_function): + d: Dict[nodes.Item, Dict[_Key, None]] = {} + argkeys_cache[scopenum] = d + item_d: Dict[_Key, Deque[nodes.Item]] = defaultdict(deque) + items_by_argkey[scopenum] = item_d + for item in items: + keys = dict.fromkeys(get_parametrized_fixture_keys(item, scopenum), None) + if keys: + d[item] = keys + for key in keys: + item_d[key].append(item) + items_dict = dict.fromkeys(items, None) + return list(reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, 0)) + + +def fix_cache_order( + item: nodes.Item, + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], +) -> None: + for scopenum in range(0, scopenum_function): + for key in argkeys_cache[scopenum].get(item, []): + items_by_argkey[scopenum][key].appendleft(item) + + +def reorder_items_atscope( + items: Dict[nodes.Item, None], + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], + scopenum: int, +) -> Dict[nodes.Item, None]: + if scopenum >= scopenum_function or len(items) < 3: + return items + ignore: Set[Optional[_Key]] = set() + items_deque = deque(items) + items_done: Dict[nodes.Item, None] = {} + scoped_items_by_argkey = items_by_argkey[scopenum] + scoped_argkeys_cache = argkeys_cache[scopenum] + while items_deque: + no_argkey_group: Dict[nodes.Item, None] = {} + slicing_argkey = None + while items_deque: + item = items_deque.popleft() + if item in items_done or item in no_argkey_group: + continue + argkeys = dict.fromkeys( + (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None + ) + if not argkeys: + no_argkey_group[item] = None + else: + slicing_argkey, _ = argkeys.popitem() + # We don't have to remove relevant items from later in the + # deque because they'll just be ignored. + matching_items = [ + i for i in scoped_items_by_argkey[slicing_argkey] if i in items + ] + for i in reversed(matching_items): + fix_cache_order(i, argkeys_cache, items_by_argkey) + items_deque.appendleft(i) + break + if no_argkey_group: + no_argkey_group = reorder_items_atscope( + no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1 + ) + for item in no_argkey_group: + items_done[item] = None + ignore.add(slicing_argkey) + return items_done + + +def _fillfuncargs(function: "Function") -> None: + """Fill missing fixtures for a test function, old public API (deprecated).""" + warnings.warn(FILLFUNCARGS.format(name="pytest._fillfuncargs()"), stacklevel=2) + _fill_fixtures_impl(function) + + +def fillfixtures(function: "Function") -> None: + """Fill missing fixtures for a test function (deprecated).""" + warnings.warn( + FILLFUNCARGS.format(name="_pytest.fixtures.fillfixtures()"), stacklevel=2 + ) + _fill_fixtures_impl(function) + + +def _fill_fixtures_impl(function: "Function") -> None: + """Internal implementation to fill fixtures on the given function object.""" + try: + request = function._request + except AttributeError: + # XXX this special code path is only expected to execute + # with the oejskit plugin. It uses classes with funcargs + # and we thus have to work a bit to allow this. + fm = function.session._fixturemanager + assert function.parent is not None + fi = fm.getfixtureinfo(function.parent, function.obj, None) + function._fixtureinfo = fi + request = function._request = FixtureRequest(function, _ispytest=True) + request._fillfixtures() + # Prune out funcargs for jstests. + newfuncargs = {} + for name in fi.argnames: + newfuncargs[name] = function.funcargs[name] + function.funcargs = newfuncargs + else: + request._fillfixtures() + + +def get_direct_param_fixture_func(request): + return request.param + + +@attr.s(slots=True) +class FuncFixtureInfo: + # Original function argument names. + argnames = attr.ib(type=Tuple[str, ...]) + # Argnames that function immediately requires. These include argnames + + # fixture names specified via usefixtures and via autouse=True in fixture + # definitions. + initialnames = attr.ib(type=Tuple[str, ...]) + names_closure = attr.ib(type=List[str]) + name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef[Any]"]]) + + def prune_dependency_tree(self) -> None: + """Recompute names_closure from initialnames and name2fixturedefs. + + Can only reduce names_closure, which means that the new closure will + always be a subset of the old one. The order is preserved. + + This method is needed because direct parametrization may shadow some + of the fixtures that were included in the originally built dependency + tree. In this way the dependency tree can get pruned, and the closure + of argnames may get reduced. + """ + closure: Set[str] = set() + working_set = set(self.initialnames) + while working_set: + argname = working_set.pop() + # Argname may be smth not included in the original names_closure, + # in which case we ignore it. This currently happens with pseudo + # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. + # So they introduce the new dependency 'request' which might have + # been missing in the original tree (closure). + if argname not in closure and argname in self.names_closure: + closure.add(argname) + if argname in self.name2fixturedefs: + working_set.update(self.name2fixturedefs[argname][-1].argnames) + + self.names_closure[:] = sorted(closure, key=self.names_closure.index) + + +class FixtureRequest: + """A request for a fixture from a test or fixture function. + + A request object gives access to the requesting test context and has + an optional ``param`` attribute in case the fixture is parametrized + indirectly. + """ + + def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pyfuncitem = pyfuncitem + #: Fixture for which this request is being performed. + self.fixturename: Optional[str] = None + #: Scope string, one of "function", "class", "module", "session". + self.scope: _Scope = "function" + self._fixture_defs: Dict[str, FixtureDef[Any]] = {} + fixtureinfo: FuncFixtureInfo = pyfuncitem._fixtureinfo + self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() + self._arg2index: Dict[str, int] = {} + self._fixturemanager: FixtureManager = (pyfuncitem.session._fixturemanager) + + @property + def fixturenames(self) -> List[str]: + """Names of all active fixtures in this request.""" + result = list(self._pyfuncitem._fixtureinfo.names_closure) + result.extend(set(self._fixture_defs).difference(result)) + return result + + @property + def node(self): + """Underlying collection node (depends on current request scope).""" + return self._getscopeitem(self.scope) + + def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]": + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # We arrive here because of a dynamic call to + # getfixturevalue(argname) usage which was naturally + # not known at parsing/collection time. + assert self._pyfuncitem.parent is not None + parentid = self._pyfuncitem.parent.nodeid + fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid) + # TODO: Fix this type ignore. Either add assert or adjust types. + # Can this be None here? + self._arg2fixturedefs[argname] = fixturedefs # type: ignore[assignment] + # fixturedefs list is immutable so we maintain a decreasing index. + index = self._arg2index.get(argname, 0) - 1 + if fixturedefs is None or (-index > len(fixturedefs)): + raise FixtureLookupError(argname, self) + self._arg2index[argname] = index + return fixturedefs[index] + + @property + def config(self) -> Config: + """The pytest config object associated with this request.""" + return self._pyfuncitem.config # type: ignore[no-any-return] + + @property + def function(self): + """Test function object if the request has a per-function scope.""" + if self.scope != "function": + raise AttributeError( + f"function not available in {self.scope}-scoped context" + ) + return self._pyfuncitem.obj + + @property + def cls(self): + """Class (can be None) where the test function was collected.""" + if self.scope not in ("class", "function"): + raise AttributeError(f"cls not available in {self.scope}-scoped context") + clscol = self._pyfuncitem.getparent(_pytest.python.Class) + if clscol: + return clscol.obj + + @property + def instance(self): + """Instance (can be None) on which test function was collected.""" + # unittest support hack, see _pytest.unittest.TestCaseFunction. + try: + return self._pyfuncitem._testcase + except AttributeError: + function = getattr(self, "function", None) + return getattr(function, "__self__", None) + + @property + def module(self): + """Python module object where the test function was collected.""" + if self.scope not in ("function", "class", "module"): + raise AttributeError(f"module not available in {self.scope}-scoped context") + return self._pyfuncitem.getparent(_pytest.python.Module).obj + + @property + def fspath(self) -> py.path.local: + """The file system path of the test module which collected this test.""" + if self.scope not in ("function", "class", "module", "package"): + raise AttributeError(f"module not available in {self.scope}-scoped context") + # TODO: Remove ignore once _pyfuncitem is properly typed. + return self._pyfuncitem.fspath # type: ignore + + @property + def keywords(self): + """Keywords/markers dictionary for the underlying node.""" + return self.node.keywords + + @property + def session(self) -> "Session": + """Pytest session object.""" + return self._pyfuncitem.session # type: ignore[no-any-return] + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called after the last test + within the requesting test context finished execution.""" + # XXX usually this method is shadowed by fixturedef specific ones. + self._addfinalizer(finalizer, scope=self.scope) + + def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None: + colitem = self._getscopeitem(scope) + self._pyfuncitem.session._setupstate.addfinalizer( + finalizer=finalizer, colitem=colitem + ) + + def applymarker(self, marker: Union[str, MarkDecorator]) -> None: + """Apply a marker to a single test function invocation. + + This method is useful if you don't want to have a keyword/marker + on all function invocations. + + :param marker: + A :py:class:`_pytest.mark.MarkDecorator` object created by a call + to ``pytest.mark.NAME(...)``. + """ + self.node.add_marker(marker) + + def raiseerror(self, msg: Optional[str]) -> "NoReturn": + """Raise a FixtureLookupError with the given message.""" + raise self._fixturemanager.FixtureLookupError(None, self, msg) + + def _fillfixtures(self) -> None: + item = self._pyfuncitem + fixturenames = getattr(item, "fixturenames", self.fixturenames) + for argname in fixturenames: + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) + + def getfixturevalue(self, argname: str) -> Any: + """Dynamically run a named fixture function. + + Declaring fixtures via function argument is recommended where possible. + But if you can only decide whether to use another fixture at test + setup time, you may use this function to retrieve it inside a fixture + or test function body. + + :raises pytest.FixtureLookupError: + If the given fixture could not be found. + """ + fixturedef = self._get_active_fixturedef(argname) + assert fixturedef.cached_result is not None + return fixturedef.cached_result[0] + + def _get_active_fixturedef( + self, argname: str + ) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]: + try: + return self._fixture_defs[argname] + except KeyError: + try: + fixturedef = self._getnextfixturedef(argname) + except FixtureLookupError: + if argname == "request": + cached_result = (self, [0], None) + scope: _Scope = "function" + return PseudoFixtureDef(cached_result, scope) + raise + # Remove indent to prevent the python3 exception + # from leaking into the call. + self._compute_fixture_value(fixturedef) + self._fixture_defs[argname] = fixturedef + return fixturedef + + def _get_fixturestack(self) -> List["FixtureDef[Any]"]: + current = self + values: List[FixtureDef[Any]] = [] + while 1: + fixturedef = getattr(current, "_fixturedef", None) + if fixturedef is None: + values.reverse() + return values + values.append(fixturedef) + assert isinstance(current, SubRequest) + current = current._parent_request + + def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: + """Create a SubRequest based on "self" and call the execute method + of the given FixtureDef object. + + This will force the FixtureDef object to throw away any previous + results and compute a new fixture value, which will be stored into + the FixtureDef object itself. + """ + # prepare a subrequest object before calling fixture function + # (latter managed by fixturedef) + argname = fixturedef.argname + funcitem = self._pyfuncitem + scope = fixturedef.scope + try: + param = funcitem.callspec.getparam(argname) + except (AttributeError, ValueError): + param = NOTSET + param_index = 0 + has_params = fixturedef.params is not None + fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + if has_params and fixtures_not_supported: + msg = ( + "{name} does not support fixtures, maybe unittest.TestCase subclass?\n" + "Node id: {nodeid}\n" + "Function type: {typename}" + ).format( + name=funcitem.name, + nodeid=funcitem.nodeid, + typename=type(funcitem).__name__, + ) + fail(msg, pytrace=False) + if has_params: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = py.path.local(frameinfo.filename) + source_lineno = frameinfo.lineno + rel_source_path = source_path.relto(funcitem.config.rootdir) + if rel_source_path: + source_path_str = rel_source_path + else: + source_path_str = str(source_path) + msg = ( + "The requested fixture has no parameter defined for test:\n" + " {}\n\n" + "Requested fixture '{}' defined in:\n{}" + "\n\nRequested here:\n{}:{}".format( + funcitem.nodeid, + fixturedef.argname, + getlocation(fixturedef.func, funcitem.config.rootdir), + source_path_str, + source_lineno, + ) + ) + fail(msg, pytrace=False) + else: + param_index = funcitem.callspec.indices[argname] + # If a parametrize invocation set a scope it will override + # the static scope defined with the fixture function. + paramscopenum = funcitem.callspec._arg2scopenum.get(argname) + if paramscopenum is not None: + scope = scopes[paramscopenum] + + subrequest = SubRequest( + self, scope, param, param_index, fixturedef, _ispytest=True + ) + + # Check if a higher-level scoped fixture accesses a lower level one. + subrequest._check_scope(argname, self.scope, scope) + try: + # Call the fixture function. + fixturedef.execute(request=subrequest) + finally: + self._schedule_finalizers(fixturedef, subrequest) + + def _schedule_finalizers( + self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + ) -> None: + # If fixture function failed it might have registered finalizers. + self.session._setupstate.addfinalizer( + functools.partial(fixturedef.finish, request=subrequest), subrequest.node + ) + + def _check_scope( + self, argname: str, invoking_scope: "_Scope", requested_scope: "_Scope", + ) -> None: + if argname == "request": + return + if scopemismatch(invoking_scope, requested_scope): + # Try to report something helpful. + lines = self._factorytraceback() + fail( + "ScopeMismatch: You tried to access the %r scoped " + "fixture %r with a %r scoped request object, " + "involved factories\n%s" + % ((requested_scope, argname, invoking_scope, "\n".join(lines))), + pytrace=False, + ) + + def _factorytraceback(self) -> List[str]: + lines = [] + for fixturedef in self._get_fixturestack(): + factory = fixturedef.func + fs, lineno = getfslineno(factory) + p = self._pyfuncitem.session.fspath.bestrelpath(fs) + args = _format_args(factory) + lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) + return lines + + def _getscopeitem(self, scope: "_Scope") -> Union[nodes.Item, nodes.Collector]: + if scope == "function": + # This might also be a non-function Item despite its attribute name. + node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem + elif scope == "package": + # FIXME: _fixturedef is not defined on FixtureRequest (this class), + # but on FixtureRequest (a subclass). + node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined] + else: + node = get_scope_node(self._pyfuncitem, scope) + if node is None and scope == "class": + # Fallback to function item itself. + node = self._pyfuncitem + assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format( + scope, self._pyfuncitem + ) + return node + + def __repr__(self) -> str: + return "" % (self.node) + + +@final +class SubRequest(FixtureRequest): + """A sub request for handling getting a fixture from a test function/fixture.""" + + def __init__( + self, + request: "FixtureRequest", + scope: "_Scope", + param, + param_index: int, + fixturedef: "FixtureDef[object]", + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._parent_request = request + self.fixturename = fixturedef.argname + if param is not NOTSET: + self.param = param + self.param_index = param_index + self.scope = scope + self._fixturedef = fixturedef + self._pyfuncitem = request._pyfuncitem + self._fixture_defs = request._fixture_defs + self._arg2fixturedefs = request._arg2fixturedefs + self._arg2index = request._arg2index + self._fixturemanager = request._fixturemanager + + def __repr__(self) -> str: + return f"" + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called after the last test + within the requesting test context finished execution.""" + self._fixturedef.addfinalizer(finalizer) + + def _schedule_finalizers( + self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + ) -> None: + # If the executing fixturedef was not explicitly requested in the argument list (via + # getfixturevalue inside the fixture call) then ensure this fixture def will be finished + # first. + if fixturedef.argname not in self.fixturenames: + fixturedef.addfinalizer( + functools.partial(self._fixturedef.finish, request=self) + ) + super()._schedule_finalizers(fixturedef, subrequest) + + +scopes: List["_Scope"] = ["session", "package", "module", "class", "function"] +scopenum_function = scopes.index("function") + + +def scopemismatch(currentscope: "_Scope", newscope: "_Scope") -> bool: + return scopes.index(newscope) > scopes.index(currentscope) + + +def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int: + """Look up the index of ``scope`` and raise a descriptive value error + if not defined.""" + strscopes: Sequence[str] = scopes + try: + return strscopes.index(scope) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope + ), + pytrace=False, + ) + + +@final +class FixtureLookupError(LookupError): + """Could not return a requested fixture (missing or invalid).""" + + def __init__( + self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = None + ) -> None: + self.argname = argname + self.request = request + self.fixturestack = request._get_fixturestack() + self.msg = msg + + def formatrepr(self) -> "FixtureLookupErrorRepr": + tblines: List[str] = [] + addline = tblines.append + stack = [self.request._pyfuncitem.obj] + stack.extend(map(lambda x: x.func, self.fixturestack)) + msg = self.msg + if msg is not None: + # The last fixture raise an error, let's present + # it at the requesting side. + stack = stack[:-1] + for function in stack: + fspath, lineno = getfslineno(function) + try: + lines, _ = inspect.getsourcelines(get_real_func(function)) + except (OSError, IndexError, TypeError): + error_msg = "file %s, line %s: source code not available" + addline(error_msg % (fspath, lineno + 1)) + else: + addline("file {}, line {}".format(fspath, lineno + 1)) + for i, line in enumerate(lines): + line = line.rstrip() + addline(" " + line) + if line.lstrip().startswith("def"): + break + + if msg is None: + fm = self.request._fixturemanager + available = set() + parentid = self.request._pyfuncitem.parent.nodeid + for name, fixturedefs in fm._arg2fixturedefs.items(): + faclist = list(fm._matchfactories(fixturedefs, parentid)) + if faclist: + available.add(name) + if self.argname in available: + msg = " recursive dependency involving fixture '{}' detected".format( + self.argname + ) + else: + msg = f"fixture '{self.argname}' not found" + msg += "\n available fixtures: {}".format(", ".join(sorted(available))) + msg += "\n use 'pytest --fixtures [testpath]' for help on them." + + return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) + + +class FixtureLookupErrorRepr(TerminalRepr): + def __init__( + self, + filename: Union[str, py.path.local], + firstlineno: int, + tblines: Sequence[str], + errorstring: str, + argname: Optional[str], + ) -> None: + self.tblines = tblines + self.errorstring = errorstring + self.filename = filename + self.firstlineno = firstlineno + self.argname = argname + + def toterminal(self, tw: TerminalWriter) -> None: + # tw.line("FixtureLookupError: %s" %(self.argname), red=True) + for tbline in self.tblines: + tw.line(tbline.rstrip()) + lines = self.errorstring.split("\n") + if lines: + tw.line( + "{} {}".format(FormattedExcinfo.fail_marker, lines[0].strip()), + red=True, + ) + for line in lines[1:]: + tw.line( + f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True, + ) + tw.line() + tw.line("%s:%d" % (self.filename, self.firstlineno + 1)) + + +def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": + fs, lineno = getfslineno(fixturefunc) + location = "{}:{}".format(fs, lineno + 1) + source = _pytest._code.Source(fixturefunc) + fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) + + +def call_fixture_func( + fixturefunc: "_FixtureFunc[_FixtureValue]", request: FixtureRequest, kwargs +) -> _FixtureValue: + if is_generator(fixturefunc): + fixturefunc = cast( + Callable[..., Generator[_FixtureValue, None, None]], fixturefunc + ) + generator = fixturefunc(**kwargs) + try: + fixture_result = next(generator) + except StopIteration: + raise ValueError(f"{request.fixturename} did not yield a value") from None + finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) + request.addfinalizer(finalizer) + else: + fixturefunc = cast(Callable[..., _FixtureValue], fixturefunc) + fixture_result = fixturefunc(**kwargs) + return fixture_result + + +def _teardown_yield_fixture(fixturefunc, it) -> None: + """Execute the teardown of a fixture function by advancing the iterator + after the yield and ensure the iteration ends (if not it means there is + more than one yield in the function).""" + try: + next(it) + except StopIteration: + pass + else: + fail_fixturefunc(fixturefunc, "fixture function has more than one 'yield'") + + +def _eval_scope_callable( + scope_callable: "Callable[[str, Config], _Scope]", + fixture_name: str, + config: Config, +) -> "_Scope": + try: + # Type ignored because there is no typing mechanism to specify + # keyword arguments, currently. + result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] + except Exception as e: + raise TypeError( + "Error evaluating {} while defining fixture '{}'.\n" + "Expected a function with the signature (*, fixture_name, config)".format( + scope_callable, fixture_name + ) + ) from e + if not isinstance(result, str): + fail( + "Expected {} to return a 'str' while defining fixture '{}', but it returned:\n" + "{!r}".format(scope_callable, fixture_name, result), + pytrace=False, + ) + return result + + +@final +class FixtureDef(Generic[_FixtureValue]): + """A container for a factory definition.""" + + def __init__( + self, + fixturemanager: "FixtureManager", + baseid: Optional[str], + argname: str, + func: "_FixtureFunc[_FixtureValue]", + scope: "Union[_Scope, Callable[[str, Config], _Scope]]", + params: Optional[Sequence[object]], + unittest: bool = False, + ids: Optional[ + Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ] + ] = None, + ) -> None: + self._fixturemanager = fixturemanager + self.baseid = baseid or "" + self.has_location = baseid is not None + self.func = func + self.argname = argname + if callable(scope): + scope_ = _eval_scope_callable(scope, argname, fixturemanager.config) + else: + scope_ = scope + self.scopenum = scope2index( + # TODO: Check if the `or` here is really necessary. + scope_ or "function", # type: ignore[unreachable] + descr=f"Fixture '{func.__name__}'", + where=baseid, + ) + self.scope = scope_ + self.params: Optional[Sequence[object]] = params + self.argnames: Tuple[str, ...] = getfuncargnames( + func, name=argname, is_method=unittest + ) + self.unittest = unittest + self.ids = ids + self.cached_result: Optional[_FixtureCachedResult[_FixtureValue]] = None + self._finalizers: List[Callable[[], object]] = [] + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._finalizers.append(finalizer) + + def finish(self, request: SubRequest) -> None: + exc = None + try: + while self._finalizers: + try: + func = self._finalizers.pop() + func() + except BaseException as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + finally: + hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + hook.pytest_fixture_post_finalizer(fixturedef=self, request=request) + # Even if finalization fails, we invalidate the cached fixture + # value and remove all finalizers because they may be bound methods + # which will keep instances alive. + self.cached_result = None + self._finalizers = [] + + def execute(self, request: SubRequest) -> _FixtureValue: + # Get required arguments and register our own finish() + # with their finalization. + for argname in self.argnames: + fixturedef = request._get_active_fixturedef(argname) + if argname != "request": + # PseudoFixtureDef is only for "request". + assert isinstance(fixturedef, FixtureDef) + fixturedef.addfinalizer(functools.partial(self.finish, request=request)) + + my_cache_key = self.cache_key(request) + if self.cached_result is not None: + # note: comparison with `==` can fail (or be expensive) for e.g. + # numpy arrays (#6497). + cache_key = self.cached_result[1] + if my_cache_key is cache_key: + if self.cached_result[2] is not None: + _, val, tb = self.cached_result[2] + raise val.with_traceback(tb) + else: + result = self.cached_result[0] + return result + # We have a previous but differently parametrized fixture instance + # so we need to tear it down before creating a new one. + self.finish(request) + assert self.cached_result is None + + hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + result = hook.pytest_fixture_setup(fixturedef=self, request=request) + return result + + def cache_key(self, request: SubRequest) -> object: + return request.param_index if not hasattr(request, "param") else request.param + + def __repr__(self) -> str: + return "".format( + self.argname, self.scope, self.baseid + ) + + +def resolve_fixture_function( + fixturedef: FixtureDef[_FixtureValue], request: FixtureRequest +) -> "_FixtureFunc[_FixtureValue]": + """Get the actual callable that can be called to obtain the fixture + value, dealing with unittest-specific instances and bound methods.""" + fixturefunc = fixturedef.func + if fixturedef.unittest: + if request.instance is not None: + # Bind the unbound method to the TestCase instance. + fixturefunc = fixturedef.func.__get__(request.instance) # type: ignore[union-attr] + else: + # The fixture function needs to be bound to the actual + # request.instance so that code working with "fixturedef" behaves + # as expected. + if request.instance is not None: + # Handle the case where fixture is defined not in a test class, but some other class + # (for example a plugin class with a fixture), see #2270. + if hasattr(fixturefunc, "__self__") and not isinstance( + request.instance, fixturefunc.__self__.__class__ # type: ignore[union-attr] + ): + return fixturefunc + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(request.instance) # type: ignore[union-attr] + return fixturefunc + + +def pytest_fixture_setup( + fixturedef: FixtureDef[_FixtureValue], request: SubRequest +) -> _FixtureValue: + """Execution of fixture setup.""" + kwargs = {} + for argname in fixturedef.argnames: + fixdef = request._get_active_fixturedef(argname) + assert fixdef.cached_result is not None + result, arg_cache_key, exc = fixdef.cached_result + request._check_scope(argname, request.scope, fixdef.scope) + kwargs[argname] = result + + fixturefunc = resolve_fixture_function(fixturedef, request) + my_cache_key = fixturedef.cache_key(request) + try: + result = call_fixture_func(fixturefunc, request, kwargs) + except TEST_OUTCOME: + exc_info = sys.exc_info() + assert exc_info[0] is not None + fixturedef.cached_result = (None, my_cache_key, exc_info) + raise + fixturedef.cached_result = (result, my_cache_key, None) + return result + + +def _ensure_immutable_ids( + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ], +) -> Optional[ + Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ] +]: + if ids is None: + return None + if callable(ids): + return ids + return tuple(ids) + + +def _params_converter( + params: Optional[Iterable[object]], +) -> Optional[Tuple[object, ...]]: + return tuple(params) if params is not None else None + + +def wrap_function_to_error_out_if_called_directly( + function: _FixtureFunction, fixture_marker: "FixtureFunctionMarker", +) -> _FixtureFunction: + """Wrap the given fixture function so we can raise an error about it being called directly, + instead of used as an argument in a test function.""" + message = ( + 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' + "but are created automatically when test functions request them as parameters.\n" + "See https://docs.pytest.org/en/stable/fixture.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code." + ).format(name=fixture_marker.name or function.__name__) + + @functools.wraps(function) + def result(*args, **kwargs): + fail(message, pytrace=False) + + # Keep reference to the original function in our own custom attribute so we don't unwrap + # further than this point and lose useful wrappings like @mock.patch (#3774). + result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] + + return cast(_FixtureFunction, result) + + +@final +@attr.s(frozen=True) +class FixtureFunctionMarker: + scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]") + params = attr.ib(type=Optional[Tuple[object, ...]], converter=_params_converter) + autouse = attr.ib(type=bool, default=False) + ids = attr.ib( + type=Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ], + default=None, + converter=_ensure_immutable_ids, + ) + name = attr.ib(type=Optional[str], default=None) + + def __call__(self, function: _FixtureFunction) -> _FixtureFunction: + if inspect.isclass(function): + raise ValueError("class fixtures not supported (maybe in the future)") + + if getattr(function, "_pytestfixturefunction", False): + raise ValueError( + "fixture is being applied more than once to the same function" + ) + + function = wrap_function_to_error_out_if_called_directly(function, self) + + name = self.name or function.__name__ + if name == "request": + location = getlocation(function) + fail( + "'request' is a reserved word for fixtures, use another name:\n {}".format( + location + ), + pytrace=False, + ) + + # Type ignored because https://github.com/python/mypy/issues/2087. + function._pytestfixturefunction = self # type: ignore[attr-defined] + return function + + +@overload +def fixture( + fixture_function: _FixtureFunction, + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = ..., +) -> _FixtureFunction: + ... + + +@overload +def fixture( + fixture_function: None = ..., + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = None, +) -> FixtureFunctionMarker: + ... + + +def fixture( + fixture_function: Optional[_FixtureFunction] = None, + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", + params: Optional[Iterable[object]] = None, + autouse: bool = False, + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = None, + name: Optional[str] = None, +) -> Union[FixtureFunctionMarker, _FixtureFunction]: + """Decorator to mark a fixture factory function. + + This decorator can be used, with or without parameters, to define a + fixture function. + + The name of the fixture function can later be referenced to cause its + invocation ahead of running tests: test modules or classes can use the + ``pytest.mark.usefixtures(fixturename)`` marker. + + Test functions can directly use fixture names as input arguments in which + case the fixture instance returned from the fixture function will be + injected. + + Fixtures can provide their values to test functions using ``return`` or + ``yield`` statements. When using ``yield`` the code block after the + ``yield`` statement is executed as teardown code regardless of the test + outcome, and must yield exactly once. + + :param scope: + The scope for which this fixture is shared; one of ``"function"`` + (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. + + This parameter may also be a callable which receives ``(fixture_name, config)`` + as parameters, and must return a ``str`` with one of the values mentioned above. + + See :ref:`dynamic scope` in the docs for more information. + + :param params: + An optional list of parameters which will cause multiple invocations + of the fixture function and all of the tests using it. The current + parameter is available in ``request.param``. + + :param autouse: + If True, the fixture func is activated for all tests that can see it. + If False (the default), an explicit reference is needed to activate + the fixture. + + :param ids: + List of string ids each corresponding to the params so that they are + part of the test id. If no ids are provided they will be generated + automatically from the params. + + :param name: + The name of the fixture. This defaults to the name of the decorated + function. If a fixture is used in the same module in which it is + defined, the function name of the fixture will be shadowed by the + function arg that requests the fixture; one way to resolve this is to + name the decorated function ``fixture_`` and then use + ``@pytest.fixture(name='')``. + """ + fixture_marker = FixtureFunctionMarker( + scope=scope, params=params, autouse=autouse, ids=ids, name=name, + ) + + # Direct decoration. + if fixture_function: + return fixture_marker(fixture_function) + + return fixture_marker + + +def yield_fixture( + fixture_function=None, + *args, + scope="function", + params=None, + autouse=False, + ids=None, + name=None, +): + """(Return a) decorator to mark a yield-fixture factory function. + + .. deprecated:: 3.0 + Use :py:func:`pytest.fixture` directly instead. + """ + warnings.warn(YIELD_FIXTURE, stacklevel=2) + return fixture( + fixture_function, + *args, + scope=scope, + params=params, + autouse=autouse, + ids=ids, + name=name, + ) + + +@fixture(scope="session") +def pytestconfig(request: FixtureRequest) -> Config: + """Session-scoped fixture that returns the :class:`_pytest.config.Config` object. + + Example:: + + def test_foo(pytestconfig): + if pytestconfig.getoption("verbose") > 0: + ... + + """ + return request.config + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "usefixtures", + type="args", + default=[], + help="list of default fixtures to be used with this project", + ) + + +class FixtureManager: + """pytest fixture definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i.e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + + FixtureLookupError = FixtureLookupError + FixtureLookupErrorRepr = FixtureLookupErrorRepr + + def __init__(self, session: "Session") -> None: + self.session = session + self.config: Config = session.config + self._arg2fixturedefs: Dict[str, List[FixtureDef[Any]]] = {} + self._holderobjseen: Set[object] = set() + # A mapping from a nodeid to a list of autouse fixtures it defines. + self._nodeid_autousenames: Dict[str, List[str]] = { + "": self.config.getini("usefixtures"), + } + session.config.pluginmanager.register(self, "funcmanage") + + def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]: + """Return all direct parametrization arguments of a node, so we don't + mistake them for fixtures. + + Check https://github.com/pytest-dev/pytest/issues/5036. + + These things are done later as well when dealing with parametrization + so this could be improved. + """ + parametrize_argnames: List[str] = [] + for marker in node.iter_markers(name="parametrize"): + if not marker.kwargs.get("indirect", False): + p_argnames, _ = ParameterSet._parse_parametrize_args( + *marker.args, **marker.kwargs + ) + parametrize_argnames.extend(p_argnames) + + return parametrize_argnames + + def getfixtureinfo( + self, node: nodes.Node, func, cls, funcargs: bool = True + ) -> FuncFixtureInfo: + if funcargs and not getattr(node, "nofuncargs", False): + argnames = getfuncargnames(func, name=node.name, cls=cls) + else: + argnames = () + + usefixtures = tuple( + arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args + ) + initialnames = usefixtures + argnames + fm = node.session._fixturemanager + initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure( + initialnames, node, ignore_args=self._get_direct_parametrize_args(node) + ) + return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: + nodeid = None + try: + p = absolutepath(plugin.__file__) # type: ignore[attr-defined] + except AttributeError: + pass + else: + # Construct the base nodeid which is later used to check + # what fixtures are visible for particular tests (as denoted + # by their test id). + if p.name.startswith("conftest.py"): + try: + nodeid = str(p.parent.relative_to(self.config.rootpath)) + except ValueError: + nodeid = "" + if nodeid == ".": + nodeid = "" + if os.sep != nodes.SEP: + nodeid = nodeid.replace(os.sep, nodes.SEP) + + self.parsefactories(plugin, nodeid) + + def _getautousenames(self, nodeid: str) -> Iterator[str]: + """Return the names of autouse fixtures applicable to nodeid.""" + for parentnodeid in nodes.iterparentnodeids(nodeid): + basenames = self._nodeid_autousenames.get(parentnodeid) + if basenames: + yield from basenames + + def getfixtureclosure( + self, + fixturenames: Tuple[str, ...], + parentnode: nodes.Node, + ignore_args: Sequence[str] = (), + ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef[Any]]]]: + # Collect the closure of all fixtures, starting with the given + # fixturenames as the initial set. As we have to visit all + # factory definitions anyway, we also return an arg2fixturedefs + # mapping so that the caller can reuse it and does not have + # to re-discover fixturedefs again for each fixturename + # (discovering matching fixtures for a given name/node is expensive). + + parentid = parentnode.nodeid + fixturenames_closure = list(self._getautousenames(parentid)) + + def merge(otherlist: Iterable[str]) -> None: + for arg in otherlist: + if arg not in fixturenames_closure: + fixturenames_closure.append(arg) + + merge(fixturenames) + + # At this point, fixturenames_closure contains what we call "initialnames", + # which is a set of fixturenames the function immediately requests. We + # need to return it as well, so save this. + initialnames = tuple(fixturenames_closure) + + arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} + lastlen = -1 + while lastlen != len(fixturenames_closure): + lastlen = len(fixturenames_closure) + for argname in fixturenames_closure: + if argname in ignore_args: + continue + if argname in arg2fixturedefs: + continue + fixturedefs = self.getfixturedefs(argname, parentid) + if fixturedefs: + arg2fixturedefs[argname] = fixturedefs + merge(fixturedefs[-1].argnames) + + def sort_by_scope(arg_name: str) -> int: + try: + fixturedefs = arg2fixturedefs[arg_name] + except KeyError: + return scopes.index("function") + else: + return fixturedefs[-1].scopenum + + fixturenames_closure.sort(key=sort_by_scope) + return initialnames, fixturenames_closure, arg2fixturedefs + + def pytest_generate_tests(self, metafunc: "Metafunc") -> None: + """Generate new tests based on parametrized fixtures used by the given metafunc""" + + def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: + args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs) + return args + + for argname in metafunc.fixturenames: + # Get the FixtureDefs for the argname. + fixture_defs = metafunc._arg2fixturedefs.get(argname) + if not fixture_defs: + # Will raise FixtureLookupError at setup time if not parametrized somewhere + # else (e.g @pytest.mark.parametrize) + continue + + # If the test itself parametrizes using this argname, give it + # precedence. + if any( + argname in get_parametrize_mark_argnames(mark) + for mark in metafunc.definition.iter_markers("parametrize") + ): + continue + + # In the common case we only look at the fixture def with the + # closest scope (last in the list). But if the fixture overrides + # another fixture, while requesting the super fixture, keep going + # in case the super fixture is parametrized (#1953). + for fixturedef in reversed(fixture_defs): + # Fixture is parametrized, apply it and stop. + if fixturedef.params is not None: + metafunc.parametrize( + argname, + fixturedef.params, + indirect=True, + scope=fixturedef.scope, + ids=fixturedef.ids, + ) + break + + # Not requesting the overridden super fixture, stop. + if argname not in fixturedef.argnames: + break + + # Try next super fixture, if any. + + def pytest_collection_modifyitems(self, items: List[nodes.Item]) -> None: + # Separate parametrized setups. + items[:] = reorder_items(items) + + def parsefactories( + self, node_or_obj, nodeid=NOTSET, unittest: bool = False + ) -> None: + if nodeid is not NOTSET: + holderobj = node_or_obj + else: + holderobj = node_or_obj.obj + nodeid = node_or_obj.nodeid + if holderobj in self._holderobjseen: + return + + self._holderobjseen.add(holderobj) + autousenames = [] + for name in dir(holderobj): + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getatt() ignores such exceptions. + obj = safe_getattr(holderobj, name, None) + marker = getfixturemarker(obj) + if not isinstance(marker, FixtureFunctionMarker): + # Magic globals with __getattr__ might have got us a wrong + # fixture attribute. + continue + + if marker.name: + name = marker.name + + # During fixture definition we wrap the original fixture function + # to issue a warning if called directly, so here we unwrap it in + # order to not emit the warning when pytest itself calls the + # fixture function. + obj = get_real_method(obj, holderobj) + + fixture_def = FixtureDef( + fixturemanager=self, + baseid=nodeid, + argname=name, + func=obj, + scope=marker.scope, + params=marker.params, + unittest=unittest, + ids=marker.ids, + ) + + faclist = self._arg2fixturedefs.setdefault(name, []) + if fixture_def.has_location: + faclist.append(fixture_def) + else: + # fixturedefs with no location are at the front + # so this inserts the current fixturedef after the + # existing fixturedefs from external plugins but + # before the fixturedefs provided in conftests. + i = len([f for f in faclist if not f.has_location]) + faclist.insert(i, fixture_def) + if marker.autouse: + autousenames.append(name) + + if autousenames: + self._nodeid_autousenames.setdefault(nodeid or "", []).extend(autousenames) + + def getfixturedefs( + self, argname: str, nodeid: str + ) -> Optional[Sequence[FixtureDef[Any]]]: + """Get a list of fixtures which are applicable to the given node id. + + :param str argname: Name of the fixture to search for. + :param str nodeid: Full node id of the requesting test. + :rtype: Sequence[FixtureDef] + """ + try: + fixturedefs = self._arg2fixturedefs[argname] + except KeyError: + return None + return tuple(self._matchfactories(fixturedefs, nodeid)) + + def _matchfactories( + self, fixturedefs: Iterable[FixtureDef[Any]], nodeid: str + ) -> Iterator[FixtureDef[Any]]: + parentnodeids = set(nodes.iterparentnodeids(nodeid)) + for fixturedef in fixturedefs: + if fixturedef.baseid in parentnodeids: + yield fixturedef diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/freeze_support.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/freeze_support.py new file mode 100644 index 0000000..8b93ed5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/freeze_support.py @@ -0,0 +1,45 @@ +"""Provides a function to report all internal modules for using freezing +tools.""" +import types +from typing import Iterator +from typing import List +from typing import Union + + +def freeze_includes() -> List[str]: + """Return a list of module names used by pytest that should be + included by cx_freeze.""" + import py + import _pytest + + result = list(_iter_all_modules(py)) + result += list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules( + package: Union[str, types.ModuleType], prefix: str = "", +) -> Iterator[str]: + """Iterate over the names of all modules that can be found in the given + package, recursively. + + >>> import _pytest + >>> list(_iter_all_modules(_pytest)) + ['_pytest._argcomplete', '_pytest._code.code', ...] + """ + import os + import pkgutil + + if isinstance(package, str): + path = package + else: + # Type ignored because typeshed doesn't define ModuleType.__path__ + # (only defined on packages). + package_path = package.__path__ # type: ignore[attr-defined] + path, prefix = package_path[0], package.__name__ + "." + for _, name, is_package in pkgutil.iter_modules([path]): + if is_package: + for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): + yield prefix + m + else: + yield prefix + name diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/helpconfig.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/helpconfig.py new file mode 100644 index 0000000..4384d07 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/helpconfig.py @@ -0,0 +1,261 @@ +"""Version info, help messages, tracing configuration.""" +import os +import sys +from argparse import Action +from typing import List +from typing import Optional +from typing import Union + +import py + +import pytest +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import PrintHelp +from _pytest.config.argparsing import Parser + + +class HelpAction(Action): + """An argparse Action that will raise an exception in order to skip the + rest of the argument parsing when --help is passed. + + This prevents argparse from quitting due to missing required arguments + when any are defined, for example by ``pytest_addoption``. + This is similar to the way that the builtin argparse --help option is + implemented by raising SystemExit. + """ + + def __init__(self, option_strings, dest=None, default=False, help=None): + super().__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + nargs=0, + help=help, + ) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + # We should only skip the rest of the parsing after preparse is done. + if getattr(parser._parser, "after_preparse", False): + raise PrintHelp + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--version", + "-V", + action="count", + default=0, + dest="version", + help="display pytest version and information about plugins." + "When given twice, also display information about plugins.", + ) + group._addoption( + "-h", + "--help", + action=HelpAction, + dest="help", + help="show help message and configuration info", + ) + group._addoption( + "-p", + action="append", + dest="plugins", + default=[], + metavar="name", + help="early-load given plugin module name or entry point (multi-allowed).\n" + "To avoid loading of plugins, use the `no:` prefix, e.g. " + "`no:doctest`.", + ) + group.addoption( + "--traceconfig", + "--trace-config", + action="store_true", + default=False, + help="trace considerations of conftest.py files.", + ) + group.addoption( + "--debug", + action="store_true", + dest="debug", + default=False, + help="store internal tracing debug information in 'pytestdebug.log'.", + ) + group._addoption( + "-o", + "--override-ini", + dest="override_ini", + action="append", + help='override ini option with "option=value" style, e.g. `-o xfail_strict=True -o cache_dir=cache`.', + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_cmdline_parse(): + outcome = yield + config: Config = outcome.get_result() + if config.option.debug: + path = os.path.abspath("pytestdebug.log") + debugfile = open(path, "w") + debugfile.write( + "versions pytest-%s, py-%s, " + "python-%s\ncwd=%s\nargs=%s\n\n" + % ( + pytest.__version__, + py.__version__, + ".".join(map(str, sys.version_info)), + os.getcwd(), + config.invocation_params.args, + ) + ) + config.trace.root.setwriter(debugfile.write) + undo_tracing = config.pluginmanager.enable_tracing() + sys.stderr.write("writing pytestdebug information to %s\n" % path) + + def unset_tracing() -> None: + debugfile.close() + sys.stderr.write("wrote pytestdebug information to %s\n" % debugfile.name) + config.trace.root.setwriter(None) + undo_tracing() + + config.add_cleanup(unset_tracing) + + +def showversion(config: Config) -> None: + if config.option.version > 1: + sys.stderr.write( + "This is pytest version {}, imported from {}\n".format( + pytest.__version__, pytest.__file__ + ) + ) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stderr.write(line + "\n") + else: + sys.stderr.write(f"pytest {pytest.__version__}\n") + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.version > 0: + showversion(config) + return 0 + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return 0 + return None + + +def showhelp(config: Config) -> None: + import textwrap + + reporter = config.pluginmanager.get_plugin("terminalreporter") + tw = reporter._tw + tw.write(config._parser.optparser.format_help()) + tw.line() + tw.line( + "[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:" + ) + tw.line() + + columns = tw.fullwidth # costly call + indent_len = 24 # based on argparse's max_help_position=24 + indent = " " * indent_len + for name in config._parser._ininames: + help, type, default = config._parser._inidict[name] + if type is None: + type = "string" + if help is None: + raise TypeError(f"help argument cannot be None for {name}") + spec = f"{name} ({type}):" + tw.write(" %s" % spec) + spec_len = len(spec) + if spec_len > (indent_len - 3): + # Display help starting at a new line. + tw.line() + helplines = textwrap.wrap( + help, + columns, + initial_indent=indent, + subsequent_indent=indent, + break_on_hyphens=False, + ) + + for line in helplines: + tw.line(line) + else: + # Display help starting after the spec, following lines indented. + tw.write(" " * (indent_len - spec_len - 2)) + wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) + + if wrapped: + tw.line(wrapped[0]) + for line in wrapped[1:]: + tw.line(indent + line) + + tw.line() + tw.line("environment variables:") + vars = [ + ("PYTEST_ADDOPTS", "extra command line options"), + ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"), + ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "set to disable plugin auto-loading"), + ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), + ] + for name, help in vars: + tw.line(f" {name:<24} {help}") + tw.line() + tw.line() + + tw.line("to see available markers type: pytest --markers") + tw.line("to see available fixtures type: pytest --fixtures") + tw.line( + "(shown according to specified file_or_dir or current dir " + "if not specified; fixtures with leading '_' are only shown " + "with the '-v' option" + ) + + for warningreport in reporter.stats.get("warnings", []): + tw.line("warning : " + warningreport.message, red=True) + return + + +conftest_options = [("pytest_plugins", "list of plugin names to load")] + + +def getpluginversioninfo(config: Config) -> List[str]: + lines = [] + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + lines.append("setuptools registered plugins:") + for plugin, dist in plugininfo: + loc = getattr(plugin, "__file__", repr(plugin)) + content = f"{dist.project_name}-{dist.version} at {loc}" + lines.append(" " + content) + return lines + + +def pytest_report_header(config: Config) -> List[str]: + lines = [] + if config.option.debug or config.option.traceconfig: + lines.append(f"using: pytest-{pytest.__version__} pylib-{py.__version__}") + + verinfo = getpluginversioninfo(config) + if verinfo: + lines.extend(verinfo) + + if config.option.traceconfig: + lines.append("active plugins:") + items = config.pluginmanager.list_name_plugin() + for name, plugin in items: + if hasattr(plugin, "__file__"): + r = plugin.__file__ + else: + r = repr(plugin) + lines.append(f" {name:<20}: {r}") + return lines diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/hookspec.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/hookspec.py new file mode 100644 index 0000000..e499b74 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/hookspec.py @@ -0,0 +1,891 @@ +"""Hook specifications for pytest plugins which are invoked by pytest itself +and by builtin plugins.""" +from typing import Any +from typing import Dict +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import py.path +from pluggy import HookspecMarker + +from _pytest.deprecated import WARNING_CAPTURED_HOOK + +if TYPE_CHECKING: + import pdb + import warnings + from typing_extensions import Literal + + from _pytest._code.code import ExceptionRepr + from _pytest.code import ExceptionInfo + from _pytest.config import Config + from _pytest.config import ExitCode + from _pytest.config import PytestPluginManager + from _pytest.config import _PluggyPlugin + from _pytest.config.argparsing import Parser + from _pytest.fixtures import FixtureDef + from _pytest.fixtures import SubRequest + from _pytest.main import Session + from _pytest.nodes import Collector + from _pytest.nodes import Item + from _pytest.outcomes import Exit + from _pytest.python import Function + from _pytest.python import Metafunc + from _pytest.python import Module + from _pytest.python import PyCollector + from _pytest.reports import CollectReport + from _pytest.reports import TestReport + from _pytest.runner import CallInfo + from _pytest.terminal import TerminalReporter + + +hookspec = HookspecMarker("pytest") + +# ------------------------------------------------------------------------- +# Initialization hooks called for every plugin +# ------------------------------------------------------------------------- + + +@hookspec(historic=True) +def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None: + """Called at plugin registration time to allow adding new hooks via a call to + ``pluginmanager.add_hookspecs(module_or_class, prefix)``. + + :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_plugin_registered( + plugin: "_PluggyPlugin", manager: "PytestPluginManager" +) -> None: + """A new pytest plugin got registered. + + :param plugin: The plugin module or instance. + :param _pytest.config.PytestPluginManager manager: pytest plugin manager. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> None: + """Register argparse-style options and ini-style config values, + called once at the beginning of a test run. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + + :param _pytest.config.argparsing.Parser parser: + To add command line options, call + :py:func:`parser.addoption(...) <_pytest.config.argparsing.Parser.addoption>`. + To add ini-file values call :py:func:`parser.addini(...) + <_pytest.config.argparsing.Parser.addini>`. + + :param _pytest.config.PytestPluginManager pluginmanager: + pytest plugin manager, which can be used to install :py:func:`hookspec`'s + or :py:func:`hookimpl`'s and allow one plugin to call another plugin's hooks + to change how command line options are added. + + Options can later be accessed through the + :py:class:`config <_pytest.config.Config>` object, respectively: + + - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to + retrieve the value of a command line option. + + - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve + a value read from an ini-style file. + + The config object is passed around on many internal objects via the ``.config`` + attribute or can be retrieved as the ``pytestconfig`` fixture. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_configure(config: "Config") -> None: + """Allow plugins and conftest files to perform initial configuration. + + This hook is called for every plugin and initial conftest file + after command line options have been parsed. + + After that, the hook is called for other conftest files as they are + imported. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + + :param _pytest.config.Config config: The pytest config object. + """ + + +# ------------------------------------------------------------------------- +# Bootstrapping hooks called for plugins registered early enough: +# internal and 3rd party plugins. +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_cmdline_parse( + pluginmanager: "PytestPluginManager", args: List[str] +) -> Optional["Config"]: + """Return an initialized config object, parsing the specified args. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + This hook will only be called for plugin classes passed to the + ``plugins`` arg when using `pytest.main`_ to perform an in-process + test run. + + :param _pytest.config.PytestPluginManager pluginmanager: Pytest plugin manager. + :param List[str] args: List of arguments passed on the command line. + """ + + +def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None: + """(**Deprecated**) modify command line arguments before option parsing. + + This hook is considered deprecated and will be removed in a future pytest version. Consider + using :func:`pytest_load_initial_conftests` instead. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + :param _pytest.config.Config config: The pytest config object. + :param List[str] args: Arguments passed on the command line. + """ + + +@hookspec(firstresult=True) +def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: + """Called for performing the main command line action. The default + implementation will invoke the configure hooks and runtest_mainloop. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + Stops at first non-None result, see :ref:`firstresult`. + + :param _pytest.config.Config config: The pytest config object. + """ + + +def pytest_load_initial_conftests( + early_config: "Config", parser: "Parser", args: List[str] +) -> None: + """Called to implement the loading of initial conftest files ahead + of command line option parsing. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + :param _pytest.config.Config early_config: The pytest config object. + :param List[str] args: Arguments passed on the command line. + :param _pytest.config.argparsing.Parser parser: To add command line options. + """ + + +# ------------------------------------------------------------------------- +# collection hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_collection(session: "Session") -> Optional[object]: + """Perform the collection phase for the given session. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + The default collection phase is this (see individual hooks for full details): + + 1. Starting from ``session`` as the initial collector: + + 1. ``pytest_collectstart(collector)`` + 2. ``report = pytest_make_collect_report(collector)`` + 3. ``pytest_exception_interact(collector, call, report)`` if an interactive exception occurred + 4. For each collected node: + + 1. If an item, ``pytest_itemcollected(item)`` + 2. If a collector, recurse into it. + + 5. ``pytest_collectreport(report)`` + + 2. ``pytest_collection_modifyitems(session, config, items)`` + + 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) + + 3. ``pytest_collection_finish(session)`` + 4. Set ``session.items`` to the list of collected items + 5. Set ``session.testscollected`` to the number of collected items + + You can implement this hook to only perform some action before collection, + for example the terminal plugin uses it to start displaying the collection + counter (and returns `None`). + + :param pytest.Session session: The pytest session object. + """ + + +def pytest_collection_modifyitems( + session: "Session", config: "Config", items: List["Item"] +) -> None: + """Called after collection has been performed. May filter or re-order + the items in-place. + + :param pytest.Session session: The pytest session object. + :param _pytest.config.Config config: The pytest config object. + :param List[pytest.Item] items: List of item objects. + """ + + +def pytest_collection_finish(session: "Session") -> None: + """Called after collection has been performed and modified. + + :param pytest.Session session: The pytest session object. + """ + + +@hookspec(firstresult=True) +def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]: + """Return True to prevent considering this path for collection. + + This hook is consulted for all files and directories prior to calling + more specific hooks. + + Stops at first non-None result, see :ref:`firstresult`. + + :param py.path.local path: The path to analyze. + :param _pytest.config.Config config: The pytest config object. + """ + + +def pytest_collect_file( + path: py.path.local, parent: "Collector" +) -> "Optional[Collector]": + """Create a Collector for the given path, or None if not relevant. + + The new node needs to have the specified ``parent`` as a parent. + + :param py.path.local path: The path to collect. + """ + + +# logging hooks for collection + + +def pytest_collectstart(collector: "Collector") -> None: + """Collector starts collecting.""" + + +def pytest_itemcollected(item: "Item") -> None: + """We just collected a test item.""" + + +def pytest_collectreport(report: "CollectReport") -> None: + """Collector finished collecting.""" + + +def pytest_deselected(items: Sequence["Item"]) -> None: + """Called for deselected test items, e.g. by keyword. + + May be called multiple times. + """ + + +@hookspec(firstresult=True) +def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectReport]": + """Perform ``collector.collect()`` and return a CollectReport. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +# ------------------------------------------------------------------------- +# Python test function related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module"]: + """Return a Module collector or None for the given path. + + This hook will be called for each matching test module path. + The pytest_collect_file hook needs to be used if you want to + create test modules for files that do not match as a test module. + + Stops at first non-None result, see :ref:`firstresult`. + + :param py.path.local path: The path of module to collect. + """ + + +@hookspec(firstresult=True) +def pytest_pycollect_makeitem( + collector: "PyCollector", name: str, obj: object +) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]: + """Return a custom item/collector for a Python object in a module, or None. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +@hookspec(firstresult=True) +def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: + """Call underlying test function. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_generate_tests(metafunc: "Metafunc") -> None: + """Generate (multiple) parametrized calls to a test function.""" + + +@hookspec(firstresult=True) +def pytest_make_parametrize_id( + config: "Config", val: object, argname: str +) -> Optional[str]: + """Return a user-friendly string representation of the given ``val`` + that will be used by @pytest.mark.parametrize calls, or None if the hook + doesn't know about ``val``. + + The parameter name is available as ``argname``, if required. + + Stops at first non-None result, see :ref:`firstresult`. + + :param _pytest.config.Config config: The pytest config object. + :param val: The parametrized value. + :param str argname: The automatic parameter name produced by pytest. + """ + + +# ------------------------------------------------------------------------- +# runtest related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_runtestloop(session: "Session") -> Optional[object]: + """Perform the main runtest loop (after collection finished). + + The default hook implementation performs the runtest protocol for all items + collected in the session (``session.items``), unless the collection failed + or the ``collectonly`` pytest option is set. + + If at any point :py:func:`pytest.exit` is called, the loop is + terminated immediately. + + If at any point ``session.shouldfail`` or ``session.shouldstop`` are set, the + loop is terminated after the runtest protocol for the current item is finished. + + :param pytest.Session session: The pytest session object. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_protocol( + item: "Item", nextitem: "Optional[Item]" +) -> Optional[object]: + """Perform the runtest protocol for a single test item. + + The default runtest protocol is this (see individual hooks for full details): + + - ``pytest_runtest_logstart(nodeid, location)`` + + - Setup phase: + - ``call = pytest_runtest_setup(item)`` (wrapped in ``CallInfo(when="setup")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Call phase, if the the setup passed and the ``setuponly`` pytest option is not set: + - ``call = pytest_runtest_call(item)`` (wrapped in ``CallInfo(when="call")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Teardown phase: + - ``call = pytest_runtest_teardown(item, nextitem)`` (wrapped in ``CallInfo(when="teardown")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - ``pytest_runtest_logfinish(nodeid, location)`` + + :param item: Test item for which the runtest protocol is performed. + :param nextitem: The scheduled-to-be-next test item (or None if this is the end my friend). + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + """ + + +def pytest_runtest_logstart( + nodeid: str, location: Tuple[str, Optional[int], str] +) -> None: + """Called at the start of running the runtest protocol for a single item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param str nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)``. + """ + + +def pytest_runtest_logfinish( + nodeid: str, location: Tuple[str, Optional[int], str] +) -> None: + """Called at the end of running the runtest protocol for a single item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param str nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)``. + """ + + +def pytest_runtest_setup(item: "Item") -> None: + """Called to perform the setup phase for a test item. + + The default implementation runs ``setup()`` on ``item`` and all of its + parents (which haven't been setup yet). This includes obtaining the + values of fixtures required by the item (which haven't been obtained + yet). + """ + + +def pytest_runtest_call(item: "Item") -> None: + """Called to run the test for test item (the call phase). + + The default implementation calls ``item.runtest()``. + """ + + +def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None: + """Called to perform the teardown phase for a test item. + + The default implementation runs the finalizers and calls ``teardown()`` + on ``item`` and all of its parents (which need to be torn down). This + includes running the teardown phase of fixtures required by the item (if + they go out of scope). + + :param nextitem: + The scheduled-to-be-next test item (None if no further test item is + scheduled). This argument can be used to perform exact teardowns, + i.e. calling just enough finalizers so that nextitem only needs to + call setup-functions. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_makereport( + item: "Item", call: "CallInfo[None]" +) -> Optional["TestReport"]: + """Called to create a :py:class:`_pytest.reports.TestReport` for each of + the setup, call and teardown runtest phases of a test item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param CallInfo[None] call: The ``CallInfo`` for the phase. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_runtest_logreport(report: "TestReport") -> None: + """Process the :py:class:`_pytest.reports.TestReport` produced for each + of the setup, call and teardown runtest phases of an item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + """ + + +@hookspec(firstresult=True) +def pytest_report_to_serializable( + config: "Config", report: Union["CollectReport", "TestReport"], +) -> Optional[Dict[str, Any]]: + """Serialize the given report object into a data structure suitable for + sending over the wire, e.g. converted to JSON.""" + + +@hookspec(firstresult=True) +def pytest_report_from_serializable( + config: "Config", data: Dict[str, Any], +) -> Optional[Union["CollectReport", "TestReport"]]: + """Restore a report object previously serialized with pytest_report_to_serializable().""" + + +# ------------------------------------------------------------------------- +# Fixture related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_fixture_setup( + fixturedef: "FixtureDef[Any]", request: "SubRequest" +) -> Optional[object]: + """Perform fixture setup execution. + + :returns: The return value of the call to the fixture function. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + If the fixture function returns None, other implementations of + this hook function will continue to be called, according to the + behavior of the :ref:`firstresult` option. + """ + + +def pytest_fixture_post_finalizer( + fixturedef: "FixtureDef[Any]", request: "SubRequest" +) -> None: + """Called after fixture teardown, but before the cache is cleared, so + the fixture result ``fixturedef.cached_result`` is still available (not + ``None``).""" + + +# ------------------------------------------------------------------------- +# test session related hooks +# ------------------------------------------------------------------------- + + +def pytest_sessionstart(session: "Session") -> None: + """Called after the ``Session`` object has been created and before performing collection + and entering the run test loop. + + :param pytest.Session session: The pytest session object. + """ + + +def pytest_sessionfinish( + session: "Session", exitstatus: Union[int, "ExitCode"], +) -> None: + """Called after whole test run finished, right before returning the exit status to the system. + + :param pytest.Session session: The pytest session object. + :param int exitstatus: The status which pytest will return to the system. + """ + + +def pytest_unconfigure(config: "Config") -> None: + """Called before test process is exited. + + :param _pytest.config.Config config: The pytest config object. + """ + + +# ------------------------------------------------------------------------- +# hooks for customizing the assert methods +# ------------------------------------------------------------------------- + + +def pytest_assertrepr_compare( + config: "Config", op: str, left: object, right: object +) -> Optional[List[str]]: + """Return explanation for comparisons in failing assert expressions. + + Return None for no custom explanation, otherwise return a list + of strings. The strings will be joined by newlines but any newlines + *in* a string will be escaped. Note that all but the first line will + be indented slightly, the intention is for the first line to be a summary. + + :param _pytest.config.Config config: The pytest config object. + """ + + +def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> None: + """**(Experimental)** Called whenever an assertion passes. + + .. versionadded:: 5.0 + + Use this hook to do some processing after a passing assertion. + The original assertion information is available in the `orig` string + and the pytest introspected assertion information is available in the + `expl` string. + + This hook must be explicitly enabled by the ``enable_assertion_pass_hook`` + ini-file option: + + .. code-block:: ini + + [pytest] + enable_assertion_pass_hook=true + + You need to **clean the .pyc** files in your project directory and interpreter libraries + when enabling this option, as assertions will require to be re-written. + + :param pytest.Item item: pytest item object of current test. + :param int lineno: Line number of the assert statement. + :param str orig: String with the original assertion. + :param str expl: String with the assert explanation. + + .. note:: + + This hook is **experimental**, so its parameters or even the hook itself might + be changed/removed without warning in any future pytest release. + + If you find this hook useful, please share your feedback in an issue. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing reporting (invoked from _pytest_terminal). +# ------------------------------------------------------------------------- + + +def pytest_report_header( + config: "Config", startdir: py.path.local +) -> Union[str, List[str]]: + """Return a string or list of strings to be displayed as header info for terminal reporting. + + :param _pytest.config.Config config: The pytest config object. + :param py.path.local startdir: The starting dir. + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + """ + + +def pytest_report_collectionfinish( + config: "Config", startdir: py.path.local, items: Sequence["Item"], +) -> Union[str, List[str]]: + """Return a string or list of strings to be displayed after collection + has finished successfully. + + These strings will be displayed after the standard "collected X items" message. + + .. versionadded:: 3.2 + + :param _pytest.config.Config config: The pytest config object. + :param py.path.local startdir: The starting dir. + :param items: List of pytest items that are going to be executed; this list should not be modified. + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + """ + + +@hookspec(firstresult=True) +def pytest_report_teststatus( + report: Union["CollectReport", "TestReport"], config: "Config" +) -> Tuple[ + str, str, Union[str, Mapping[str, bool]], +]: + """Return result-category, shortletter and verbose word for status + reporting. + + The result-category is a category in which to count the result, for + example "passed", "skipped", "error" or the empty string. + + The shortletter is shown as testing progresses, for example ".", "s", + "E" or the empty string. + + The verbose word is shown as testing progresses in verbose mode, for + example "PASSED", "SKIPPED", "ERROR" or the empty string. + + pytest may style these implicitly according to the report outcome. + To provide explicit styling, return a tuple for the verbose word, + for example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :param report: The report object whose status is to be returned. + :param _pytest.config.Config config: The pytest config object. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_terminal_summary( + terminalreporter: "TerminalReporter", exitstatus: "ExitCode", config: "Config", +) -> None: + """Add a section to terminal summary reporting. + + :param _pytest.terminal.TerminalReporter terminalreporter: The internal terminal reporter object. + :param int exitstatus: The exit status that will be reported back to the OS. + :param _pytest.config.Config config: The pytest config object. + + .. versionadded:: 4.2 + The ``config`` parameter. + """ + + +@hookspec(historic=True, warn_on_impl=WARNING_CAPTURED_HOOK) +def pytest_warning_captured( + warning_message: "warnings.WarningMessage", + when: "Literal['config', 'collect', 'runtest']", + item: Optional["Item"], + location: Optional[Tuple[str, int, str]], +) -> None: + """(**Deprecated**) Process a warning captured by the internal pytest warnings plugin. + + .. deprecated:: 6.0 + + This hook is considered deprecated and will be removed in a future pytest version. + Use :func:`pytest_warning_recorded` instead. + + :param warnings.WarningMessage warning_message: + The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains + the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param str when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param pytest.Item|None item: + The item being executed if ``when`` is ``"runtest"``, otherwise ``None``. + + :param tuple location: + When available, holds information about the execution context of the captured + warning (filename, linenumber, function). ``function`` evaluates to + when the execution context is at the module level. + """ + + +@hookspec(historic=True) +def pytest_warning_recorded( + warning_message: "warnings.WarningMessage", + when: "Literal['config', 'collect', 'runtest']", + nodeid: str, + location: Optional[Tuple[str, int, str]], +) -> None: + """Process a warning captured by the internal pytest warnings plugin. + + :param warnings.WarningMessage warning_message: + The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains + the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param str when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param str nodeid: + Full id of the item. + + :param tuple|None location: + When available, holds information about the execution context of the captured + warning (filename, linenumber, function). ``function`` evaluates to + when the execution context is at the module level. + + .. versionadded:: 6.0 + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing skipping +# ------------------------------------------------------------------------- + + +def pytest_markeval_namespace(config: "Config") -> Dict[str, Any]: + """Called when constructing the globals dictionary used for + evaluating string conditions in xfail/skipif markers. + + This is useful when the condition for a marker requires + objects that are expensive or impossible to obtain during + collection time, which is required by normal boolean + conditions. + + .. versionadded:: 6.2 + + :param _pytest.config.Config config: The pytest config object. + :returns: A dictionary of additional globals to add. + """ + + +# ------------------------------------------------------------------------- +# error handling and internal debugging hooks +# ------------------------------------------------------------------------- + + +def pytest_internalerror( + excrepr: "ExceptionRepr", excinfo: "ExceptionInfo[BaseException]", +) -> Optional[bool]: + """Called for internal errors. + + Return True to suppress the fallback handling of printing an + INTERNALERROR message directly to sys.stderr. + """ + + +def pytest_keyboard_interrupt( + excinfo: "ExceptionInfo[Union[KeyboardInterrupt, Exit]]", +) -> None: + """Called for keyboard interrupt.""" + + +def pytest_exception_interact( + node: Union["Item", "Collector"], + call: "CallInfo[Any]", + report: Union["CollectReport", "TestReport"], +) -> None: + """Called when an exception was raised which can potentially be + interactively handled. + + May be called during collection (see :py:func:`pytest_make_collect_report`), + in which case ``report`` is a :py:class:`_pytest.reports.CollectReport`. + + May be called during runtest of an item (see :py:func:`pytest_runtest_protocol`), + in which case ``report`` is a :py:class:`_pytest.reports.TestReport`. + + This hook is not called if the exception that was raised is an internal + exception like ``skip.Exception``. + """ + + +def pytest_enter_pdb(config: "Config", pdb: "pdb.Pdb") -> None: + """Called upon pdb.set_trace(). + + Can be used by plugins to take special action just before the python + debugger enters interactive mode. + + :param _pytest.config.Config config: The pytest config object. + :param pdb.Pdb pdb: The Pdb instance. + """ + + +def pytest_leave_pdb(config: "Config", pdb: "pdb.Pdb") -> None: + """Called when leaving pdb (e.g. with continue after pdb.set_trace()). + + Can be used by plugins to take special action just after the python + debugger leaves interactive mode. + + :param _pytest.config.Config config: The pytest config object. + :param pdb.Pdb pdb: The Pdb instance. + """ diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/junitxml.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/junitxml.py new file mode 100644 index 0000000..c4761cd --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/junitxml.py @@ -0,0 +1,700 @@ +"""Report test results in JUnit-XML format, for use with Jenkins and build +integration servers. + +Based on initial code from Ross Lawley. + +Output conforms to +https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd +""" +import functools +import os +import platform +import re +import xml.etree.ElementTree as ET +from datetime import datetime +from typing import Callable +from typing import Dict +from typing import List +from typing import Match +from typing import Optional +from typing import Tuple +from typing import Union + +import pytest +from _pytest import nodes +from _pytest import timing +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprFileLocation +from _pytest.config import Config +from _pytest.config import filename_arg +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureRequest +from _pytest.reports import TestReport +from _pytest.store import StoreKey +from _pytest.terminal import TerminalReporter + + +xml_key = StoreKey["LogXML"]() + + +def bin_xml_escape(arg: object) -> str: + r"""Visually escape invalid XML characters. + + For example, transforms + 'hello\aworld\b' + into + 'hello#x07world#x08' + Note that the #xABs are *not* XML escapes - missing the ampersand «. + The idea is to escape visually for the user rather than for XML itself. + """ + + def repl(matchobj: Match[str]) -> str: + i = ord(matchobj.group()) + if i <= 0xFF: + return "#x%02X" % i + else: + return "#x%04X" % i + + # The spec range of valid chars is: + # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + # For an unknown(?) reason, we disallow #x7F (DEL) as well. + illegal_xml_re = ( + "[^\u0009\u000A\u000D\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]" + ) + return re.sub(illegal_xml_re, repl, str(arg)) + + +def merge_family(left, right) -> None: + result = {} + for kl, vl in left.items(): + for kr, vr in right.items(): + if not isinstance(vl, list): + raise TypeError(type(vl)) + result[kl] = vl + vr + left.update(result) + + +families = {} +families["_base"] = {"testcase": ["classname", "name"]} +families["_base_legacy"] = {"testcase": ["file", "line", "url"]} + +# xUnit 1.x inherits legacy attributes. +families["xunit1"] = families["_base"].copy() +merge_family(families["xunit1"], families["_base_legacy"]) + +# xUnit 2.x uses strict base attributes. +families["xunit2"] = families["_base"] + + +class _NodeReporter: + def __init__(self, nodeid: Union[str, TestReport], xml: "LogXML") -> None: + self.id = nodeid + self.xml = xml + self.add_stats = self.xml.add_stats + self.family = self.xml.family + self.duration = 0 + self.properties: List[Tuple[str, str]] = [] + self.nodes: List[ET.Element] = [] + self.attrs: Dict[str, str] = {} + + def append(self, node: ET.Element) -> None: + self.xml.add_stats(node.tag) + self.nodes.append(node) + + def add_property(self, name: str, value: object) -> None: + self.properties.append((str(name), bin_xml_escape(value))) + + def add_attribute(self, name: str, value: object) -> None: + self.attrs[str(name)] = bin_xml_escape(value) + + def make_properties_node(self) -> Optional[ET.Element]: + """Return a Junit node containing custom properties, if any.""" + if self.properties: + properties = ET.Element("properties") + for name, value in self.properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None + + def record_testreport(self, testreport: TestReport) -> None: + names = mangle_test_address(testreport.nodeid) + existing_attrs = self.attrs + classnames = names[:-1] + if self.xml.prefix: + classnames.insert(0, self.xml.prefix) + attrs: Dict[str, str] = { + "classname": ".".join(classnames), + "name": bin_xml_escape(names[-1]), + "file": testreport.location[0], + } + if testreport.location[1] is not None: + attrs["line"] = str(testreport.location[1]) + if hasattr(testreport, "url"): + attrs["url"] = testreport.url + self.attrs = attrs + self.attrs.update(existing_attrs) # Restore any user-defined attributes. + + # Preserve legacy testcase behavior. + if self.family == "xunit1": + return + + # Filter out attributes not permitted by this test family. + # Including custom attributes because they are not valid here. + temp_attrs = {} + for key in self.attrs.keys(): + if key in families[self.family]["testcase"]: + temp_attrs[key] = self.attrs[key] + self.attrs = temp_attrs + + def to_xml(self) -> ET.Element: + testcase = ET.Element("testcase", self.attrs, time="%.3f" % self.duration) + properties = self.make_properties_node() + if properties is not None: + testcase.append(properties) + testcase.extend(self.nodes) + return testcase + + def _add_simple(self, tag: str, message: str, data: Optional[str] = None) -> None: + node = ET.Element(tag, message=message) + node.text = bin_xml_escape(data) + self.append(node) + + def write_captured_output(self, report: TestReport) -> None: + if not self.xml.log_passing_tests and report.passed: + return + + content_out = report.capstdout + content_log = report.caplog + content_err = report.capstderr + if self.xml.logging == "no": + return + content_all = "" + if self.xml.logging in ["log", "all"]: + content_all = self._prepare_content(content_log, " Captured Log ") + if self.xml.logging in ["system-out", "out-err", "all"]: + content_all += self._prepare_content(content_out, " Captured Out ") + self._write_content(report, content_all, "system-out") + content_all = "" + if self.xml.logging in ["system-err", "out-err", "all"]: + content_all += self._prepare_content(content_err, " Captured Err ") + self._write_content(report, content_all, "system-err") + content_all = "" + if content_all: + self._write_content(report, content_all, "system-out") + + def _prepare_content(self, content: str, header: str) -> str: + return "\n".join([header.center(80, "-"), content, ""]) + + def _write_content(self, report: TestReport, content: str, jheader: str) -> None: + tag = ET.Element(jheader) + tag.text = bin_xml_escape(content) + self.append(tag) + + def append_pass(self, report: TestReport) -> None: + self.add_stats("passed") + + def append_failure(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + if hasattr(report, "wasxfail"): + self._add_simple("skipped", "xfail-marked test passes unexpectedly") + else: + assert report.longrepr is not None + reprcrash: Optional[ReprFileLocation] = getattr( + report.longrepr, "reprcrash", None + ) + if reprcrash is not None: + message = reprcrash.message + else: + message = str(report.longrepr) + message = bin_xml_escape(message) + self._add_simple("failure", message, str(report.longrepr)) + + def append_collect_error(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + assert report.longrepr is not None + self._add_simple("error", "collection failure", str(report.longrepr)) + + def append_collect_skipped(self, report: TestReport) -> None: + self._add_simple("skipped", "collection skipped", str(report.longrepr)) + + def append_error(self, report: TestReport) -> None: + assert report.longrepr is not None + reprcrash: Optional[ReprFileLocation] = getattr( + report.longrepr, "reprcrash", None + ) + if reprcrash is not None: + reason = reprcrash.message + else: + reason = str(report.longrepr) + + if report.when == "teardown": + msg = f'failed on teardown with "{reason}"' + else: + msg = f'failed on setup with "{reason}"' + self._add_simple("error", msg, str(report.longrepr)) + + def append_skipped(self, report: TestReport) -> None: + if hasattr(report, "wasxfail"): + xfailreason = report.wasxfail + if xfailreason.startswith("reason: "): + xfailreason = xfailreason[8:] + xfailreason = bin_xml_escape(xfailreason) + skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason) + self.append(skipped) + else: + assert isinstance(report.longrepr, tuple) + filename, lineno, skipreason = report.longrepr + if skipreason.startswith("Skipped: "): + skipreason = skipreason[9:] + details = f"{filename}:{lineno}: {skipreason}" + + skipped = ET.Element("skipped", type="pytest.skip", message=skipreason) + skipped.text = bin_xml_escape(details) + self.append(skipped) + self.write_captured_output(report) + + def finalize(self) -> None: + data = self.to_xml() + self.__dict__.clear() + # Type ignored becuase mypy doesn't like overriding a method. + # Also the return value doesn't match... + self.to_xml = lambda: data # type: ignore[assignment] + + +def _warn_incompatibility_with_xunit2( + request: FixtureRequest, fixture_name: str +) -> None: + """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" + from _pytest.warning_types import PytestWarning + + xml = request.config._store.get(xml_key, None) + if xml is not None and xml.family not in ("xunit1", "legacy"): + request.node.warn( + PytestWarning( + "{fixture_name} is incompatible with junit_family '{family}' (use 'legacy' or 'xunit1')".format( + fixture_name=fixture_name, family=xml.family + ) + ) + ) + + +@pytest.fixture +def record_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra properties to the calling test. + + User properties become part of the test report and are available to the + configured reporters, like JUnit XML. + + The fixture is callable with ``name, value``. The value is automatically + XML-encoded. + + Example:: + + def test_function(record_property): + record_property("example_key", 1) + """ + _warn_incompatibility_with_xunit2(request, "record_property") + + def append_property(name: str, value: object) -> None: + request.node.user_properties.append((name, value)) + + return append_property + + +@pytest.fixture +def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra xml attributes to the tag for the calling test. + + The fixture is callable with ``name, value``. The value is + automatically XML-encoded. + """ + from _pytest.warning_types import PytestExperimentalApiWarning + + request.node.warn( + PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + ) + + _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + + # Declare noop + def add_attr_noop(name: str, value: object) -> None: + pass + + attr_func = add_attr_noop + + xml = request.config._store.get(xml_key, None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + attr_func = node_reporter.add_attribute + + return attr_func + + +def _check_record_param_type(param: str, v: str) -> None: + """Used by record_testsuite_property to check that the given parameter name is of the proper + type.""" + __tracebackhide__ = True + if not isinstance(v, str): + msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable] + raise TypeError(msg.format(param=param, g=type(v).__name__)) + + +@pytest.fixture(scope="session") +def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Record a new ```` tag as child of the root ````. + + This is suitable to writing global information regarding the entire test + suite, and is compatible with ``xunit2`` JUnit family. + + This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: + + .. code-block:: python + + def test_foo(record_testsuite_property): + record_testsuite_property("ARCH", "PPC") + record_testsuite_property("STORAGE_TYPE", "CEPH") + + ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. + + .. warning:: + + Currently this fixture **does not work** with the + `pytest-xdist `__ plugin. See issue + `#7767 `__ for details. + """ + + __tracebackhide__ = True + + def record_func(name: str, value: object) -> None: + """No-op function in case --junitxml was not passed in the command-line.""" + __tracebackhide__ = True + _check_record_param_type("name", name) + + xml = request.config._store.get(xml_key, None) + if xml is not None: + record_func = xml.add_global_property # noqa + return record_func + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--junitxml", + "--junit-xml", + action="store", + dest="xmlpath", + metavar="path", + type=functools.partial(filename_arg, optname="--junitxml"), + default=None, + help="create junit-xml style report file at given path.", + ) + group.addoption( + "--junitprefix", + "--junit-prefix", + action="store", + metavar="str", + default=None, + help="prepend prefix to classnames in junit-xml output", + ) + parser.addini( + "junit_suite_name", "Test suite name for JUnit report", default="pytest" + ) + parser.addini( + "junit_logging", + "Write captured log messages to JUnit report: " + "one of no|log|system-out|system-err|out-err|all", + default="no", + ) + parser.addini( + "junit_log_passing_tests", + "Capture log information for passing tests to JUnit report: ", + type="bool", + default=True, + ) + parser.addini( + "junit_duration_report", + "Duration time to report: one of total|call", + default="total", + ) # choices=['total', 'call']) + parser.addini( + "junit_family", + "Emit XML for schema: one of legacy|xunit1|xunit2", + default="xunit2", + ) + + +def pytest_configure(config: Config) -> None: + xmlpath = config.option.xmlpath + # Prevent opening xmllog on worker nodes (xdist). + if xmlpath and not hasattr(config, "workerinput"): + junit_family = config.getini("junit_family") + config._store[xml_key] = LogXML( + xmlpath, + config.option.junitprefix, + config.getini("junit_suite_name"), + config.getini("junit_logging"), + config.getini("junit_duration_report"), + junit_family, + config.getini("junit_log_passing_tests"), + ) + config.pluginmanager.register(config._store[xml_key]) + + +def pytest_unconfigure(config: Config) -> None: + xml = config._store.get(xml_key, None) + if xml: + del config._store[xml_key] + config.pluginmanager.unregister(xml) + + +def mangle_test_address(address: str) -> List[str]: + path, possible_open_bracket, params = address.partition("[") + names = path.split("::") + try: + names.remove("()") + except ValueError: + pass + # Convert file path to dotted path. + names[0] = names[0].replace(nodes.SEP, ".") + names[0] = re.sub(r"\.py$", "", names[0]) + # Put any params back. + names[-1] += possible_open_bracket + params + return names + + +class LogXML: + def __init__( + self, + logfile, + prefix: Optional[str], + suite_name: str = "pytest", + logging: str = "no", + report_duration: str = "total", + family="xunit1", + log_passing_tests: bool = True, + ) -> None: + logfile = os.path.expanduser(os.path.expandvars(logfile)) + self.logfile = os.path.normpath(os.path.abspath(logfile)) + self.prefix = prefix + self.suite_name = suite_name + self.logging = logging + self.log_passing_tests = log_passing_tests + self.report_duration = report_duration + self.family = family + self.stats: Dict[str, int] = dict.fromkeys( + ["error", "passed", "failure", "skipped"], 0 + ) + self.node_reporters: Dict[ + Tuple[Union[str, TestReport], object], _NodeReporter + ] = ({}) + self.node_reporters_ordered: List[_NodeReporter] = [] + self.global_properties: List[Tuple[str, str]] = [] + + # List of reports that failed on call but teardown is pending. + self.open_reports: List[TestReport] = [] + self.cnt_double_fail_tests = 0 + + # Replaces convenience family with real family. + if self.family == "legacy": + self.family = "xunit1" + + def finalize(self, report: TestReport) -> None: + nodeid = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + reporter = self.node_reporters.pop((nodeid, workernode)) + if reporter is not None: + reporter.finalize() + + def node_reporter(self, report: Union[TestReport, str]) -> _NodeReporter: + nodeid: Union[str, TestReport] = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + + key = nodeid, workernode + + if key in self.node_reporters: + # TODO: breaks for --dist=each + return self.node_reporters[key] + + reporter = _NodeReporter(nodeid, self) + + self.node_reporters[key] = reporter + self.node_reporters_ordered.append(reporter) + + return reporter + + def add_stats(self, key: str) -> None: + if key in self.stats: + self.stats[key] += 1 + + def _opentestcase(self, report: TestReport) -> _NodeReporter: + reporter = self.node_reporter(report) + reporter.record_testreport(report) + return reporter + + def pytest_runtest_logreport(self, report: TestReport) -> None: + """Handle a setup/call/teardown report, generating the appropriate + XML tags as necessary. + + Note: due to plugins like xdist, this hook may be called in interlaced + order with reports from other nodes. For example: + + Usual call order: + -> setup node1 + -> call node1 + -> teardown node1 + -> setup node2 + -> call node2 + -> teardown node2 + + Possible call order in xdist: + -> setup node1 + -> call node1 + -> setup node2 + -> call node2 + -> teardown node2 + -> teardown node1 + """ + close_report = None + if report.passed: + if report.when == "call": # ignore setup/teardown + reporter = self._opentestcase(report) + reporter.append_pass(report) + elif report.failed: + if report.when == "teardown": + # The following vars are needed when xdist plugin is used. + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + # We need to open new testcase in case we have failure in + # call and error in teardown in order to follow junit + # schema. + self.finalize(close_report) + self.cnt_double_fail_tests += 1 + reporter = self._opentestcase(report) + if report.when == "call": + reporter.append_failure(report) + self.open_reports.append(report) + if not self.log_passing_tests: + reporter.write_captured_output(report) + else: + reporter.append_error(report) + elif report.skipped: + reporter = self._opentestcase(report) + reporter.append_skipped(report) + self.update_testcase_duration(report) + if report.when == "teardown": + reporter = self._opentestcase(report) + reporter.write_captured_output(report) + + for propname, propvalue in report.user_properties: + reporter.add_property(propname, str(propvalue)) + + self.finalize(report) + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + self.open_reports.remove(close_report) + + def update_testcase_duration(self, report: TestReport) -> None: + """Accumulate total duration for nodeid from given report and update + the Junit.testcase with the new total if already created.""" + if self.report_duration == "total" or report.when == self.report_duration: + reporter = self.node_reporter(report) + reporter.duration += getattr(report, "duration", 0.0) + + def pytest_collectreport(self, report: TestReport) -> None: + if not report.passed: + reporter = self._opentestcase(report) + if report.failed: + reporter.append_collect_error(report) + else: + reporter.append_collect_skipped(report) + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: + reporter = self.node_reporter("internal") + reporter.attrs.update(classname="pytest", name="internal") + reporter._add_simple("error", "internal error", str(excrepr)) + + def pytest_sessionstart(self) -> None: + self.suite_start_time = timing.time() + + def pytest_sessionfinish(self) -> None: + dirname = os.path.dirname(os.path.abspath(self.logfile)) + if not os.path.isdir(dirname): + os.makedirs(dirname) + logfile = open(self.logfile, "w", encoding="utf-8") + suite_stop_time = timing.time() + suite_time_delta = suite_stop_time - self.suite_start_time + + numtests = ( + self.stats["passed"] + + self.stats["failure"] + + self.stats["skipped"] + + self.stats["error"] + - self.cnt_double_fail_tests + ) + logfile.write('') + + suite_node = ET.Element( + "testsuite", + name=self.suite_name, + errors=str(self.stats["error"]), + failures=str(self.stats["failure"]), + skipped=str(self.stats["skipped"]), + tests=str(numtests), + time="%.3f" % suite_time_delta, + timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), + hostname=platform.node(), + ) + global_properties = self._get_global_properties_node() + if global_properties is not None: + suite_node.append(global_properties) + for node_reporter in self.node_reporters_ordered: + suite_node.append(node_reporter.to_xml()) + testsuites = ET.Element("testsuites") + testsuites.append(suite_node) + logfile.write(ET.tostring(testsuites, encoding="unicode")) + logfile.close() + + def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: + terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") + + def add_global_property(self, name: str, value: object) -> None: + __tracebackhide__ = True + _check_record_param_type("name", name) + self.global_properties.append((name, bin_xml_escape(value))) + + def _get_global_properties_node(self) -> Optional[ET.Element]: + """Return a Junit node containing custom properties, if any.""" + if self.global_properties: + properties = ET.Element("properties") + for name, value in self.global_properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/logging.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/logging.py new file mode 100644 index 0000000..2e48473 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/logging.py @@ -0,0 +1,821 @@ +"""Access and control log capturing.""" +import logging +import os +import re +import sys +from contextlib import contextmanager +from io import StringIO +from pathlib import Path +from typing import AbstractSet +from typing import Dict +from typing import Generator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Tuple +from typing import TypeVar +from typing import Union + +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.capture import CaptureManager +from _pytest.compat import final +from _pytest.compat import nullcontext +from _pytest.config import _strtobool +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.store import StoreKey +from _pytest.terminal import TerminalReporter + + +DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" +DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +caplog_handler_key = StoreKey["LogCaptureHandler"]() +caplog_records_key = StoreKey[Dict[str, List[logging.LogRecord]]]() + + +def _remove_ansi_escape_sequences(text: str) -> str: + return _ANSI_ESCAPE_SEQ.sub("", text) + + +class ColoredLevelFormatter(logging.Formatter): + """A logging formatter which colorizes the %(levelname)..s part of the + log format passed to __init__.""" + + LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { + logging.CRITICAL: {"red"}, + logging.ERROR: {"red", "bold"}, + logging.WARNING: {"yellow"}, + logging.WARN: {"yellow"}, + logging.INFO: {"green"}, + logging.DEBUG: {"purple"}, + logging.NOTSET: set(), + } + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*s)") + + def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._original_fmt = self._style._fmt + self._level_to_fmt_mapping: Dict[int, str] = {} + + assert self._fmt is not None + levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) + if not levelname_fmt_match: + return + levelname_fmt = levelname_fmt_match.group() + + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + formatted_levelname = levelname_fmt % { + "levelname": logging.getLevelName(level) + } + + # add ANSI escape sequences around the formatted levelname + color_kwargs = {name: True for name in color_opts} + colorized_formatted_levelname = terminalwriter.markup( + formatted_levelname, **color_kwargs + ) + self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( + colorized_formatted_levelname, self._fmt + ) + + def format(self, record: logging.LogRecord) -> str: + fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) + self._style._fmt = fmt + return super().format(record) + + +class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. + + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ + + def __init__(self, fmt: str, auto_indent: Union[int, str, bool, None]) -> None: + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + + @staticmethod + def _update_message( + record_dict: Dict[str, object], message: str + ) -> Dict[str, object]: + tmp = record_dict.copy() + tmp["message"] = message + return tmp + + @staticmethod + def _get_auto_indent(auto_indent_option: Union[int, str, bool, None]) -> int: + """Determine the current auto indentation setting. + + Specify auto indent behavior (on/off/fixed) by passing in + extra={"auto_indent": [value]} to the call to logging.log() or + using a --log-auto-indent [value] command line or the + log_auto_indent [value] config option. + + Default behavior is auto-indent off. + + Using the string "True" or "on" or the boolean True as the value + turns auto indent on, using the string "False" or "off" or the + boolean False or the int 0 turns it off, and specifying a + positive integer fixes the indentation position to the value + specified. + + Any other values for the option are invalid, and will silently be + converted to the default. + + :param None|bool|int|str auto_indent_option: + User specified option for indentation from command line, config + or extra kwarg. Accepts int, bool or str. str option accepts the + same range of values as boolean config options, as well as + positive integers represented in str form. + + :returns: + Indentation value, which can be + -1 (automatically determine indentation) or + 0 (auto-indent turned off) or + >0 (explicitly set indentation position). + """ + + if auto_indent_option is None: + return 0 + elif isinstance(auto_indent_option, bool): + if auto_indent_option: + return -1 + else: + return 0 + elif isinstance(auto_indent_option, int): + return int(auto_indent_option) + elif isinstance(auto_indent_option, str): + try: + return int(auto_indent_option) + except ValueError: + pass + try: + if _strtobool(auto_indent_option): + return -1 + except ValueError: + return 0 + + return 0 + + def format(self, record: logging.LogRecord) -> str: + if "\n" in record.message: + if hasattr(record, "auto_indent"): + # Passed in from the "extra={}" kwarg on the call to logging.log(). + auto_indent = self._get_auto_indent(record.auto_indent) # type: ignore[attr-defined] + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % self._update_message(record.__dict__, lines[0]) + + if auto_indent < 0: + indentation = _remove_ansi_escape_sequences(formatted).find( + lines[0] + ) + else: + # Optimizes logging by allowing a fixed indentation. + indentation = auto_indent + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + return self._fmt % record.__dict__ + + +def get_option_ini(config: Config, *names: str): + for name in names: + ret = config.getoption(name) # 'default' arg won't work as expected + if ret is None: + ret = config.getini(name) + if ret: + return ret + + +def pytest_addoption(parser: Parser) -> None: + """Add options to control log capturing.""" + group = parser.getgroup("logging") + + def add_option_ini(option, dest, default=None, type=None, **kwargs): + parser.addini( + dest, default=default, type=type, help="default value for " + option + ) + group.addoption(option, dest=dest, **kwargs) + + add_option_ini( + "--log-level", + dest="log_level", + default=None, + metavar="LEVEL", + help=( + "level of messages to catch/display.\n" + "Not set by default, so it depends on the root/parent log handler's" + ' effective level, where it is "WARNING" by default.' + ), + ) + add_option_ini( + "--log-format", + dest="log_format", + default=DEFAULT_LOG_FORMAT, + help="log format as used by the logging module.", + ) + add_option_ini( + "--log-date-format", + dest="log_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="log date format as used by the logging module.", + ) + parser.addini( + "log_cli", + default=False, + type="bool", + help='enable log display during test run (also known as "live logging").', + ) + add_option_ini( + "--log-cli-level", dest="log_cli_level", default=None, help="cli logging level." + ) + add_option_ini( + "--log-cli-format", + dest="log_cli_format", + default=None, + help="log format as used by the logging module.", + ) + add_option_ini( + "--log-cli-date-format", + dest="log_cli_date_format", + default=None, + help="log date format as used by the logging module.", + ) + add_option_ini( + "--log-file", + dest="log_file", + default=None, + help="path to a file when logging will be written to.", + ) + add_option_ini( + "--log-file-level", + dest="log_file_level", + default=None, + help="log file logging level.", + ) + add_option_ini( + "--log-file-format", + dest="log_file_format", + default=DEFAULT_LOG_FORMAT, + help="log format as used by the logging module.", + ) + add_option_ini( + "--log-file-date-format", + dest="log_file_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="log date format as used by the logging module.", + ) + add_option_ini( + "--log-auto-indent", + dest="log_auto_indent", + default=None, + help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + ) + + +_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) + + +# Not using @contextmanager for performance reasons. +class catching_logs: + """Context manager that prepares the whole logging machinery properly.""" + + __slots__ = ("handler", "level", "orig_level") + + def __init__(self, handler: _HandlerType, level: Optional[int] = None) -> None: + self.handler = handler + self.level = level + + def __enter__(self): + root_logger = logging.getLogger() + if self.level is not None: + self.handler.setLevel(self.level) + root_logger.addHandler(self.handler) + if self.level is not None: + self.orig_level = root_logger.level + root_logger.setLevel(min(self.orig_level, self.level)) + return self.handler + + def __exit__(self, type, value, traceback): + root_logger = logging.getLogger() + if self.level is not None: + root_logger.setLevel(self.orig_level) + root_logger.removeHandler(self.handler) + + +class LogCaptureHandler(logging.StreamHandler): + """A logging handler that stores log records and the log text.""" + + stream: StringIO + + def __init__(self) -> None: + """Create a new log handler.""" + super().__init__(StringIO()) + self.records: List[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + """Keep the log records in a list in addition to the log text.""" + self.records.append(record) + super().emit(record) + + def reset(self) -> None: + self.records = [] + self.stream = StringIO() + + def handleError(self, record: logging.LogRecord) -> None: + if logging.raiseExceptions: + # Fail the test if the log message is bad (emit failed). + # The default behavior of logging is to print "Logging error" + # to stderr with the call stack and some extra details. + # pytest wants to make such mistakes visible during testing. + raise + + +@final +class LogCaptureFixture: + """Provides access and control of log capturing.""" + + def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._item = item + self._initial_handler_level: Optional[int] = None + # Dict of log name -> log level. + self._initial_logger_levels: Dict[Optional[str], int] = {} + + def _finalize(self) -> None: + """Finalize the fixture. + + This restores the log levels changed by :meth:`set_level`. + """ + # Restore log levels. + if self._initial_handler_level is not None: + self.handler.setLevel(self._initial_handler_level) + for logger_name, level in self._initial_logger_levels.items(): + logger = logging.getLogger(logger_name) + logger.setLevel(level) + + @property + def handler(self) -> LogCaptureHandler: + """Get the logging handler used by the fixture. + + :rtype: LogCaptureHandler + """ + return self._item._store[caplog_handler_key] + + def get_records(self, when: str) -> List[logging.LogRecord]: + """Get the logging records for one of the possible test phases. + + :param str when: + Which test phase to obtain the records from. Valid values are: "setup", "call" and "teardown". + + :returns: The list of captured records at the given stage. + :rtype: List[logging.LogRecord] + + .. versionadded:: 3.4 + """ + return self._item._store[caplog_records_key].get(when, []) + + @property + def text(self) -> str: + """The formatted log text.""" + return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) + + @property + def records(self) -> List[logging.LogRecord]: + """The list of log records.""" + return self.handler.records + + @property + def record_tuples(self) -> List[Tuple[str, int, str]]: + """A list of a stripped down version of log records intended + for use in assertion comparison. + + The format of the tuple is: + + (logger_name, log_level, message) + """ + return [(r.name, r.levelno, r.getMessage()) for r in self.records] + + @property + def messages(self) -> List[str]: + """A list of format-interpolated log messages. + + Unlike 'records', which contains the format string and parameters for + interpolation, log messages in this list are all interpolated. + + Unlike 'text', which contains the output from the handler, log + messages in this list are unadorned with levels, timestamps, etc, + making exact comparisons more reliable. + + Note that traceback or stack info (from :func:`logging.exception` or + the `exc_info` or `stack_info` arguments to the logging functions) is + not included, as this is added by the formatter in the handler. + + .. versionadded:: 3.7 + """ + return [r.getMessage() for r in self.records] + + def clear(self) -> None: + """Reset the list of log records and the captured log text.""" + self.handler.reset() + + def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: + """Set the level of a logger for the duration of a test. + + .. versionchanged:: 3.4 + The levels of the loggers changed by this function will be + restored to their initial values at the end of the test. + + :param int level: The level. + :param str logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + # Save the original log-level to restore it during teardown. + self._initial_logger_levels.setdefault(logger, logger_obj.level) + logger_obj.setLevel(level) + if self._initial_handler_level is None: + self._initial_handler_level = self.handler.level + self.handler.setLevel(level) + + @contextmanager + def at_level( + self, level: int, logger: Optional[str] = None + ) -> Generator[None, None, None]: + """Context manager that sets the level for capturing of logs. After + the end of the 'with' statement the level is restored to its original + value. + + :param int level: The level. + :param str logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + orig_level = logger_obj.level + logger_obj.setLevel(level) + handler_orig_level = self.handler.level + self.handler.setLevel(level) + try: + yield + finally: + logger_obj.setLevel(orig_level) + self.handler.setLevel(handler_orig_level) + + +@fixture +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]: + """Access and control log capturing. + + Captured logs are available through the following properties/methods:: + + * caplog.messages -> list of format-interpolated log messages + * caplog.text -> string containing formatted log output + * caplog.records -> list of logging.LogRecord instances + * caplog.record_tuples -> list of (logger_name, level, message) tuples + * caplog.clear() -> clear captured records and formatted log output string + """ + result = LogCaptureFixture(request.node, _ispytest=True) + yield result + result._finalize() + + +def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[int]: + for setting_name in setting_names: + log_level = config.getoption(setting_name) + if log_level is None: + log_level = config.getini(setting_name) + if log_level: + break + else: + return None + + if isinstance(log_level, str): + log_level = log_level.upper() + try: + return int(getattr(logging, log_level, log_level)) + except ValueError as e: + # Python logging does not recognise this as a logging level + raise UsageError( + "'{}' is not recognized as a logging level name for " + "'{}'. Please consider passing the " + "logging level num instead.".format(log_level, setting_name) + ) from e + + +# run after terminalreporter/capturemanager are configured +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") + + +class LoggingPlugin: + """Attaches to the logging module and captures log messages for each test.""" + + def __init__(self, config: Config) -> None: + """Create a new plugin to capture log messages. + + The formatter can be safely shared across all handlers so + create a single one for the entire test session here. + """ + self._config = config + + # Report logging. + self.formatter = self._create_formatter( + get_option_ini(config, "log_format"), + get_option_ini(config, "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_level = get_log_level_for_setting(config, "log_level") + self.caplog_handler = LogCaptureHandler() + self.caplog_handler.setFormatter(self.formatter) + self.report_handler = LogCaptureHandler() + self.report_handler.setFormatter(self.formatter) + + # File logging. + self.log_file_level = get_log_level_for_setting(config, "log_file_level") + log_file = get_option_ini(config, "log_file") or os.devnull + if log_file != os.devnull: + directory = os.path.dirname(os.path.abspath(log_file)) + if not os.path.isdir(directory): + os.makedirs(directory) + + self.log_file_handler = _FileHandler(log_file, mode="w", encoding="UTF-8") + log_file_format = get_option_ini(config, "log_file_format", "log_format") + log_file_date_format = get_option_ini( + config, "log_file_date_format", "log_date_format" + ) + + log_file_formatter = logging.Formatter( + log_file_format, datefmt=log_file_date_format + ) + self.log_file_handler.setFormatter(log_file_formatter) + + # CLI/live logging. + self.log_cli_level = get_log_level_for_setting( + config, "log_cli_level", "log_level" + ) + if self._log_cli_enabled(): + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + self.log_cli_handler: Union[ + _LiveLoggingStreamHandler, _LiveLoggingNullHandler + ] = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + else: + self.log_cli_handler = _LiveLoggingNullHandler() + log_cli_formatter = self._create_formatter( + get_option_ini(config, "log_cli_format", "log_format"), + get_option_ini(config, "log_cli_date_format", "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_cli_handler.setFormatter(log_cli_formatter) + + def _create_formatter(self, log_format, log_date_format, auto_indent): + # Color option doesn't exist if terminal plugin is disabled. + color = getattr(self._config.option, "color", "no") + if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format + ): + formatter: logging.Formatter = ColoredLevelFormatter( + create_terminal_writer(self._config), log_format, log_date_format + ) + else: + formatter = logging.Formatter(log_format, log_date_format) + + formatter._style = PercentStyleMultiline( + formatter._style._fmt, auto_indent=auto_indent + ) + + return formatter + + def set_log_path(self, fname: str) -> None: + """Set the filename parameter for Logging.FileHandler(). + + Creates parent directory if it does not exist. + + .. warning:: + This is an experimental API. + """ + fpath = Path(fname) + + if not fpath.is_absolute(): + fpath = self._config.rootpath / fpath + + if not fpath.parent.exists(): + fpath.parent.mkdir(exist_ok=True, parents=True) + + stream = fpath.open(mode="w", encoding="UTF-8") + if sys.version_info >= (3, 7): + old_stream = self.log_file_handler.setStream(stream) + else: + old_stream = self.log_file_handler.stream + self.log_file_handler.acquire() + try: + self.log_file_handler.flush() + self.log_file_handler.stream = stream + finally: + self.log_file_handler.release() + if old_stream: + old_stream.close() + + def _log_cli_enabled(self): + """Return whether live logging is enabled.""" + enabled = self._config.getoption( + "--log-cli-level" + ) is not None or self._config.getini("log_cli") + if not enabled: + return False + + terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter is None: + # terminal reporter is disabled e.g. by pytest-xdist. + return False + + return True + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_sessionstart(self) -> Generator[None, None, None]: + self.log_cli_handler.set_when("sessionstart") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_collection(self) -> Generator[None, None, None]: + self.log_cli_handler.set_when("collection") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtestloop(self, session: Session) -> Generator[None, None, None]: + if session.config.option.collectonly: + yield + return + + if self._log_cli_enabled() and self._config.getoption("verbose") < 1: + # The verbose flag is needed to avoid messy test progress output. + self._config.option.verbose = 1 + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield # Run all the tests. + + @hookimpl + def pytest_runtest_logstart(self) -> None: + self.log_cli_handler.reset() + self.log_cli_handler.set_when("start") + + @hookimpl + def pytest_runtest_logreport(self) -> None: + self.log_cli_handler.set_when("logreport") + + def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]: + """Implement the internals of the pytest_runtest_xxx() hooks.""" + with catching_logs( + self.caplog_handler, level=self.log_level, + ) as caplog_handler, catching_logs( + self.report_handler, level=self.log_level, + ) as report_handler: + caplog_handler.reset() + report_handler.reset() + item._store[caplog_records_key][when] = caplog_handler.records + item._store[caplog_handler_key] = caplog_handler + + yield + + log = report_handler.stream.getvalue().strip() + item.add_report_section(when, "log", log) + + @hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("setup") + + empty: Dict[str, List[logging.LogRecord]] = {} + item._store[caplog_records_key] = empty + yield from self._runtest_for(item, "setup") + + @hookimpl(hookwrapper=True) + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("call") + + yield from self._runtest_for(item, "call") + + @hookimpl(hookwrapper=True) + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("teardown") + + yield from self._runtest_for(item, "teardown") + del item._store[caplog_records_key] + del item._store[caplog_handler_key] + + @hookimpl + def pytest_runtest_logfinish(self) -> None: + self.log_cli_handler.set_when("finish") + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_sessionfinish(self) -> Generator[None, None, None]: + self.log_cli_handler.set_when("sessionfinish") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + + @hookimpl + def pytest_unconfigure(self) -> None: + # Close the FileHandler explicitly. + # (logging.shutdown might have lost the weakref?!) + self.log_file_handler.close() + + +class _FileHandler(logging.FileHandler): + """A logging FileHandler with pytest tweaks.""" + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingStreamHandler(logging.StreamHandler): + """A logging StreamHandler used by the live logging feature: it will + write a newline before the first log message in each test. + + During live logging we must also explicitly disable stdout/stderr + capturing otherwise it will get captured and won't appear in the + terminal. + """ + + # Officially stream needs to be a IO[str], but TerminalReporter + # isn't. So force it. + stream: TerminalReporter = None # type: ignore + + def __init__( + self, + terminal_reporter: TerminalReporter, + capture_manager: Optional[CaptureManager], + ) -> None: + logging.StreamHandler.__init__(self, stream=terminal_reporter) # type: ignore[arg-type] + self.capture_manager = capture_manager + self.reset() + self.set_when(None) + self._test_outcome_written = False + + def reset(self) -> None: + """Reset the handler; should be called before the start of each test.""" + self._first_record_emitted = False + + def set_when(self, when: Optional[str]) -> None: + """Prepare for the given test phase (setup/call/teardown).""" + self._when = when + self._section_name_shown = False + if when == "start": + self._test_outcome_written = False + + def emit(self, record: logging.LogRecord) -> None: + ctx_manager = ( + self.capture_manager.global_and_fixture_disabled() + if self.capture_manager + else nullcontext() + ) + with ctx_manager: + if not self._first_record_emitted: + self.stream.write("\n") + self._first_record_emitted = True + elif self._when in ("teardown", "finish"): + if not self._test_outcome_written: + self._test_outcome_written = True + self.stream.write("\n") + if not self._section_name_shown and self._when: + self.stream.section("live log " + self._when, sep="-", bold=True) + self._section_name_shown = True + super().emit(record) + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingNullHandler(logging.NullHandler): + """A logging handler used when live logging is disabled.""" + + def reset(self) -> None: + pass + + def set_when(self, when: str) -> None: + pass + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/main.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/main.py new file mode 100644 index 0000000..41a33d4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/main.py @@ -0,0 +1,876 @@ +"""Core implementation of the testing process: init, session, runtest loop.""" +import argparse +import fnmatch +import functools +import importlib +import os +import sys +from pathlib import Path +from typing import Callable +from typing import Dict +from typing import FrozenSet +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import attr +import py + +import _pytest._code +from _pytest import nodes +from _pytest.compat import final +from _pytest.config import Config +from _pytest.config import directory_arg +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureManager +from _pytest.outcomes import exit +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import visit +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.runner import collect_one_node +from _pytest.runner import SetupState + + +if TYPE_CHECKING: + from typing_extensions import Literal + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "norecursedirs", + "directory patterns to avoid for recursion", + type="args", + default=[ + "*.egg", + ".*", + "_darcs", + "build", + "CVS", + "dist", + "node_modules", + "venv", + "{arch}", + ], + ) + parser.addini( + "testpaths", + "directories to search for tests when no files or directories are given in the " + "command line.", + type="args", + default=[], + ) + group = parser.getgroup("general", "running and selection options") + group._addoption( + "-x", + "--exitfirst", + action="store_const", + dest="maxfail", + const=1, + help="exit instantly on first error or failed test.", + ) + group = parser.getgroup("pytest-warnings") + group.addoption( + "-W", + "--pythonwarnings", + action="append", + help="set which warnings to report, see -W option of python itself.", + ) + parser.addini( + "filterwarnings", + type="linelist", + help="Each line specifies a pattern for " + "warnings.filterwarnings. " + "Processed after -W/--pythonwarnings.", + ) + group._addoption( + "--maxfail", + metavar="num", + action="store", + type=int, + dest="maxfail", + default=0, + help="exit after first num failures or errors.", + ) + group._addoption( + "--strict-config", + action="store_true", + help="any warnings encountered while parsing the `pytest` section of the configuration file raise errors.", + ) + group._addoption( + "--strict-markers", + action="store_true", + help="markers not registered in the `markers` section of the configuration file raise errors.", + ) + group._addoption( + "--strict", action="store_true", help="(deprecated) alias to --strict-markers.", + ) + group._addoption( + "-c", + metavar="file", + type=str, + dest="inifilename", + help="load configuration from `file` instead of trying to locate one of the implicit " + "configuration files.", + ) + group._addoption( + "--continue-on-collection-errors", + action="store_true", + default=False, + dest="continue_on_collection_errors", + help="Force test execution even if collection errors occur.", + ) + group._addoption( + "--rootdir", + action="store", + dest="rootdir", + help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " + "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " + "'$HOME/root_dir'.", + ) + + group = parser.getgroup("collect", "collection") + group.addoption( + "--collectonly", + "--collect-only", + "--co", + action="store_true", + help="only collect tests, don't execute them.", + ) + group.addoption( + "--pyargs", + action="store_true", + help="try to interpret all arguments as python packages.", + ) + group.addoption( + "--ignore", + action="append", + metavar="path", + help="ignore path during collection (multi-allowed).", + ) + group.addoption( + "--ignore-glob", + action="append", + metavar="path", + help="ignore path pattern during collection (multi-allowed).", + ) + group.addoption( + "--deselect", + action="append", + metavar="nodeid_prefix", + help="deselect item (via node id prefix) during collection (multi-allowed).", + ) + group.addoption( + "--confcutdir", + dest="confcutdir", + default=None, + metavar="dir", + type=functools.partial(directory_arg, optname="--confcutdir"), + help="only load conftest.py's relative to specified dir.", + ) + group.addoption( + "--noconftest", + action="store_true", + dest="noconftest", + default=False, + help="Don't load any conftest.py files.", + ) + group.addoption( + "--keepduplicates", + "--keep-duplicates", + action="store_true", + dest="keepduplicates", + default=False, + help="Keep duplicate tests.", + ) + group.addoption( + "--collect-in-virtualenv", + action="store_true", + dest="collect_in_virtualenv", + default=False, + help="Don't ignore tests in a local virtualenv directory", + ) + group.addoption( + "--import-mode", + default="prepend", + choices=["prepend", "append", "importlib"], + dest="importmode", + help="prepend/append to sys.path when importing test modules and conftest files, " + "default is to prepend.", + ) + + group = parser.getgroup("debugconfig", "test session debugging and configuration") + group.addoption( + "--basetemp", + dest="basetemp", + default=None, + type=validate_basetemp, + metavar="dir", + help=( + "base temporary directory for this test run." + "(warning: this directory is removed if it exists)" + ), + ) + + +def validate_basetemp(path: str) -> str: + # GH 7119 + msg = "basetemp must not be empty, the current working directory or any parent directory of it" + + # empty path + if not path: + raise argparse.ArgumentTypeError(msg) + + def is_ancestor(base: Path, query: Path) -> bool: + """Return whether query is an ancestor of base.""" + if base == query: + return True + for parent in base.parents: + if parent == query: + return True + return False + + # check if path is an ancestor of cwd + if is_ancestor(Path.cwd(), Path(path).absolute()): + raise argparse.ArgumentTypeError(msg) + + # check symlinks for ancestors + if is_ancestor(Path.cwd().resolve(), Path(path).resolve()): + raise argparse.ArgumentTypeError(msg) + + return path + + +def wrap_session( + config: Config, doit: Callable[[Config, "Session"], Optional[Union[int, ExitCode]]] +) -> Union[int, ExitCode]: + """Skeleton command line program.""" + session = Session.from_config(config) + session.exitstatus = ExitCode.OK + initstate = 0 + try: + try: + config._do_configure() + initstate = 1 + config.hook.pytest_sessionstart(session=session) + initstate = 2 + session.exitstatus = doit(config, session) or 0 + except UsageError: + session.exitstatus = ExitCode.USAGE_ERROR + raise + except Failed: + session.exitstatus = ExitCode.TESTS_FAILED + except (KeyboardInterrupt, exit.Exception): + excinfo = _pytest._code.ExceptionInfo.from_current() + exitstatus: Union[int, ExitCode] = ExitCode.INTERRUPTED + if isinstance(excinfo.value, exit.Exception): + if excinfo.value.returncode is not None: + exitstatus = excinfo.value.returncode + if initstate < 2: + sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") + config.hook.pytest_keyboard_interrupt(excinfo=excinfo) + session.exitstatus = exitstatus + except BaseException: + session.exitstatus = ExitCode.INTERNAL_ERROR + excinfo = _pytest._code.ExceptionInfo.from_current() + try: + config.notify_exception(excinfo, config.option) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc)) + else: + if isinstance(excinfo.value, SystemExit): + sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + + finally: + # Explicitly break reference cycle. + excinfo = None # type: ignore + session.startdir.chdir() + if initstate >= 2: + try: + config.hook.pytest_sessionfinish( + session=session, exitstatus=session.exitstatus + ) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc)) + config._ensure_unconfigure() + return session.exitstatus + + +def pytest_cmdline_main(config: Config) -> Union[int, ExitCode]: + return wrap_session(config, _main) + + +def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]: + """Default command line protocol for initialization, session, + running tests and reporting.""" + config.hook.pytest_collection(session=session) + config.hook.pytest_runtestloop(session=session) + + if session.testsfailed: + return ExitCode.TESTS_FAILED + elif session.testscollected == 0: + return ExitCode.NO_TESTS_COLLECTED + return None + + +def pytest_collection(session: "Session") -> None: + session.perform_collect() + + +def pytest_runtestloop(session: "Session") -> bool: + if session.testsfailed and not session.config.option.continue_on_collection_errors: + raise session.Interrupted( + "%d error%s during collection" + % (session.testsfailed, "s" if session.testsfailed != 1 else "") + ) + + if session.config.option.collectonly: + return True + + for i, item in enumerate(session.items): + nextitem = session.items[i + 1] if i + 1 < len(session.items) else None + item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) + if session.shouldfail: + raise session.Failed(session.shouldfail) + if session.shouldstop: + raise session.Interrupted(session.shouldstop) + return True + + +def _in_venv(path: py.path.local) -> bool: + """Attempt to detect if ``path`` is the root of a Virtual Environment by + checking for the existence of the appropriate activate script.""" + bindir = path.join("Scripts" if sys.platform.startswith("win") else "bin") + if not bindir.isdir(): + return False + activates = ( + "activate", + "activate.csh", + "activate.fish", + "Activate", + "Activate.bat", + "Activate.ps1", + ) + return any([fname.basename in activates for fname in bindir.listdir()]) + + +def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: + ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath()) + ignore_paths = ignore_paths or [] + excludeopt = config.getoption("ignore") + if excludeopt: + ignore_paths.extend([py.path.local(x) for x in excludeopt]) + + if py.path.local(path) in ignore_paths: + return True + + ignore_globs = config._getconftest_pathlist( + "collect_ignore_glob", path=path.dirpath() + ) + ignore_globs = ignore_globs or [] + excludeglobopt = config.getoption("ignore_glob") + if excludeglobopt: + ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) + + if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): + return True + + allow_in_venv = config.getoption("collect_in_virtualenv") + if not allow_in_venv and _in_venv(path): + return True + return None + + +def pytest_collection_modifyitems(items: List[nodes.Item], config: Config) -> None: + deselect_prefixes = tuple(config.getoption("deselect") or []) + if not deselect_prefixes: + return + + remaining = [] + deselected = [] + for colitem in items: + if colitem.nodeid.startswith(deselect_prefixes): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +class FSHookProxy: + def __init__(self, pm: PytestPluginManager, remove_mods) -> None: + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name: str): + x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) + self.__dict__[name] = x + return x + + +class Interrupted(KeyboardInterrupt): + """Signals that the test run was interrupted.""" + + __module__ = "builtins" # For py3. + + +class Failed(Exception): + """Signals a stop as failed test run.""" + + +@attr.s +class _bestrelpath_cache(Dict[Path, str]): + path = attr.ib(type=Path) + + def __missing__(self, path: Path) -> str: + r = bestrelpath(self.path, path) + self[path] = r + return r + + +@final +class Session(nodes.FSCollector): + Interrupted = Interrupted + Failed = Failed + # Set on the session by runner.pytest_sessionstart. + _setupstate: SetupState + # Set on the session by fixtures.pytest_sessionstart. + _fixturemanager: FixtureManager + exitstatus: Union[int, ExitCode] + + def __init__(self, config: Config) -> None: + super().__init__( + config.rootdir, parent=None, config=config, session=self, nodeid="" + ) + self.testsfailed = 0 + self.testscollected = 0 + self.shouldstop: Union[bool, str] = False + self.shouldfail: Union[bool, str] = False + self.trace = config.trace.root.get("collection") + self.startdir = config.invocation_dir + self._initialpaths: FrozenSet[py.path.local] = frozenset() + + self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) + + self.config.pluginmanager.register(self, name="session") + + @classmethod + def from_config(cls, config: Config) -> "Session": + session: Session = cls._create(config) + return session + + def __repr__(self) -> str: + return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( + self.__class__.__name__, + self.name, + getattr(self, "exitstatus", ""), + self.testsfailed, + self.testscollected, + ) + + def _node_location_to_relpath(self, node_path: Path) -> str: + # bestrelpath is a quite slow function. + return self._bestrelpathcache[node_path] + + @hookimpl(tryfirst=True) + def pytest_collectstart(self) -> None: + if self.shouldfail: + raise self.Failed(self.shouldfail) + if self.shouldstop: + raise self.Interrupted(self.shouldstop) + + @hookimpl(tryfirst=True) + def pytest_runtest_logreport( + self, report: Union[TestReport, CollectReport] + ) -> None: + if report.failed and not hasattr(report, "wasxfail"): + self.testsfailed += 1 + maxfail = self.config.getvalue("maxfail") + if maxfail and self.testsfailed >= maxfail: + self.shouldfail = "stopping after %d failures" % (self.testsfailed) + + pytest_collectreport = pytest_runtest_logreport + + def isinitpath(self, path: py.path.local) -> bool: + return path in self._initialpaths + + def gethookproxy(self, fspath: py.path.local): + # Check if we have the common case of running + # hooks with all conftest.py files. + pm = self.config.pluginmanager + my_conftestmodules = pm._getconftestmodules( + fspath, self.config.getoption("importmode") + ) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + if remove_mods: + # One or more conftests are not in use at this fspath. + proxy = FSHookProxy(pm, remove_mods) + else: + # All plugins are active for this fspath. + proxy = self.config.hook + return proxy + + def _recurse(self, direntry: "os.DirEntry[str]") -> bool: + if direntry.name == "__pycache__": + return False + path = py.path.local(direntry.path) + ihook = self.gethookproxy(path.dirpath()) + if ihook.pytest_ignore_collect(path=path, config=self.config): + return False + norecursepatterns = self.config.getini("norecursedirs") + if any(path.check(fnmatch=pat) for pat in norecursepatterns): + return False + return True + + def _collectfile( + self, path: py.path.local, handle_dupes: bool = True + ) -> Sequence[nodes.Collector]: + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() + ) + ihook = self.gethookproxy(path) + if not self.isinitpath(path): + if ihook.pytest_ignore_collect(path=path, config=self.config): + return () + + if handle_dupes: + keepduplicates = self.config.getoption("keepduplicates") + if not keepduplicates: + duplicate_paths = self.config.pluginmanager._duplicatepaths + if path in duplicate_paths: + return () + else: + duplicate_paths.add(path) + + return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + + @overload + def perform_collect( + self, args: Optional[Sequence[str]] = ..., genitems: "Literal[True]" = ... + ) -> Sequence[nodes.Item]: + ... + + @overload + def perform_collect( + self, args: Optional[Sequence[str]] = ..., genitems: bool = ... + ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + ... + + def perform_collect( + self, args: Optional[Sequence[str]] = None, genitems: bool = True + ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + """Perform the collection phase for this session. + + This is called by the default + :func:`pytest_collection <_pytest.hookspec.pytest_collection>` hook + implementation; see the documentation of this hook for more details. + For testing purposes, it may also be called directly on a fresh + ``Session``. + + This function normally recursively expands any collectors collected + from the session to their items, and only items are returned. For + testing purposes, this may be suppressed by passing ``genitems=False``, + in which case the return value contains these collectors unexpanded, + and ``session.items`` is empty. + """ + if args is None: + args = self.config.args + + self.trace("perform_collect", self, args) + self.trace.root.indent += 1 + + self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] + self._initial_parts: List[Tuple[py.path.local, List[str]]] = [] + self.items: List[nodes.Item] = [] + + hook = self.config.hook + + items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items + try: + initialpaths: List[py.path.local] = [] + for arg in args: + fspath, parts = resolve_collection_argument( + self.config.invocation_params.dir, + arg, + as_pypath=self.config.option.pyargs, + ) + self._initial_parts.append((fspath, parts)) + initialpaths.append(fspath) + self._initialpaths = frozenset(initialpaths) + rep = collect_one_node(self) + self.ihook.pytest_collectreport(report=rep) + self.trace.root.indent -= 1 + if self._notfound: + errors = [] + for arg, cols in self._notfound: + line = f"(no name {arg!r} in any of {cols!r})" + errors.append(f"not found: {arg}\n{line}") + raise UsageError(*errors) + if not genitems: + items = rep.result + else: + if rep.passed: + for node in rep.result: + self.items.extend(self.genitems(node)) + + self.config.pluginmanager.check_pending() + hook.pytest_collection_modifyitems( + session=self, config=self.config, items=items + ) + finally: + hook.pytest_collection_finish(session=self) + + self.testscollected = len(items) + return items + + def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: + from _pytest.python import Package + + # Keep track of any collected nodes in here, so we don't duplicate fixtures. + node_cache1: Dict[py.path.local, Sequence[nodes.Collector]] = {} + node_cache2: Dict[ + Tuple[Type[nodes.Collector], py.path.local], nodes.Collector + ] = ({}) + + # Keep track of any collected collectors in matchnodes paths, so they + # are not collected more than once. + matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = ({}) + + # Dirnames of pkgs with dunder-init files. + pkg_roots: Dict[str, Package] = {} + + for argpath, names in self._initial_parts: + self.trace("processing argument", (argpath, names)) + self.trace.root.indent += 1 + + # Start with a Session root, and delve to argpath item (dir or file) + # and stack all Packages found on the way. + # No point in finding packages when collecting doctests. + if not self.config.getoption("doctestmodules", False): + pm = self.config.pluginmanager + for parent in reversed(argpath.parts()): + if pm._confcutdir and pm._confcutdir.relto(parent): + break + + if parent.isdir(): + pkginit = parent.join("__init__.py") + if pkginit.isfile() and pkginit not in node_cache1: + col = self._collectfile(pkginit, handle_dupes=False) + if col: + if isinstance(col[0], Package): + pkg_roots[str(parent)] = col[0] + node_cache1[col[0].fspath] = [col[0]] + + # If it's a directory argument, recurse and look for any Subpackages. + # Let the Package collector deal with subnodes, don't collect here. + if argpath.check(dir=1): + assert not names, "invalid arg {!r}".format((argpath, names)) + + seen_dirs: Set[py.path.local] = set() + for direntry in visit(str(argpath), self._recurse): + if not direntry.is_file(): + continue + + path = py.path.local(direntry.path) + dirpath = path.dirpath() + + if dirpath not in seen_dirs: + # Collect packages first. + seen_dirs.add(dirpath) + pkginit = dirpath.join("__init__.py") + if pkginit.exists(): + for x in self._collectfile(pkginit): + yield x + if isinstance(x, Package): + pkg_roots[str(dirpath)] = x + if str(dirpath) in pkg_roots: + # Do not collect packages here. + continue + + for x in self._collectfile(path): + key = (type(x), x.fspath) + if key in node_cache2: + yield node_cache2[key] + else: + node_cache2[key] = x + yield x + else: + assert argpath.check(file=1) + + if argpath in node_cache1: + col = node_cache1[argpath] + else: + collect_root = pkg_roots.get(argpath.dirname, self) + col = collect_root._collectfile(argpath, handle_dupes=False) + if col: + node_cache1[argpath] = col + + matching = [] + work: List[ + Tuple[Sequence[Union[nodes.Item, nodes.Collector]], Sequence[str]] + ] = [(col, names)] + while work: + self.trace("matchnodes", col, names) + self.trace.root.indent += 1 + + matchnodes, matchnames = work.pop() + for node in matchnodes: + if not matchnames: + matching.append(node) + continue + if not isinstance(node, nodes.Collector): + continue + key = (type(node), node.nodeid) + if key in matchnodes_cache: + rep = matchnodes_cache[key] + else: + rep = collect_one_node(node) + matchnodes_cache[key] = rep + if rep.passed: + submatchnodes = [] + for r in rep.result: + # TODO: Remove parametrized workaround once collection structure contains + # parametrization. + if ( + r.name == matchnames[0] + or r.name.split("[")[0] == matchnames[0] + ): + submatchnodes.append(r) + if submatchnodes: + work.append((submatchnodes, matchnames[1:])) + # XXX Accept IDs that don't have "()" for class instances. + elif len(rep.result) == 1 and rep.result[0].name == "()": + work.append((rep.result, matchnames)) + else: + # Report collection failures here to avoid failing to run some test + # specified in the command line because the module could not be + # imported (#134). + node.ihook.pytest_collectreport(report=rep) + + self.trace("matchnodes finished -> ", len(matching), "nodes") + self.trace.root.indent -= 1 + + if not matching: + report_arg = "::".join((str(argpath), *names)) + self._notfound.append((report_arg, col)) + continue + + # If __init__.py was the only file requested, then the matched + # node will be the corresponding Package (by default), and the + # first yielded item will be the __init__ Module itself, so + # just use that. If this special case isn't taken, then all the + # files in the package will be yielded. + if argpath.basename == "__init__.py" and isinstance( + matching[0], Package + ): + try: + yield next(iter(matching[0].collect())) + except StopIteration: + # The package collects nothing with only an __init__.py + # file in it, which gets ignored by the default + # "python_files" option. + pass + continue + + yield from matching + + self.trace.root.indent -= 1 + + def genitems( + self, node: Union[nodes.Item, nodes.Collector] + ) -> Iterator[nodes.Item]: + self.trace("genitems", node) + if isinstance(node, nodes.Item): + node.ihook.pytest_itemcollected(item=node) + yield node + else: + assert isinstance(node, nodes.Collector) + rep = collect_one_node(node) + if rep.passed: + for subnode in rep.result: + yield from self.genitems(subnode) + node.ihook.pytest_collectreport(report=rep) + + +def search_pypath(module_name: str) -> str: + """Search sys.path for the given a dotted module name, and return its file system path.""" + try: + spec = importlib.util.find_spec(module_name) + # AttributeError: looks like package module, but actually filename + # ImportError: module does not exist + # ValueError: not a module name + except (AttributeError, ImportError, ValueError): + return module_name + if spec is None or spec.origin is None or spec.origin == "namespace": + return module_name + elif spec.submodule_search_locations: + return os.path.dirname(spec.origin) + else: + return spec.origin + + +def resolve_collection_argument( + invocation_path: Path, arg: str, *, as_pypath: bool = False +) -> Tuple[py.path.local, List[str]]: + """Parse path arguments optionally containing selection parts and return (fspath, names). + + Command-line arguments can point to files and/or directories, and optionally contain + parts for specific tests selection, for example: + + "pkg/tests/test_foo.py::TestClass::test_foo" + + This function ensures the path exists, and returns a tuple: + + (py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) + + When as_pypath is True, expects that the command-line argument actually contains + module paths instead of file-system paths: + + "pkg.tests.test_foo::TestClass::test_foo" + + In which case we search sys.path for a matching module, and then return the *path* to the + found module. + + If the path doesn't exist, raise UsageError. + If the path is a directory and selection parts are present, raise UsageError. + """ + strpath, *parts = str(arg).split("::") + if as_pypath: + strpath = search_pypath(strpath) + fspath = invocation_path / strpath + fspath = absolutepath(fspath) + if not fspath.exists(): + msg = ( + "module or package not found: {arg} (missing __init__.py?)" + if as_pypath + else "file or directory not found: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + if parts and fspath.is_dir(): + msg = ( + "package argument cannot contain :: selection parts: {arg}" + if as_pypath + else "directory argument cannot contain :: selection parts: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + return py.path.local(str(fspath)), parts diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__init__.py new file mode 100644 index 0000000..329a11c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__init__.py @@ -0,0 +1,282 @@ +"""Generic mechanism for marking and selecting python functions.""" +import warnings +from typing import AbstractSet +from typing import Collection +from typing import List +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +import attr + +from .expression import Expression +from .expression import ParseError +from .structures import EMPTY_PARAMETERSET_OPTION +from .structures import get_empty_parameterset_mark +from .structures import Mark +from .structures import MARK_GEN +from .structures import MarkDecorator +from .structures import MarkGenerator +from .structures import ParameterSet +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.deprecated import MINUS_K_COLON +from _pytest.deprecated import MINUS_K_DASH +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from _pytest.nodes import Item + + +__all__ = [ + "MARK_GEN", + "Mark", + "MarkDecorator", + "MarkGenerator", + "ParameterSet", + "get_empty_parameterset_mark", +] + + +old_mark_config_key = StoreKey[Optional[Config]]() + + +def param( + *values: object, + marks: Union[MarkDecorator, Collection[Union[MarkDecorator, Mark]]] = (), + id: Optional[str] = None, +) -> ParameterSet: + """Specify a parameter in `pytest.mark.parametrize`_ calls or + :ref:`parametrized fixtures `. + + .. code-block:: python + + @pytest.mark.parametrize( + "test_input,expected", + [("3+5", 8), pytest.param("6*9", 42, marks=pytest.mark.xfail),], + ) + def test_eval(test_input, expected): + assert eval(test_input) == expected + + :param values: Variable args of the values of the parameter set, in order. + :keyword marks: A single mark or a list of marks to be applied to this parameter set. + :keyword str id: The id to attribute to this parameter set. + """ + return ParameterSet.param(*values, marks=marks, id=id) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( + "-k", + action="store", + dest="keyword", + default="", + metavar="EXPRESSION", + help="only run tests which match the given substring expression. " + "An expression is a python evaluatable expression " + "where all names are substring-matched against test names " + "and their parent classes. Example: -k 'test_method or test_" + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other', while -k 'not test_method' " + "matches those that don't contain 'test_method' in their names. " + "-k 'not test_method and not test_other' will eliminate the matches. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' set, " + "as well as functions which have names assigned directly to them. " + "The matching is case-insensitive.", + ) + + group._addoption( + "-m", + action="store", + dest="markexpr", + default="", + metavar="MARKEXPR", + help="only run tests matching given mark expression.\n" + "For example: -m 'mark1 and not mark2'.", + ) + + group.addoption( + "--markers", + action="store_true", + help="show markers (builtin, plugin and per-project ones).", + ) + + parser.addini("markers", "markers for test functions", "linelist") + parser.addini(EMPTY_PARAMETERSET_OPTION, "default marker for empty parametersets") + + +@hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + import _pytest.config + + if config.option.markers: + config._do_configure() + tw = _pytest.config.create_terminal_writer(config) + for line in config.getini("markers"): + parts = line.split(":", 1) + name = parts[0] + rest = parts[1] if len(parts) == 2 else "" + tw.write("@pytest.mark.%s:" % name, bold=True) + tw.line(rest) + tw.line() + config._ensure_unconfigure() + return 0 + + return None + + +@attr.s(slots=True) +class KeywordMatcher: + """A matcher for keywords. + + Given a list of names, matches any substring of one of these names. The + string inclusion check is case-insensitive. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + + Additionally, matches on names in the 'extra_keyword_matches' set of + any item, as well as names directly assigned to test functions. + """ + + _names = attr.ib(type=AbstractSet[str]) + + @classmethod + def from_item(cls, item: "Item") -> "KeywordMatcher": + mapped_names = set() + + # Add the names of the current item and any parent items. + import pytest + + for node in item.listchain(): + if not isinstance(node, (pytest.Instance, pytest.Session)): + mapped_names.add(node.name) + + # Add the names added as extra keywords to current or parent items. + mapped_names.update(item.listextrakeywords()) + + # Add the names attached to the current function through direct assignment. + function_obj = getattr(item, "function", None) + if function_obj: + mapped_names.update(function_obj.__dict__) + + # Add the markers to the keywords as we no longer handle them correctly. + mapped_names.update(mark.name for mark in item.iter_markers()) + + return cls(mapped_names) + + def __call__(self, subname: str) -> bool: + subname = subname.lower() + names = (name.lower() for name in self._names) + + for name in names: + if subname in name: + return True + return False + + +def deselect_by_keyword(items: "List[Item]", config: Config) -> None: + keywordexpr = config.option.keyword.lstrip() + if not keywordexpr: + return + + if keywordexpr.startswith("-"): + # To be removed in pytest 7.0.0. + warnings.warn(MINUS_K_DASH, stacklevel=2) + keywordexpr = "not " + keywordexpr[1:] + selectuntil = False + if keywordexpr[-1:] == ":": + # To be removed in pytest 7.0.0. + warnings.warn(MINUS_K_COLON, stacklevel=2) + selectuntil = True + keywordexpr = keywordexpr[:-1] + + try: + expression = Expression.compile(keywordexpr) + except ParseError as e: + raise UsageError( + f"Wrong expression passed to '-k': {keywordexpr}: {e}" + ) from None + + remaining = [] + deselected = [] + for colitem in items: + if keywordexpr and not expression.evaluate(KeywordMatcher.from_item(colitem)): + deselected.append(colitem) + else: + if selectuntil: + keywordexpr = None + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +@attr.s(slots=True) +class MarkMatcher: + """A matcher for markers which are present. + + Tries to match on any marker names, attached to the given colitem. + """ + + own_mark_names = attr.ib() + + @classmethod + def from_item(cls, item) -> "MarkMatcher": + mark_names = {mark.name for mark in item.iter_markers()} + return cls(mark_names) + + def __call__(self, name: str) -> bool: + return name in self.own_mark_names + + +def deselect_by_mark(items: "List[Item]", config: Config) -> None: + matchexpr = config.option.markexpr + if not matchexpr: + return + + try: + expression = Expression.compile(matchexpr) + except ParseError as e: + raise UsageError(f"Wrong expression passed to '-m': {matchexpr}: {e}") from None + + remaining = [] + deselected = [] + for item in items: + if expression.evaluate(MarkMatcher.from_item(item)): + remaining.append(item) + else: + deselected.append(item) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +def pytest_collection_modifyitems(items: "List[Item]", config: Config) -> None: + deselect_by_keyword(items, config) + deselect_by_mark(items, config) + + +def pytest_configure(config: Config) -> None: + config._store[old_mark_config_key] = MARK_GEN._config + MARK_GEN._config = config + + empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) + + if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): + raise UsageError( + "{!s} must be one of skip, xfail or fail_at_collect" + " but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset) + ) + + +def pytest_unconfigure(config: Config) -> None: + MARK_GEN._config = config._store.get(old_mark_config_key, None) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07b2b64e7ce7ee36499721c86a4712afef8d38d GIT binary patch literal 8731 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 TWqtJG*jbcE$4-ppM~D9p$8!ie literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ec0ef44e7f27cacd1dfbf0bc29b0589aa0a2d16 GIT binary patch literal 7377 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 Q!C$tNg9r9v7TVVT22n}vS^xk5 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/expression.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/expression.py new file mode 100644 index 0000000..dc3991b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/expression.py @@ -0,0 +1,221 @@ +r"""Evaluate match expressions, as used by `-k` and `-m`. + +The grammar is: + +expression: expr? EOF +expr: and_expr ('or' and_expr)* +and_expr: not_expr ('and' not_expr)* +not_expr: 'not' not_expr | '(' expr ')' | ident +ident: (\w|:|\+|-|\.|\[|\])+ + +The semantics are: + +- Empty expression evaluates to False. +- ident evaluates to True of False according to a provided matcher function. +- or/and/not evaluate according to the usual boolean semantics. +""" +import ast +import enum +import re +import types +from typing import Callable +from typing import Iterator +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import TYPE_CHECKING + +import attr + +if TYPE_CHECKING: + from typing import NoReturn + + +__all__ = [ + "Expression", + "ParseError", +] + + +class TokenType(enum.Enum): + LPAREN = "left parenthesis" + RPAREN = "right parenthesis" + OR = "or" + AND = "and" + NOT = "not" + IDENT = "identifier" + EOF = "end of input" + + +@attr.s(frozen=True, slots=True) +class Token: + type = attr.ib(type=TokenType) + value = attr.ib(type=str) + pos = attr.ib(type=int) + + +class ParseError(Exception): + """The expression contains invalid syntax. + + :param column: The column in the line where the error occurred (1-based). + :param message: A description of the error. + """ + + def __init__(self, column: int, message: str) -> None: + self.column = column + self.message = message + + def __str__(self) -> str: + return f"at column {self.column}: {self.message}" + + +class Scanner: + __slots__ = ("tokens", "current") + + def __init__(self, input: str) -> None: + self.tokens = self.lex(input) + self.current = next(self.tokens) + + def lex(self, input: str) -> Iterator[Token]: + pos = 0 + while pos < len(input): + if input[pos] in (" ", "\t"): + pos += 1 + elif input[pos] == "(": + yield Token(TokenType.LPAREN, "(", pos) + pos += 1 + elif input[pos] == ")": + yield Token(TokenType.RPAREN, ")", pos) + pos += 1 + else: + match = re.match(r"(:?\w|:|\+|-|\.|\[|\])+", input[pos:]) + if match: + value = match.group(0) + if value == "or": + yield Token(TokenType.OR, value, pos) + elif value == "and": + yield Token(TokenType.AND, value, pos) + elif value == "not": + yield Token(TokenType.NOT, value, pos) + else: + yield Token(TokenType.IDENT, value, pos) + pos += len(value) + else: + raise ParseError( + pos + 1, 'unexpected character "{}"'.format(input[pos]), + ) + yield Token(TokenType.EOF, "", pos) + + def accept(self, type: TokenType, *, reject: bool = False) -> Optional[Token]: + if self.current.type is type: + token = self.current + if token.type is not TokenType.EOF: + self.current = next(self.tokens) + return token + if reject: + self.reject((type,)) + return None + + def reject(self, expected: Sequence[TokenType]) -> "NoReturn": + raise ParseError( + self.current.pos + 1, + "expected {}; got {}".format( + " OR ".join(type.value for type in expected), self.current.type.value, + ), + ) + + +# True, False and None are legal match expression identifiers, +# but illegal as Python identifiers. To fix this, this prefix +# is added to identifiers in the conversion to Python AST. +IDENT_PREFIX = "$" + + +def expression(s: Scanner) -> ast.Expression: + if s.accept(TokenType.EOF): + ret: ast.expr = ast.NameConstant(False) + else: + ret = expr(s) + s.accept(TokenType.EOF, reject=True) + return ast.fix_missing_locations(ast.Expression(ret)) + + +def expr(s: Scanner) -> ast.expr: + ret = and_expr(s) + while s.accept(TokenType.OR): + rhs = and_expr(s) + ret = ast.BoolOp(ast.Or(), [ret, rhs]) + return ret + + +def and_expr(s: Scanner) -> ast.expr: + ret = not_expr(s) + while s.accept(TokenType.AND): + rhs = not_expr(s) + ret = ast.BoolOp(ast.And(), [ret, rhs]) + return ret + + +def not_expr(s: Scanner) -> ast.expr: + if s.accept(TokenType.NOT): + return ast.UnaryOp(ast.Not(), not_expr(s)) + if s.accept(TokenType.LPAREN): + ret = expr(s) + s.accept(TokenType.RPAREN, reject=True) + return ret + ident = s.accept(TokenType.IDENT) + if ident: + return ast.Name(IDENT_PREFIX + ident.value, ast.Load()) + s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT)) + + +class MatcherAdapter(Mapping[str, bool]): + """Adapts a matcher function to a locals mapping as required by eval().""" + + def __init__(self, matcher: Callable[[str], bool]) -> None: + self.matcher = matcher + + def __getitem__(self, key: str) -> bool: + return self.matcher(key[len(IDENT_PREFIX) :]) + + def __iter__(self) -> Iterator[str]: + raise NotImplementedError() + + def __len__(self) -> int: + raise NotImplementedError() + + +class Expression: + """A compiled match expression as used by -k and -m. + + The expression can be evaulated against different matchers. + """ + + __slots__ = ("code",) + + def __init__(self, code: types.CodeType) -> None: + self.code = code + + @classmethod + def compile(self, input: str) -> "Expression": + """Compile a match expression. + + :param input: The input expression - one line. + """ + astexpr = expression(Scanner(input)) + code: types.CodeType = compile( + astexpr, filename="", mode="eval", + ) + return Expression(code) + + def evaluate(self, matcher: Callable[[str], bool]) -> bool: + """Evaluate the match expression. + + :param matcher: + Given an identifier, should return whether it matches or not. + Should be prepared to handle arbitrary strings as input. + + :returns: Whether the expression matches or not. + """ + ret: bool = eval(self.code, {"__builtins__": {}}, MatcherAdapter(matcher)) + return ret diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/structures.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/structures.py new file mode 100644 index 0000000..6c126cf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/mark/structures.py @@ -0,0 +1,559 @@ +import collections.abc +import inspect +import warnings +from typing import Any +from typing import Callable +from typing import Collection +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import MutableMapping +from typing import NamedTuple +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +from .._code import getfslineno +from ..compat import ascii_escaped +from ..compat import final +from ..compat import NOTSET +from ..compat import NotSetType +from _pytest.config import Config +from _pytest.outcomes import fail +from _pytest.warning_types import PytestUnknownMarkWarning + +if TYPE_CHECKING: + from ..nodes import Node + + +EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" + + +def istestfunc(func) -> bool: + return ( + hasattr(func, "__call__") + and getattr(func, "__name__", "") != "" + ) + + +def get_empty_parameterset_mark( + config: Config, argnames: Sequence[str], func +) -> "MarkDecorator": + from ..nodes import Collector + + fs, lineno = getfslineno(func) + reason = "got empty parameter set %r, function %s at %s:%d" % ( + argnames, + func.__name__, + fs, + lineno, + ) + + requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) + if requested_mark in ("", None, "skip"): + mark = MARK_GEN.skip(reason=reason) + elif requested_mark == "xfail": + mark = MARK_GEN.xfail(reason=reason, run=False) + elif requested_mark == "fail_at_collect": + f_name = func.__name__ + _, lineno = getfslineno(func) + raise Collector.CollectError( + "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) + ) + else: + raise LookupError(requested_mark) + return mark + + +class ParameterSet( + NamedTuple( + "ParameterSet", + [ + ("values", Sequence[Union[object, NotSetType]]), + ("marks", Collection[Union["MarkDecorator", "Mark"]]), + ("id", Optional[str]), + ], + ) +): + @classmethod + def param( + cls, + *values: object, + marks: Union["MarkDecorator", Collection[Union["MarkDecorator", "Mark"]]] = (), + id: Optional[str] = None, + ) -> "ParameterSet": + if isinstance(marks, MarkDecorator): + marks = (marks,) + else: + assert isinstance(marks, collections.abc.Collection) + + if id is not None: + if not isinstance(id, str): + raise TypeError( + "Expected id to be a string, got {}: {!r}".format(type(id), id) + ) + id = ascii_escaped(id) + return cls(values, marks, id) + + @classmethod + def extract_from( + cls, + parameterset: Union["ParameterSet", Sequence[object], object], + force_tuple: bool = False, + ) -> "ParameterSet": + """Extract from an object or objects. + + :param parameterset: + A legacy style parameterset that may or may not be a tuple, + and may or may not be wrapped into a mess of mark objects. + + :param force_tuple: + Enforce tuple wrapping so single argument tuple values + don't get decomposed and break tests. + """ + + if isinstance(parameterset, cls): + return parameterset + if force_tuple: + return cls.param(parameterset) + else: + # TODO: Refactor to fix this type-ignore. Currently the following + # passes type-checking but crashes: + # + # @pytest.mark.parametrize(('x', 'y'), [1, 2]) + # def test_foo(x, y): pass + return cls(parameterset, marks=[], id=None) # type: ignore[arg-type] + + @staticmethod + def _parse_parametrize_args( + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + *args, + **kwargs, + ) -> Tuple[Union[List[str], Tuple[str, ...]], bool]: + if not isinstance(argnames, (tuple, list)): + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + force_tuple = len(argnames) == 1 + else: + force_tuple = False + return argnames, force_tuple + + @staticmethod + def _parse_parametrize_parameters( + argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + force_tuple: bool, + ) -> List["ParameterSet"]: + return [ + ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues + ] + + @classmethod + def _for_parametrize( + cls, + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + func, + config: Config, + nodeid: str, + ) -> Tuple[Union[List[str], Tuple[str, ...]], List["ParameterSet"]]: + argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) + parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) + del argvalues + + if parameters: + # Check all parameter sets have the correct number of values. + for param in parameters: + if len(param.values) != len(argnames): + msg = ( + '{nodeid}: in "parametrize" the number of names ({names_len}):\n' + " {names}\n" + "must be equal to the number of values ({values_len}):\n" + " {values}" + ) + fail( + msg.format( + nodeid=nodeid, + values=param.values, + names=argnames, + names_len=len(argnames), + values_len=len(param.values), + ), + pytrace=False, + ) + else: + # Empty parameter set (likely computed at runtime): create a single + # parameter set with NOTSET values, with the "empty parameter set" mark applied to it. + mark = get_empty_parameterset_mark(config, argnames, func) + parameters.append( + ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None) + ) + return argnames, parameters + + +@final +@attr.s(frozen=True) +class Mark: + #: Name of the mark. + name = attr.ib(type=str) + #: Positional arguments of the mark decorator. + args = attr.ib(type=Tuple[Any, ...]) + #: Keyword arguments of the mark decorator. + kwargs = attr.ib(type=Mapping[str, Any]) + + #: Source Mark for ids with parametrize Marks. + _param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False) + #: Resolved/generated ids with parametrize Marks. + _param_ids_generated = attr.ib( + type=Optional[Sequence[str]], default=None, repr=False + ) + + def _has_param_ids(self) -> bool: + return "ids" in self.kwargs or len(self.args) >= 4 + + def combined_with(self, other: "Mark") -> "Mark": + """Return a new Mark which is a combination of this + Mark and another Mark. + + Combines by appending args and merging kwargs. + + :param Mark other: The mark to combine with. + :rtype: Mark + """ + assert self.name == other.name + + # Remember source of ids with parametrize Marks. + param_ids_from: Optional[Mark] = None + if self.name == "parametrize": + if other._has_param_ids(): + param_ids_from = other + elif self._has_param_ids(): + param_ids_from = self + + return Mark( + self.name, + self.args + other.args, + dict(self.kwargs, **other.kwargs), + param_ids_from=param_ids_from, + ) + + +# A generic parameter designating an object to which a Mark may +# be applied -- a test function (callable) or class. +# Note: a lambda is not allowed, but this can't be represented. +_Markable = TypeVar("_Markable", bound=Union[Callable[..., object], type]) + + +@attr.s +class MarkDecorator: + """A decorator for applying a mark on test functions and classes. + + MarkDecorators are created with ``pytest.mark``:: + + mark1 = pytest.mark.NAME # Simple MarkDecorator + mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator + + and can then be applied as decorators to test functions:: + + @mark2 + def test_function(): + pass + + When a MarkDecorator is called it does the following: + + 1. If called with a single class as its only positional argument and no + additional keyword arguments, it attaches the mark to the class so it + gets applied automatically to all test cases found in that class. + + 2. If called with a single function as its only positional argument and + no additional keyword arguments, it attaches the mark to the function, + containing all the arguments already stored internally in the + MarkDecorator. + + 3. When called in any other case, it returns a new MarkDecorator instance + with the original MarkDecorator's content updated with the arguments + passed to this call. + + Note: The rules above prevent MarkDecorators from storing only a single + function or class reference as their positional argument with no + additional keyword or positional arguments. You can work around this by + using `with_args()`. + """ + + mark = attr.ib(type=Mark, validator=attr.validators.instance_of(Mark)) + + @property + def name(self) -> str: + """Alias for mark.name.""" + return self.mark.name + + @property + def args(self) -> Tuple[Any, ...]: + """Alias for mark.args.""" + return self.mark.args + + @property + def kwargs(self) -> Mapping[str, Any]: + """Alias for mark.kwargs.""" + return self.mark.kwargs + + @property + def markname(self) -> str: + return self.name # for backward-compat (2.4.1 had this attr) + + def __repr__(self) -> str: + return f"" + + def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": + """Return a MarkDecorator with extra arguments added. + + Unlike calling the MarkDecorator, with_args() can be used even + if the sole argument is a callable/class. + + :rtype: MarkDecorator + """ + mark = Mark(self.name, args, kwargs) + return self.__class__(self.mark.combined_with(mark)) + + # Type ignored because the overloads overlap with an incompatible + # return type. Not much we can do about that. Thankfully mypy picks + # the first match so it works out even if we break the rules. + @overload + def __call__(self, arg: _Markable) -> _Markable: # type: ignore[misc] + pass + + @overload + def __call__(self, *args: object, **kwargs: object) -> "MarkDecorator": + pass + + def __call__(self, *args: object, **kwargs: object): + """Call the MarkDecorator.""" + if args and not kwargs: + func = args[0] + is_class = inspect.isclass(func) + if len(args) == 1 and (istestfunc(func) or is_class): + store_mark(func, self.mark) + return func + return self.with_args(*args, **kwargs) + + +def get_unpacked_marks(obj) -> List[Mark]: + """Obtain the unpacked marks that are stored on an object.""" + mark_list = getattr(obj, "pytestmark", []) + if not isinstance(mark_list, list): + mark_list = [mark_list] + return normalize_mark_list(mark_list) + + +def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]: + """Normalize marker decorating helpers to mark objects. + + :type List[Union[Mark, Markdecorator]] mark_list: + :rtype: List[Mark] + """ + extracted = [ + getattr(mark, "mark", mark) for mark in mark_list + ] # unpack MarkDecorator + for mark in extracted: + if not isinstance(mark, Mark): + raise TypeError(f"got {mark!r} instead of Mark") + return [x for x in extracted if isinstance(x, Mark)] + + +def store_mark(obj, mark: Mark) -> None: + """Store a Mark on an object. + + This is used to implement the Mark declarations/decorators correctly. + """ + assert isinstance(mark, Mark), mark + # Always reassign name to avoid updating pytestmark in a reference that + # was only borrowed. + obj.pytestmark = get_unpacked_marks(obj) + [mark] + + +# Typing for builtin pytest marks. This is cheating; it gives builtin marks +# special privilege, and breaks modularity. But practicality beats purity... +if TYPE_CHECKING: + from _pytest.fixtures import _Scope + + class _SkipMarkDecorator(MarkDecorator): + @overload # type: ignore[override,misc] + def __call__(self, arg: _Markable) -> _Markable: + ... + + @overload + def __call__(self, reason: str = ...) -> "MarkDecorator": + ... + + class _SkipifMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + condition: Union[str, bool] = ..., + *conditions: Union[str, bool], + reason: str = ..., + ) -> MarkDecorator: + ... + + class _XfailMarkDecorator(MarkDecorator): + @overload # type: ignore[override,misc] + def __call__(self, arg: _Markable) -> _Markable: + ... + + @overload + def __call__( + self, + condition: Union[str, bool] = ..., + *conditions: Union[str, bool], + reason: str = ..., + run: bool = ..., + raises: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = ..., + strict: bool = ..., + ) -> MarkDecorator: + ... + + class _ParametrizeMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], + *, + indirect: Union[bool, Sequence[str]] = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + scope: Optional[_Scope] = ..., + ) -> MarkDecorator: + ... + + class _UsefixturesMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, *fixtures: str + ) -> MarkDecorator: + ... + + class _FilterwarningsMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, *filters: str + ) -> MarkDecorator: + ... + + +@final +class MarkGenerator: + """Factory for :class:`MarkDecorator` objects - exposed as + a ``pytest.mark`` singleton instance. + + Example:: + + import pytest + + @pytest.mark.slowtest + def test_function(): + pass + + applies a 'slowtest' :class:`Mark` on ``test_function``. + """ + + _config: Optional[Config] = None + _markers: Set[str] = set() + + # See TYPE_CHECKING above. + if TYPE_CHECKING: + skip: _SkipMarkDecorator + skipif: _SkipifMarkDecorator + xfail: _XfailMarkDecorator + parametrize: _ParametrizeMarkDecorator + usefixtures: _UsefixturesMarkDecorator + filterwarnings: _FilterwarningsMarkDecorator + + def __getattr__(self, name: str) -> MarkDecorator: + if name[0] == "_": + raise AttributeError("Marker name must NOT start with underscore") + + if self._config is not None: + # We store a set of markers as a performance optimisation - if a mark + # name is in the set we definitely know it, but a mark may be known and + # not in the set. We therefore start by updating the set! + if name not in self._markers: + for line in self._config.getini("markers"): + # example lines: "skipif(condition): skip the given test if..." + # or "hypothesis: tests which use Hypothesis", so to get the + # marker name we split on both `:` and `(`. + marker = line.split(":")[0].split("(")[0].strip() + self._markers.add(marker) + + # If the name is not in the set of known marks after updating, + # then it really is time to issue a warning or an error. + if name not in self._markers: + if self._config.option.strict_markers or self._config.option.strict: + fail( + f"{name!r} not found in `markers` configuration option", + pytrace=False, + ) + + # Raise a specific error for common misspellings of "parametrize". + if name in ["parameterize", "parametrise", "parameterise"]: + __tracebackhide__ = True + fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") + + warnings.warn( + "Unknown pytest.mark.%s - is this a typo? You can register " + "custom marks to avoid this warning - for details, see " + "https://docs.pytest.org/en/stable/mark.html" % name, + PytestUnknownMarkWarning, + 2, + ) + + return MarkDecorator(Mark(name, (), {})) + + +MARK_GEN = MarkGenerator() + + +@final +class NodeKeywords(MutableMapping[str, Any]): + def __init__(self, node: "Node") -> None: + self.node = node + self.parent = node.parent + self._markers = {node.name: True} + + def __getitem__(self, key: str) -> Any: + try: + return self._markers[key] + except KeyError: + if self.parent is None: + raise + return self.parent.keywords[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._markers[key] = value + + def __delitem__(self, key: str) -> None: + raise ValueError("cannot delete key in keywords dict") + + def __iter__(self) -> Iterator[str]: + seen = self._seen() + return iter(seen) + + def _seen(self) -> Set[str]: + seen = set(self._markers) + if self.parent is not None: + seen.update(self.parent.keywords) + return seen + + def __len__(self) -> int: + return len(self._seen()) + + def __repr__(self) -> str: + return f"" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/monkeypatch.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/monkeypatch.py new file mode 100644 index 0000000..a052f69 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/monkeypatch.py @@ -0,0 +1,379 @@ +"""Monkeypatching and mocking functionality.""" +import os +import re +import sys +import warnings +from contextlib import contextmanager +from pathlib import Path +from typing import Any +from typing import Generator +from typing import List +from typing import MutableMapping +from typing import Optional +from typing import overload +from typing import Tuple +from typing import TypeVar +from typing import Union + +from _pytest.compat import final +from _pytest.fixtures import fixture +from _pytest.warning_types import PytestWarning + +RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") + + +K = TypeVar("K") +V = TypeVar("V") + + +@fixture +def monkeypatch() -> Generator["MonkeyPatch", None, None]: + """A convenient fixture for monkey-patching. + + The fixture provides these methods to modify objects, dictionaries or + os.environ:: + + monkeypatch.setattr(obj, name, value, raising=True) + monkeypatch.delattr(obj, name, raising=True) + monkeypatch.setitem(mapping, name, value) + monkeypatch.delitem(obj, name, raising=True) + monkeypatch.setenv(name, value, prepend=False) + monkeypatch.delenv(name, raising=True) + monkeypatch.syspath_prepend(path) + monkeypatch.chdir(path) + + All modifications will be undone after the requesting test function or + fixture has finished. The ``raising`` parameter determines if a KeyError + or AttributeError will be raised if the set/deletion operation has no target. + """ + mpatch = MonkeyPatch() + yield mpatch + mpatch.undo() + + +def resolve(name: str) -> object: + # Simplified from zope.dottedname. + parts = name.split(".") + + used = parts.pop(0) + found = __import__(used) + for part in parts: + used += "." + part + try: + found = getattr(found, part) + except AttributeError: + pass + else: + continue + # We use explicit un-nesting of the handling block in order + # to avoid nested exceptions. + try: + __import__(used) + except ImportError as ex: + expected = str(ex).split()[-1] + if expected == used: + raise + else: + raise ImportError(f"import error in {used}: {ex}") from ex + found = annotated_getattr(found, part, used) + return found + + +def annotated_getattr(obj: object, name: str, ann: str) -> object: + try: + obj = getattr(obj, name) + except AttributeError as e: + raise AttributeError( + "{!r} object at {} has no attribute {!r}".format( + type(obj).__name__, ann, name + ) + ) from e + return obj + + +def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]: + if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable] + raise TypeError(f"must be absolute import path string, not {import_path!r}") + module, attr = import_path.rsplit(".", 1) + target = resolve(module) + if raising: + annotated_getattr(target, attr, ann=module) + return attr, target + + +class Notset: + def __repr__(self) -> str: + return "" + + +notset = Notset() + + +@final +class MonkeyPatch: + """Helper to conveniently monkeypatch attributes/items/environment + variables/syspath. + + Returned by the :fixture:`monkeypatch` fixture. + + :versionchanged:: 6.2 + Can now also be used directly as `pytest.MonkeyPatch()`, for when + the fixture is not available. In this case, use + :meth:`with MonkeyPatch.context() as mp: ` or remember to call + :meth:`undo` explicitly. + """ + + def __init__(self) -> None: + self._setattr: List[Tuple[object, str, object]] = [] + self._setitem: List[Tuple[MutableMapping[Any, Any], object, object]] = ([]) + self._cwd: Optional[str] = None + self._savesyspath: Optional[List[str]] = None + + @classmethod + @contextmanager + def context(cls) -> Generator["MonkeyPatch", None, None]: + """Context manager that returns a new :class:`MonkeyPatch` object + which undoes any patching done inside the ``with`` block upon exit. + + Example: + + .. code-block:: python + + import functools + + + def test_partial(monkeypatch): + with monkeypatch.context() as m: + m.setattr(functools, "partial", 3) + + Useful in situations where it is desired to undo some patches before the test ends, + such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples + of this see `#3290 `_. + """ + m = cls() + try: + yield m + finally: + m.undo() + + @overload + def setattr( + self, target: str, name: object, value: Notset = ..., raising: bool = ..., + ) -> None: + ... + + @overload + def setattr( + self, target: object, name: str, value: object, raising: bool = ..., + ) -> None: + ... + + def setattr( + self, + target: Union[str, object], + name: Union[object, str], + value: object = notset, + raising: bool = True, + ) -> None: + """Set attribute value on target, memorizing the old value. + + For convenience you can specify a string as ``target`` which + will be interpreted as a dotted import path, with the last part + being the attribute name. For example, + ``monkeypatch.setattr("os.getcwd", lambda: "/")`` + would set the ``getcwd`` function of the ``os`` module. + + Raises AttributeError if the attribute does not exist, unless + ``raising`` is set to False. + """ + __tracebackhide__ = True + import inspect + + if isinstance(value, Notset): + if not isinstance(target, str): + raise TypeError( + "use setattr(target, name, value) or " + "setattr(target, value) with target being a dotted " + "import string" + ) + value = name + name, target = derive_importpath(target, raising) + else: + if not isinstance(name, str): + raise TypeError( + "use setattr(target, name, value) with name being a string or " + "setattr(target, value) with target being a dotted " + "import string" + ) + + oldval = getattr(target, name, notset) + if raising and oldval is notset: + raise AttributeError(f"{target!r} has no attribute {name!r}") + + # avoid class descriptors like staticmethod/classmethod + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + setattr(target, name, value) + + def delattr( + self, + target: Union[object, str], + name: Union[str, Notset] = notset, + raising: bool = True, + ) -> None: + """Delete attribute ``name`` from ``target``. + + If no ``name`` is specified and ``target`` is a string + it will be interpreted as a dotted import path with the + last part being the attribute name. + + Raises AttributeError it the attribute does not exist, unless + ``raising`` is set to False. + """ + __tracebackhide__ = True + import inspect + + if isinstance(name, Notset): + if not isinstance(target, str): + raise TypeError( + "use delattr(target, name) or " + "delattr(target) with target being a dotted " + "import string" + ) + name, target = derive_importpath(target, raising) + + if not hasattr(target, name): + if raising: + raise AttributeError(name) + else: + oldval = getattr(target, name, notset) + # Avoid class descriptors like staticmethod/classmethod. + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + delattr(target, name) + + def setitem(self, dic: MutableMapping[K, V], name: K, value: V) -> None: + """Set dictionary entry ``name`` to value.""" + self._setitem.append((dic, name, dic.get(name, notset))) + dic[name] = value + + def delitem(self, dic: MutableMapping[K, V], name: K, raising: bool = True) -> None: + """Delete ``name`` from dict. + + Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to + False. + """ + if name not in dic: + if raising: + raise KeyError(name) + else: + self._setitem.append((dic, name, dic.get(name, notset))) + del dic[name] + + def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None: + """Set environment variable ``name`` to ``value``. + + If ``prepend`` is a character, read the current environment variable + value and prepend the ``value`` adjoined with the ``prepend`` + character. + """ + if not isinstance(value, str): + warnings.warn( # type: ignore[unreachable] + PytestWarning( + "Value of environment variable {name} type should be str, but got " + "{value!r} (type: {type}); converted to str implicitly".format( + name=name, value=value, type=type(value).__name__ + ) + ), + stacklevel=2, + ) + value = str(value) + if prepend and name in os.environ: + value = value + prepend + os.environ[name] + self.setitem(os.environ, name, value) + + def delenv(self, name: str, raising: bool = True) -> None: + """Delete ``name`` from the environment. + + Raises ``KeyError`` if it does not exist, unless ``raising`` is set to + False. + """ + environ: MutableMapping[str, str] = os.environ + self.delitem(environ, name, raising=raising) + + def syspath_prepend(self, path) -> None: + """Prepend ``path`` to ``sys.path`` list of import locations.""" + from pkg_resources import fixup_namespace_packages + + if self._savesyspath is None: + self._savesyspath = sys.path[:] + sys.path.insert(0, str(path)) + + # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 + fixup_namespace_packages(str(path)) + + # A call to syspathinsert() usually means that the caller wants to + # import some dynamically created files, thus with python3 we + # invalidate its import caches. + # This is especially important when any namespace package is in use, + # since then the mtime based FileFinder cache (that gets created in + # this case already) gets not invalidated when writing the new files + # quickly afterwards. + from importlib import invalidate_caches + + invalidate_caches() + + def chdir(self, path) -> None: + """Change the current working directory to the specified path. + + Path can be a string or a py.path.local object. + """ + if self._cwd is None: + self._cwd = os.getcwd() + if hasattr(path, "chdir"): + path.chdir() + elif isinstance(path, Path): + # Modern python uses the fspath protocol here LEGACY + os.chdir(str(path)) + else: + os.chdir(path) + + def undo(self) -> None: + """Undo previous changes. + + This call consumes the undo stack. Calling it a second time has no + effect unless you do more monkeypatching after the undo call. + + There is generally no need to call `undo()`, since it is + called automatically during tear-down. + + Note that the same `monkeypatch` fixture is used across a + single test function invocation. If `monkeypatch` is used both by + the test function itself and one of the test fixtures, + calling `undo()` will undo all of the changes made in + both functions. + """ + for obj, name, value in reversed(self._setattr): + if value is not notset: + setattr(obj, name, value) + else: + delattr(obj, name) + self._setattr[:] = [] + for dictionary, key, value in reversed(self._setitem): + if value is notset: + try: + del dictionary[key] + except KeyError: + pass # Was already deleted, so we have the desired state. + else: + dictionary[key] = value + self._setitem[:] = [] + if self._savesyspath is not None: + sys.path[:] = self._savesyspath + self._savesyspath = None + + if self._cwd is not None: + os.chdir(self._cwd) + self._cwd = None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nodes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nodes.py new file mode 100644 index 0000000..27434fb --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nodes.py @@ -0,0 +1,591 @@ +import os +import warnings +from pathlib import Path +from typing import Callable +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import py + +import _pytest._code +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest.compat import cached_property +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import NodeKeywords +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.store import Store + +if TYPE_CHECKING: + # Imported here due to circular import. + from _pytest.main import Session + from _pytest._code.code import _TracebackStyle + + +SEP = "/" + +tracebackcutdir = py.path.local(_pytest.__file__).dirpath() + + +def iterparentnodeids(nodeid: str) -> Iterator[str]: + """Return the parent node IDs of a given node ID, inclusive. + + For the node ID + + "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" + + the result would be + + "" + "testing" + "testing/code" + "testing/code/test_excinfo.py" + "testing/code/test_excinfo.py::TestFormattedExcinfo" + "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" + + Note that :: parts are only considered at the last / component. + """ + pos = 0 + sep = SEP + yield "" + while True: + at = nodeid.find(sep, pos) + if at == -1 and sep == SEP: + sep = "::" + elif at == -1: + if nodeid: + yield nodeid + break + else: + if at: + yield nodeid[:at] + pos = at + len(sep) + + +_NodeType = TypeVar("_NodeType", bound="Node") + + +class NodeMeta(type): + def __call__(self, *k, **kw): + msg = ( + "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" + "See " + "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" + " for more details." + ).format(name=self.__name__) + fail(msg, pytrace=False) + + def _create(self, *k, **kw): + return super().__call__(*k, **kw) + + +class Node(metaclass=NodeMeta): + """Base class for Collector and Item, the components of the test + collection tree. + + Collector subclasses have children; Items are leaf nodes. + """ + + # Use __slots__ to make attribute access faster. + # Note that __dict__ is still available. + __slots__ = ( + "name", + "parent", + "config", + "session", + "fspath", + "_nodeid", + "_store", + "__dict__", + ) + + def __init__( + self, + name: str, + parent: "Optional[Node]" = None, + config: Optional[Config] = None, + session: "Optional[Session]" = None, + fspath: Optional[py.path.local] = None, + nodeid: Optional[str] = None, + ) -> None: + #: A unique name within the scope of the parent node. + self.name = name + + #: The parent collector node. + self.parent = parent + + #: The pytest config object. + if config: + self.config: Config = config + else: + if not parent: + raise TypeError("config or parent must be provided") + self.config = parent.config + + #: The pytest session this node is part of. + if session: + self.session = session + else: + if not parent: + raise TypeError("session or parent must be provided") + self.session = parent.session + + #: Filesystem path where this node was collected from (can be None). + self.fspath = fspath or getattr(parent, "fspath", None) + + #: Keywords/markers collected from all scopes. + self.keywords = NodeKeywords(self) + + #: The marker objects belonging to this node. + self.own_markers: List[Mark] = [] + + #: Allow adding of extra keywords to use for matching. + self.extra_keyword_matches: Set[str] = set() + + if nodeid is not None: + assert "::()" not in nodeid + self._nodeid = nodeid + else: + if not self.parent: + raise TypeError("nodeid or parent must be provided") + self._nodeid = self.parent.nodeid + if self.name != "()": + self._nodeid += "::" + self.name + + # A place where plugins can store information on the node for their + # own use. Currently only intended for internal plugins. + self._store = Store() + + @classmethod + def from_parent(cls, parent: "Node", **kw): + """Public constructor for Nodes. + + This indirection got introduced in order to enable removing + the fragile logic from the node constructors. + + Subclasses can use ``super().from_parent(...)`` when overriding the + construction. + + :param parent: The parent node of this Node. + """ + if "config" in kw: + raise TypeError("config is not a valid argument for from_parent") + if "session" in kw: + raise TypeError("session is not a valid argument for from_parent") + return cls._create(parent=parent, **kw) + + @property + def ihook(self): + """fspath-sensitive hook proxy used to call pytest hooks.""" + return self.session.gethookproxy(self.fspath) + + def __repr__(self) -> str: + return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) + + def warn(self, warning: Warning) -> None: + """Issue a warning for this Node. + + Warnings will be displayed after the test session, unless explicitly suppressed. + + :param Warning warning: + The warning instance to issue. + + :raises ValueError: If ``warning`` instance is not a subclass of Warning. + + Example usage: + + .. code-block:: python + + node.warn(PytestWarning("some message")) + node.warn(UserWarning("some message")) + + .. versionchanged:: 6.2 + Any subclass of :class:`Warning` is now accepted, rather than only + :class:`PytestWarning ` subclasses. + """ + # enforce type checks here to avoid getting a generic type error later otherwise. + if not isinstance(warning, Warning): + raise ValueError( + "warning must be an instance of Warning or subclass, got {!r}".format( + warning + ) + ) + path, lineno = get_fslocation_from_item(self) + assert lineno is not None + warnings.warn_explicit( + warning, category=None, filename=str(path), lineno=lineno + 1, + ) + + # Methods for ordering nodes. + + @property + def nodeid(self) -> str: + """A ::-separated string denoting its collection tree address.""" + return self._nodeid + + def __hash__(self) -> int: + return hash(self._nodeid) + + def setup(self) -> None: + pass + + def teardown(self) -> None: + pass + + def listchain(self) -> List["Node"]: + """Return list of all parent collectors up to self, starting from + the root of collection tree.""" + chain = [] + item: Optional[Node] = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker( + self, marker: Union[str, MarkDecorator], append: bool = True + ) -> None: + """Dynamically add a marker object to the node. + + :param append: + Whether to append the marker, or prepend it. + """ + from _pytest.mark import MARK_GEN + + if isinstance(marker, MarkDecorator): + marker_ = marker + elif isinstance(marker, str): + marker_ = getattr(MARK_GEN, marker) + else: + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker_.name] = marker_ + if append: + self.own_markers.append(marker_.mark) + else: + self.own_markers.insert(0, marker_.mark) + + def iter_markers(self, name: Optional[str] = None) -> Iterator[Mark]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + """ + return (x[1] for x in self.iter_markers_with_node(name=name)) + + def iter_markers_with_node( + self, name: Optional[str] = None + ) -> Iterator[Tuple["Node", Mark]]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of (node, mark) tuples. + """ + for node in reversed(self.listchain()): + for mark in node.own_markers: + if name is None or getattr(mark, "name", None) == name: + yield node, mark + + @overload + def get_closest_marker(self, name: str) -> Optional[Mark]: + ... + + @overload + def get_closest_marker(self, name: str, default: Mark) -> Mark: + ... + + def get_closest_marker( + self, name: str, default: Optional[Mark] = None + ) -> Optional[Mark]: + """Return the first marker matching the name, from closest (for + example function) to farther level (for example module level). + + :param default: Fallback return value if no marker was found. + :param name: Name to filter by. + """ + return next(self.iter_markers(name=name), default) + + def listextrakeywords(self) -> Set[str]: + """Return a set of all extra keywords in self and any parents.""" + extra_keywords: Set[str] = set() + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + + def listnames(self) -> List[str]: + return [x.name for x in self.listchain()] + + def addfinalizer(self, fin: Callable[[], object]) -> None: + """Register a function to be called when this node is finalized. + + This method can only be called when this node is active + in a setup chain, for example during self.setup(). + """ + self.session._setupstate.addfinalizer(fin, self) + + def getparent(self, cls: Type[_NodeType]) -> Optional[_NodeType]: + """Get the next parent node (including self) which is an instance of + the given class.""" + current: Optional[Node] = self + while current and not isinstance(current, cls): + current = current.parent + assert current is None or isinstance(current, cls) + return current + + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + pass + + def _repr_failure_py( + self, + excinfo: ExceptionInfo[BaseException], + style: "Optional[_TracebackStyle]" = None, + ) -> TerminalRepr: + from _pytest.fixtures import FixtureLookupError + + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo(excinfo.value.excinfo) + if isinstance(excinfo.value, fail.Exception): + if not excinfo.value.pytrace: + style = "value" + if isinstance(excinfo.value, FixtureLookupError): + return excinfo.value.formatrepr() + if self.config.getoption("fulltrace", False): + style = "long" + else: + tb = _pytest._code.Traceback([excinfo.traceback[-1]]) + self._prunetraceback(excinfo) + if len(excinfo.traceback) == 0: + excinfo.traceback = tb + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? + if style is None: + if self.config.getoption("tbstyle", "auto") == "short": + style = "short" + else: + style = "long" + + if self.config.getoption("verbose", 0) > 1: + truncate_locals = False + else: + truncate_locals = True + + # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. + # It is possible for a fixture/test to change the CWD while this code runs, which + # would then result in the user seeing confusing paths in the failure message. + # To fix this, if the CWD changed, always display the full absolute path. + # It will be better to just always display paths relative to invocation_dir, but + # this requires a lot of plumbing (#6428). + try: + abspath = Path(os.getcwd()) != self.config.invocation_params.dir + except OSError: + abspath = True + + return excinfo.getrepr( + funcargs=True, + abspath=abspath, + showlocals=self.config.getoption("showlocals", False), + style=style, + tbfilter=False, # pruned already, or in --fulltrace mode. + truncate_locals=truncate_locals, + ) + + def repr_failure( + self, + excinfo: ExceptionInfo[BaseException], + style: "Optional[_TracebackStyle]" = None, + ) -> Union[str, TerminalRepr]: + """Return a representation of a collection or test failure. + + :param excinfo: Exception information for the failure. + """ + return self._repr_failure_py(excinfo, style) + + +def get_fslocation_from_item( + node: "Node", +) -> Tuple[Union[str, py.path.local], Optional[int]]: + """Try to extract the actual location from a node, depending on available attributes: + + * "location": a pair (path, lineno) + * "obj": a Python object that the node wraps. + * "fspath": just a path + + :rtype: A tuple of (str|py.path.local, int) with filename and line number. + """ + # See Item.location. + location: Optional[Tuple[str, Optional[int], str]] = getattr(node, "location", None) + if location is not None: + return location[:2] + obj = getattr(node, "obj", None) + if obj is not None: + return getfslineno(obj) + return getattr(node, "fspath", "unknown location"), -1 + + +class Collector(Node): + """Collector instances create children through collect() and thus + iteratively build a tree.""" + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + def collect(self) -> Iterable[Union["Item", "Collector"]]: + """Return a list of children (items and collectors) for this + collection node.""" + raise NotImplementedError("abstract") + + # TODO: This omits the style= parameter which breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException] + ) -> Union[str, TerminalRepr]: + """Return a representation of a collection failure. + + :param excinfo: Exception information for the failure. + """ + if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( + "fulltrace", False + ): + exc = excinfo.value + return str(exc.args[0]) + + # Respect explicit tbstyle option, but default to "short" + # (_repr_failure_py uses "long" with "fulltrace" option always). + tbstyle = self.config.getoption("tbstyle", "auto") + if tbstyle == "auto": + tbstyle = "short" + + return self._repr_failure_py(excinfo, style=tbstyle) + + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + if hasattr(self, "fspath"): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.fspath) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + excinfo.traceback = ntraceback.filter() + + +def _check_initialpaths_for_relpath(session, fspath): + for initial_path in session._initialpaths: + if fspath.common(initial_path) == initial_path: + return fspath.relto(initial_path) + + +class FSCollector(Collector): + def __init__( + self, + fspath: py.path.local, + parent=None, + config: Optional[Config] = None, + session: Optional["Session"] = None, + nodeid: Optional[str] = None, + ) -> None: + name = fspath.basename + if parent is not None: + rel = fspath.relto(parent.fspath) + if rel: + name = rel + name = name.replace(os.sep, SEP) + self.fspath = fspath + + session = session or parent.session + + if nodeid is None: + nodeid = self.fspath.relto(session.config.rootdir) + + if not nodeid: + nodeid = _check_initialpaths_for_relpath(session, fspath) + if nodeid and os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) + + @classmethod + def from_parent(cls, parent, *, fspath, **kw): + """The public constructor.""" + return super().from_parent(parent=parent, fspath=fspath, **kw) + + def gethookproxy(self, fspath: py.path.local): + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.gethookproxy(fspath) + + def isinitpath(self, path: py.path.local) -> bool: + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.isinitpath(path) + + +class File(FSCollector): + """Base class for collecting tests from a file. + + :ref:`non-python tests`. + """ + + +class Item(Node): + """A basic test invocation item. + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None + + def __init__( + self, + name, + parent=None, + config: Optional[Config] = None, + session: Optional["Session"] = None, + nodeid: Optional[str] = None, + ) -> None: + super().__init__(name, parent, config, session, nodeid=nodeid) + self._report_sections: List[Tuple[str, str, str]] = [] + + #: A list of tuples (name, value) that holds user defined properties + #: for this test. + self.user_properties: List[Tuple[str, object]] = [] + + def runtest(self) -> None: + raise NotImplementedError("runtest must be implemented by Item subclass") + + def add_report_section(self, when: str, key: str, content: str) -> None: + """Add a new report section, similar to what's done internally to add + stdout and stderr captured output:: + + item.add_report_section("call", "stdout", "report section contents") + + :param str when: + One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. + :param str key: + Name of the section, can be customized at will. Pytest uses ``"stdout"`` and + ``"stderr"`` internally. + :param str content: + The full contents as a string. + """ + if content: + self._report_sections.append((when, key, content)) + + def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]: + return self.fspath, None, "" + + @cached_property + def location(self) -> Tuple[str, Optional[int], str]: + location = self.reportinfo() + fspath = absolutepath(str(location[0])) + relfspath = self.session._node_location_to_relpath(fspath) + assert type(location[2]) is str + return (relfspath, location[1], location[2]) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nose.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nose.py new file mode 100644 index 0000000..bb8f997 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/nose.py @@ -0,0 +1,39 @@ +"""Run testsuites written for nose.""" +from _pytest import python +from _pytest import unittest +from _pytest.config import hookimpl +from _pytest.nodes import Item + + +@hookimpl(trylast=True) +def pytest_runtest_setup(item): + if is_potential_nosetest(item): + if not call_optional(item.obj, "setup"): + # Call module level setup if there is no object level one. + call_optional(item.parent.obj, "setup") + # XXX This implies we only call teardown when setup worked. + item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item) + + +def teardown_nose(item): + if is_potential_nosetest(item): + if not call_optional(item.obj, "teardown"): + call_optional(item.parent.obj, "teardown") + + +def is_potential_nosetest(item: Item) -> bool: + # Extra check needed since we do not do nose style setup/teardown + # on direct unittest style classes. + return isinstance(item, python.Function) and not isinstance( + item, unittest.TestCaseFunction + ) + + +def call_optional(obj, name): + method = getattr(obj, name, None) + isfixture = hasattr(method, "_pytestfixturefunction") + if method is not None and not isfixture and callable(method): + # If there's any problems allow the exception to raise rather than + # silently ignoring them. + method() + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/outcomes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/outcomes.py new file mode 100644 index 0000000..8f6203f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/outcomes.py @@ -0,0 +1,227 @@ +"""Exception classes and constants handling test outcomes as well as +functions creating them.""" +import sys +from typing import Any +from typing import Callable +from typing import cast +from typing import Optional +from typing import Type +from typing import TypeVar + +TYPE_CHECKING = False # Avoid circular import through compat. + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Protocol +else: + # typing.Protocol is only available starting from Python 3.8. It is also + # available from typing_extensions, but we don't want a runtime dependency + # on that. So use a dummy runtime implementation. + from typing import Generic + + Protocol = Generic + + +class OutcomeException(BaseException): + """OutcomeException and its subclass instances indicate and contain info + about test and collection outcomes.""" + + def __init__(self, msg: Optional[str] = None, pytrace: bool = True) -> None: + if msg is not None and not isinstance(msg, str): + error_msg = ( # type: ignore[unreachable] + "{} expected string as 'msg' parameter, got '{}' instead.\n" + "Perhaps you meant to use a mark?" + ) + raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__)) + BaseException.__init__(self, msg) + self.msg = msg + self.pytrace = pytrace + + def __repr__(self) -> str: + if self.msg is not None: + return self.msg + return f"<{self.__class__.__name__} instance>" + + __str__ = __repr__ + + +TEST_OUTCOME = (OutcomeException, Exception) + + +class Skipped(OutcomeException): + # XXX hackish: on 3k we fake to live in the builtins + # in order to have Skipped exception printing shorter/nicer + __module__ = "builtins" + + def __init__( + self, + msg: Optional[str] = None, + pytrace: bool = True, + allow_module_level: bool = False, + ) -> None: + OutcomeException.__init__(self, msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + + +class Failed(OutcomeException): + """Raised from an explicit call to pytest.fail().""" + + __module__ = "builtins" + + +class Exit(Exception): + """Raised for immediate program exits (no tracebacks/summaries).""" + + def __init__( + self, msg: str = "unknown reason", returncode: Optional[int] = None + ) -> None: + self.msg = msg + self.returncode = returncode + super().__init__(msg) + + +# Elaborate hack to work around https://github.com/python/mypy/issues/2087. +# Ideally would just be `exit.Exception = Exit` etc. + +_F = TypeVar("_F", bound=Callable[..., object]) +_ET = TypeVar("_ET", bound=Type[BaseException]) + + +class _WithException(Protocol[_F, _ET]): + Exception: _ET + __call__: _F + + +def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _ET]]: + def decorate(func: _F) -> _WithException[_F, _ET]: + func_with_exception = cast(_WithException[_F, _ET], func) + func_with_exception.Exception = exception_type + return func_with_exception + + return decorate + + +# Exposed helper methods. + + +@_with_exception(Exit) +def exit(msg: str, returncode: Optional[int] = None) -> "NoReturn": + """Exit testing process. + + :param str msg: Message to display upon exit. + :param int returncode: Return code to be used when exiting pytest. + """ + __tracebackhide__ = True + raise Exit(msg, returncode) + + +@_with_exception(Skipped) +def skip(msg: str = "", *, allow_module_level: bool = False) -> "NoReturn": + """Skip an executing test with the given message. + + This function should be called only during testing (setup, call or teardown) or + during collection by using the ``allow_module_level`` flag. This function can + be called in doctests as well. + + :param bool allow_module_level: + Allows this function to be called at module level, skipping the rest + of the module. Defaults to False. + + .. note:: + It is better to use the :ref:`pytest.mark.skipif ref` marker when + possible to declare a test to be skipped under certain conditions + like mismatching platforms or dependencies. + Similarly, use the ``# doctest: +SKIP`` directive (see `doctest.SKIP + `_) + to skip a doctest statically. + """ + __tracebackhide__ = True + raise Skipped(msg=msg, allow_module_level=allow_module_level) + + +@_with_exception(Failed) +def fail(msg: str = "", pytrace: bool = True) -> "NoReturn": + """Explicitly fail an executing test with the given message. + + :param str msg: + The message to show the user as reason for the failure. + :param bool pytrace: + If False, msg represents the full failure information and no + python traceback will be reported. + """ + __tracebackhide__ = True + raise Failed(msg=msg, pytrace=pytrace) + + +class XFailed(Failed): + """Raised from an explicit call to pytest.xfail().""" + + +@_with_exception(XFailed) +def xfail(reason: str = "") -> "NoReturn": + """Imperatively xfail an executing test or setup function with the given reason. + + This function should be called only during testing (setup, call or teardown). + + .. note:: + It is better to use the :ref:`pytest.mark.xfail ref` marker when + possible to declare a test to be xfailed under certain conditions + like known bugs or missing features. + """ + __tracebackhide__ = True + raise XFailed(reason) + + +def importorskip( + modname: str, minversion: Optional[str] = None, reason: Optional[str] = None +) -> Any: + """Import and return the requested module ``modname``, or skip the + current test if the module cannot be imported. + + :param str modname: + The name of the module to import. + :param str minversion: + If given, the imported module's ``__version__`` attribute must be at + least this minimal version, otherwise the test is still skipped. + :param str reason: + If given, this reason is shown as the message when the module cannot + be imported. + + :returns: + The imported module. This should be assigned to its canonical name. + + Example:: + + docutils = pytest.importorskip("docutils") + """ + import warnings + + __tracebackhide__ = True + compile(modname, "", "eval") # to catch syntaxerrors + + with warnings.catch_warnings(): + # Make sure to ignore ImportWarnings that might happen because + # of existing directories with the same name we're trying to + # import but without a __init__.py file. + warnings.simplefilter("ignore") + try: + __import__(modname) + except ImportError as exc: + if reason is None: + reason = f"could not import {modname!r}: {exc}" + raise Skipped(reason, allow_module_level=True) from None + mod = sys.modules[modname] + if minversion is None: + return mod + verattr = getattr(mod, "__version__", None) + if minversion is not None: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if verattr is None or Version(verattr) < Version(minversion): + raise Skipped( + "module %r has __version__ %r, required is: %r" + % (modname, verattr, minversion), + allow_module_level=True, + ) + return mod diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pastebin.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pastebin.py new file mode 100644 index 0000000..131873c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pastebin.py @@ -0,0 +1,110 @@ +"""Submit failure or test session information to a pastebin service.""" +import tempfile +from io import StringIO +from typing import IO +from typing import Union + +import pytest +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config.argparsing import Parser +from _pytest.store import StoreKey +from _pytest.terminal import TerminalReporter + + +pastebinfile_key = StoreKey[IO[bytes]]() + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group._addoption( + "--pastebin", + metavar="mode", + action="store", + dest="pastebin", + default=None, + choices=["failed", "all"], + help="send failed|all info to bpaste.net pastebin service.", + ) + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + if config.option.pastebin == "all": + tr = config.pluginmanager.getplugin("terminalreporter") + # If no terminal reporter plugin is present, nothing we can do here; + # this can happen when this function executes in a worker node + # when using pytest-xdist, for example. + if tr is not None: + # pastebin file will be UTF-8 encoded binary file. + config._store[pastebinfile_key] = tempfile.TemporaryFile("w+b") + oldwrite = tr._tw.write + + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + if isinstance(s, str): + s = s.encode("utf-8") + config._store[pastebinfile_key].write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config: Config) -> None: + if pastebinfile_key in config._store: + pastebinfile = config._store[pastebinfile_key] + # Get terminal contents and delete file. + pastebinfile.seek(0) + sessionlog = pastebinfile.read() + pastebinfile.close() + del config._store[pastebinfile_key] + # Undo our patching in the terminal reporter. + tr = config.pluginmanager.getplugin("terminalreporter") + del tr._tw.__dict__["write"] + # Write summary. + tr.write_sep("=", "Sending information to Paste Service") + pastebinurl = create_new_paste(sessionlog) + tr.write_line("pastebin session-log: %s\n" % pastebinurl) + + +def create_new_paste(contents: Union[str, bytes]) -> str: + """Create a new paste using the bpaste.net service. + + :contents: Paste contents string. + :returns: URL to the pasted contents, or an error message. + """ + import re + from urllib.request import urlopen + from urllib.parse import urlencode + + params = {"code": contents, "lexer": "text", "expiry": "1week"} + url = "https://bpaste.net" + try: + response: str = ( + urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + ) + except OSError as exc_info: # urllib errors + return "bad response: %s" % exc_info + m = re.search(r'href="/raw/(\w+)"', response) + if m: + return "{}/show/{}".format(url, m.group(1)) + else: + return "bad response: invalid format ('" + response + "')" + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + if terminalreporter.config.option.pastebin != "failed": + return + if "failed" in terminalreporter.stats: + terminalreporter.write_sep("=", "Sending information to Paste Service") + for rep in terminalreporter.stats["failed"]: + try: + msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc + except AttributeError: + msg = terminalreporter._getfailureheadline(rep) + file = StringIO() + tw = create_terminal_writer(terminalreporter.config, file) + rep.toterminal(tw) + s = file.getvalue() + assert len(s) + pastebinurl = create_new_paste(s) + terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pathlib.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pathlib.py new file mode 100644 index 0000000..7d9269a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pathlib.py @@ -0,0 +1,654 @@ +import atexit +import contextlib +import fnmatch +import importlib.util +import itertools +import os +import shutil +import sys +import uuid +import warnings +from enum import Enum +from errno import EBADF +from errno import ELOOP +from errno import ENOENT +from errno import ENOTDIR +from functools import partial +from os.path import expanduser +from os.path import expandvars +from os.path import isabs +from os.path import sep +from pathlib import Path +from pathlib import PurePath +from posixpath import sep as posix_sep +from types import ModuleType +from typing import Callable +from typing import Iterable +from typing import Iterator +from typing import Optional +from typing import Set +from typing import TypeVar +from typing import Union + +import py + +from _pytest.compat import assert_never +from _pytest.outcomes import skip +from _pytest.warning_types import PytestWarning + +LOCK_TIMEOUT = 60 * 60 * 24 * 3 + + +_AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) + +# The following function, variables and comments were +# copied from cpython 3.9 Lib/pathlib.py file. + +# EBADF - guard against macOS `stat` throwing EBADF +_IGNORED_ERRORS = (ENOENT, ENOTDIR, EBADF, ELOOP) + +_IGNORED_WINERRORS = ( + 21, # ERROR_NOT_READY - drive exists but is not accessible + 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself +) + + +def _ignore_error(exception): + return ( + getattr(exception, "errno", None) in _IGNORED_ERRORS + or getattr(exception, "winerror", None) in _IGNORED_WINERRORS + ) + + +def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: + return path.joinpath(".lock") + + +def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool: + """Handle known read-only errors during rmtree. + + The returned value is used only by our own tests. + """ + exctype, excvalue = exc[:2] + + # Another process removed the file in the middle of the "rm_rf" (xdist for example). + # More context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 + if isinstance(excvalue, FileNotFoundError): + return False + + if not isinstance(excvalue, PermissionError): + warnings.warn( + PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}") + ) + return False + + if func not in (os.rmdir, os.remove, os.unlink): + if func not in (os.open,): + warnings.warn( + PytestWarning( + "(rm_rf) unknown function {} when removing {}:\n{}: {}".format( + func, path, exctype, excvalue + ) + ) + ) + return False + + # Chmod + retry. + import stat + + def chmod_rw(p: str) -> None: + mode = os.stat(p).st_mode + os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) + + # For files, we need to recursively go upwards in the directories to + # ensure they all are also writable. + p = Path(path) + if p.is_file(): + for parent in p.parents: + chmod_rw(str(parent)) + # Stop when we reach the original path passed to rm_rf. + if parent == start_path: + break + chmod_rw(str(path)) + + func(path) + return True + + +def ensure_extended_length_path(path: Path) -> Path: + """Get the extended-length version of a path (Windows). + + On Windows, by default, the maximum length of a path (MAX_PATH) is 260 + characters, and operations on paths longer than that fail. But it is possible + to overcome this by converting the path to "extended-length" form before + performing the operation: + https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + + On Windows, this function returns the extended-length absolute version of path. + On other platforms it returns path unchanged. + """ + if sys.platform.startswith("win32"): + path = path.resolve() + path = Path(get_extended_length_path_str(str(path))) + return path + + +def get_extended_length_path_str(path: str) -> str: + """Convert a path to a Windows extended length path.""" + long_path_prefix = "\\\\?\\" + unc_long_path_prefix = "\\\\?\\UNC\\" + if path.startswith((long_path_prefix, unc_long_path_prefix)): + return path + # UNC + if path.startswith("\\\\"): + return unc_long_path_prefix + path[2:] + return long_path_prefix + path + + +def rm_rf(path: Path) -> None: + """Remove the path contents recursively, even if some elements + are read-only.""" + path = ensure_extended_length_path(path) + onerror = partial(on_rm_rf_error, start_path=path) + shutil.rmtree(str(path), onerror=onerror) + + +def find_prefixed(root: Path, prefix: str) -> Iterator[Path]: + """Find all elements in root that begin with the prefix, case insensitive.""" + l_prefix = prefix.lower() + for x in root.iterdir(): + if x.name.lower().startswith(l_prefix): + yield x + + +def extract_suffixes(iter: Iterable[PurePath], prefix: str) -> Iterator[str]: + """Return the parts of the paths following the prefix. + + :param iter: Iterator over path names. + :param prefix: Expected prefix of the path names. + """ + p_len = len(prefix) + for p in iter: + yield p.name[p_len:] + + +def find_suffixes(root: Path, prefix: str) -> Iterator[str]: + """Combine find_prefixes and extract_suffixes.""" + return extract_suffixes(find_prefixed(root, prefix), prefix) + + +def parse_num(maybe_num) -> int: + """Parse number path suffixes, returns -1 on error.""" + try: + return int(maybe_num) + except ValueError: + return -1 + + +def _force_symlink( + root: Path, target: Union[str, PurePath], link_to: Union[str, Path] +) -> None: + """Helper to create the current symlink. + + It's full of race conditions that are reasonably OK to ignore + for the context of best effort linking to the latest test run. + + The presumption being that in case of much parallelism + the inaccuracy is going to be acceptable. + """ + current_symlink = root.joinpath(target) + try: + current_symlink.unlink() + except OSError: + pass + try: + current_symlink.symlink_to(link_to) + except Exception: + pass + + +def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: + """Create a directory with an increased number as suffix for the given prefix.""" + for i in range(10): + # try up to 10 times to create the folder + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + new_number = max_existing + 1 + new_path = root.joinpath(f"{prefix}{new_number}") + try: + new_path.mkdir(mode=mode) + except Exception: + pass + else: + _force_symlink(root, prefix + "current", new_path) + return new_path + else: + raise OSError( + "could not create numbered dir with prefix " + "{prefix} in {root} after 10 tries".format(prefix=prefix, root=root) + ) + + +def create_cleanup_lock(p: Path) -> Path: + """Create a lock to prevent premature folder cleanup.""" + lock_path = get_lock_path(p) + try: + fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) + except FileExistsError as e: + raise OSError(f"cannot create lockfile in {p}") from e + else: + pid = os.getpid() + spid = str(pid).encode() + os.write(fd, spid) + os.close(fd) + if not lock_path.is_file(): + raise OSError("lock path got renamed after successful creation") + return lock_path + + +def register_cleanup_lock_removal(lock_path: Path, register=atexit.register): + """Register a cleanup function for removing a lock, by default on atexit.""" + pid = os.getpid() + + def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None: + current_pid = os.getpid() + if current_pid != original_pid: + # fork + return + try: + lock_path.unlink() + except OSError: + pass + + return register(cleanup_on_exit) + + +def maybe_delete_a_numbered_dir(path: Path) -> None: + """Remove a numbered directory if its lock can be obtained and it does + not seem to be in use.""" + path = ensure_extended_length_path(path) + lock_path = None + try: + lock_path = create_cleanup_lock(path) + parent = path.parent + + garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") + path.rename(garbage) + rm_rf(garbage) + except OSError: + # known races: + # * other process did a cleanup at the same time + # * deletable folder was found + # * process cwd (Windows) + return + finally: + # If we created the lock, ensure we remove it even if we failed + # to properly remove the numbered dir. + if lock_path is not None: + try: + lock_path.unlink() + except OSError: + pass + + +def ensure_deletable(path: Path, consider_lock_dead_if_created_before: float) -> bool: + """Check if `path` is deletable based on whether the lock file is expired.""" + if path.is_symlink(): + return False + lock = get_lock_path(path) + try: + if not lock.is_file(): + return True + except OSError: + # we might not have access to the lock file at all, in this case assume + # we don't have access to the entire directory (#7491). + return False + try: + lock_time = lock.stat().st_mtime + except Exception: + return False + else: + if lock_time < consider_lock_dead_if_created_before: + # We want to ignore any errors while trying to remove the lock such as: + # - PermissionDenied, like the file permissions have changed since the lock creation; + # - FileNotFoundError, in case another pytest process got here first; + # and any other cause of failure. + with contextlib.suppress(OSError): + lock.unlink() + return True + return False + + +def try_cleanup(path: Path, consider_lock_dead_if_created_before: float) -> None: + """Try to cleanup a folder if we can ensure it's deletable.""" + if ensure_deletable(path, consider_lock_dead_if_created_before): + maybe_delete_a_numbered_dir(path) + + +def cleanup_candidates(root: Path, prefix: str, keep: int) -> Iterator[Path]: + """List candidates for numbered directories to be removed - follows py.path.""" + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + max_delete = max_existing - keep + paths = find_prefixed(root, prefix) + paths, paths2 = itertools.tee(paths) + numbers = map(parse_num, extract_suffixes(paths2, prefix)) + for path, number in zip(paths, numbers): + if number <= max_delete: + yield path + + +def cleanup_numbered_dir( + root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float +) -> None: + """Cleanup for lock driven numbered directories.""" + for path in cleanup_candidates(root, prefix, keep): + try_cleanup(path, consider_lock_dead_if_created_before) + for path in root.glob("garbage-*"): + try_cleanup(path, consider_lock_dead_if_created_before) + + +def make_numbered_dir_with_cleanup( + root: Path, prefix: str, keep: int, lock_timeout: float, mode: int, +) -> Path: + """Create a numbered dir with a cleanup lock and remove old ones.""" + e = None + for i in range(10): + try: + p = make_numbered_dir(root, prefix, mode) + lock_path = create_cleanup_lock(p) + register_cleanup_lock_removal(lock_path) + except Exception as exc: + e = exc + else: + consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout + # Register a cleanup for program exit + atexit.register( + cleanup_numbered_dir, + root, + prefix, + keep, + consider_lock_dead_if_created_before, + ) + return p + assert e is not None + raise e + + +def resolve_from_str(input: str, rootpath: Path) -> Path: + input = expanduser(input) + input = expandvars(input) + if isabs(input): + return Path(input) + else: + return rootpath.joinpath(input) + + +def fnmatch_ex(pattern: str, path) -> bool: + """A port of FNMatcher from py.path.common which works with PurePath() instances. + + The difference between this algorithm and PurePath.match() is that the + latter matches "**" glob expressions for each part of the path, while + this algorithm uses the whole path instead. + + For example: + "tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" + with this algorithm, but not with PurePath.match(). + + This algorithm was ported to keep backward-compatibility with existing + settings which assume paths match according this logic. + + References: + * https://bugs.python.org/issue29249 + * https://bugs.python.org/issue34731 + """ + path = PurePath(path) + iswin32 = sys.platform.startswith("win") + + if iswin32 and sep not in pattern and posix_sep in pattern: + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posix_sep, sep) + + if sep not in pattern: + name = path.name + else: + name = str(path) + if path.is_absolute() and not os.path.isabs(pattern): + pattern = f"*{os.sep}{pattern}" + return fnmatch.fnmatch(name, pattern) + + +def parts(s: str) -> Set[str]: + parts = s.split(sep) + return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))} + + +def symlink_or_skip(src, dst, **kwargs): + """Make a symlink, or skip the test in case symlinks are not supported.""" + try: + os.symlink(str(src), str(dst), **kwargs) + except OSError as e: + skip(f"symlinks not supported: {e}") + + +class ImportMode(Enum): + """Possible values for `mode` parameter of `import_path`.""" + + prepend = "prepend" + append = "append" + importlib = "importlib" + + +class ImportPathMismatchError(ImportError): + """Raised on import_path() if there is a mismatch of __file__'s. + + This can happen when `import_path` is called multiple times with different filenames that has + the same basename but reside in packages + (for example "/tests1/test_foo.py" and "/tests2/test_foo.py"). + """ + + +def import_path( + p: Union[str, py.path.local, Path], + *, + mode: Union[str, ImportMode] = ImportMode.prepend, +) -> ModuleType: + """Import and return a module from the given path, which can be a file (a module) or + a directory (a package). + + The import mechanism used is controlled by the `mode` parameter: + + * `mode == ImportMode.prepend`: the directory containing the module (or package, taking + `__init__.py` files into account) will be put at the *start* of `sys.path` before + being imported with `__import__. + + * `mode == ImportMode.append`: same as `prepend`, but the directory will be appended + to the end of `sys.path`, if not already in `sys.path`. + + * `mode == ImportMode.importlib`: uses more fine control mechanisms provided by `importlib` + to import the module, which avoids having to use `__import__` and muck with `sys.path` + at all. It effectively allows having same-named test modules in different places. + + :raises ImportPathMismatchError: + If after importing the given `path` and the module `__file__` + are different. Only raised in `prepend` and `append` modes. + """ + mode = ImportMode(mode) + + path = Path(str(p)) + + if not path.exists(): + raise ImportError(path) + + if mode is ImportMode.importlib: + module_name = path.stem + + for meta_importer in sys.meta_path: + spec = meta_importer.find_spec(module_name, [str(path.parent)]) + if spec is not None: + break + else: + spec = importlib.util.spec_from_file_location(module_name, str(path)) + + if spec is None: + raise ImportError( + "Can't find module {} at location {}".format(module_name, str(path)) + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) # type: ignore[union-attr] + return mod + + pkg_path = resolve_package_path(path) + if pkg_path is not None: + pkg_root = pkg_path.parent + names = list(path.with_suffix("").relative_to(pkg_root).parts) + if names[-1] == "__init__": + names.pop() + module_name = ".".join(names) + else: + pkg_root = path.parent + module_name = path.stem + + # Change sys.path permanently: restoring it at the end of this function would cause surprising + # problems because of delayed imports: for example, a conftest.py file imported by this function + # might have local imports, which would fail at runtime if we restored sys.path. + if mode is ImportMode.append: + if str(pkg_root) not in sys.path: + sys.path.append(str(pkg_root)) + elif mode is ImportMode.prepend: + if str(pkg_root) != sys.path[0]: + sys.path.insert(0, str(pkg_root)) + else: + assert_never(mode) + + importlib.import_module(module_name) + + mod = sys.modules[module_name] + if path.name == "__init__.py": + return mod + + ignore = os.environ.get("PY_IGNORE_IMPORTMISMATCH", "") + if ignore != "1": + module_file = mod.__file__ + if module_file.endswith((".pyc", ".pyo")): + module_file = module_file[:-1] + if module_file.endswith(os.path.sep + "__init__.py"): + module_file = module_file[: -(len(os.path.sep + "__init__.py"))] + + try: + is_same = _is_same(str(path), module_file) + except FileNotFoundError: + is_same = False + + if not is_same: + raise ImportPathMismatchError(module_name, module_file, path) + + return mod + + +# Implement a special _is_same function on Windows which returns True if the two filenames +# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). +if sys.platform.startswith("win"): + + def _is_same(f1: str, f2: str) -> bool: + return Path(f1) == Path(f2) or os.path.samefile(f1, f2) + + +else: + + def _is_same(f1: str, f2: str) -> bool: + return os.path.samefile(f1, f2) + + +def resolve_package_path(path: Path) -> Optional[Path]: + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + + Returns None if it can not be determined. + """ + result = None + for parent in itertools.chain((path,), path.parents): + if parent.is_dir(): + if not parent.joinpath("__init__.py").is_file(): + break + if not parent.name.isidentifier(): + break + result = parent + return result + + +def visit( + path: str, recurse: Callable[["os.DirEntry[str]"], bool] +) -> Iterator["os.DirEntry[str]"]: + """Walk a directory recursively, in breadth-first order. + + Entries at each directory level are sorted. + """ + + # Skip entries with symlink loops and other brokenness, so the caller doesn't + # have to deal with it. + entries = [] + for entry in os.scandir(path): + try: + entry.is_file() + except OSError as err: + if _ignore_error(err): + continue + raise + entries.append(entry) + + entries.sort(key=lambda entry: entry.name) + + yield from entries + + for entry in entries: + if entry.is_dir() and recurse(entry): + yield from visit(entry.path, recurse) + + +def absolutepath(path: Union[Path, str]) -> Path: + """Convert a path to an absolute path using os.path.abspath. + + Prefer this over Path.resolve() (see #6523). + Prefer this over Path.absolute() (not public, doesn't normalize). + """ + return Path(os.path.abspath(str(path))) + + +def commonpath(path1: Path, path2: Path) -> Optional[Path]: + """Return the common part shared with the other path, or None if there is + no common part. + + If one path is relative and one is absolute, returns None. + """ + try: + return Path(os.path.commonpath((str(path1), str(path2)))) + except ValueError: + return None + + +def bestrelpath(directory: Path, dest: Path) -> str: + """Return a string which is a relative path from directory to dest such + that directory/bestrelpath == dest. + + The paths must be either both absolute or both relative. + + If no such path can be determined, returns dest. + """ + if dest == directory: + return os.curdir + # Find the longest common directory. + base = commonpath(directory, dest) + # Can be the case on Windows for two absolute paths on different drives. + # Can be the case for two relative paths without common prefix. + # Can be the case for a relative path and an absolute path. + if not base: + return str(dest) + reldirectory = directory.relative_to(base) + reldest = dest.relative_to(base) + return os.path.join( + # Back from directory to base. + *([os.pardir] * len(reldirectory.parts)), + # Forward from base to dest. + *reldest.parts, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/py.typed b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester.py new file mode 100644 index 0000000..31259d1 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester.py @@ -0,0 +1,1922 @@ +"""(Disabled by default) support for testing pytest and pytest plugins. + +PYTEST_DONT_REWRITE +""" +import collections.abc +import contextlib +import gc +import importlib +import os +import platform +import re +import shutil +import subprocess +import sys +import traceback +from fnmatch import fnmatch +from io import StringIO +from pathlib import Path +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import TextIO +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union +from weakref import WeakKeyDictionary + +import attr +import py +from iniconfig import IniConfig +from iniconfig import SectionWrapper + +from _pytest import timing +from _pytest._code import Source +from _pytest.capture import _get_multicapture +from _pytest.compat import final +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import main +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import importorskip +from _pytest.outcomes import skip +from _pytest.pathlib import make_numbered_dir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.tmpdir import TempPathFactory +from _pytest.warning_types import PytestWarning + +if TYPE_CHECKING: + from typing_extensions import Literal + + import pexpect + + +pytest_plugins = ["pytester_assertions"] + + +IGNORE_PAM = [ # filenames added when obtaining details about the current user + "/var/lib/sss/mc/passwd" +] + + +def pytest_addoption(parser: Parser) -> None: + parser.addoption( + "--lsof", + action="store_true", + dest="lsof", + default=False, + help="run FD checks if lsof is available", + ) + + parser.addoption( + "--runpytest", + default="inprocess", + dest="runpytest", + choices=("inprocess", "subprocess"), + help=( + "run pytest sub runs in tests using an 'inprocess' " + "or 'subprocess' (python -m main) method" + ), + ) + + parser.addini( + "pytester_example_dir", help="directory to take the pytester example files from" + ) + + +def pytest_configure(config: Config) -> None: + if config.getvalue("lsof"): + checker = LsofFdLeakChecker() + if checker.matching_platform(): + config.pluginmanager.register(checker) + + config.addinivalue_line( + "markers", + "pytester_example_path(*path_segments): join the given path " + "segments to `pytester_example_dir` for this test.", + ) + + +class LsofFdLeakChecker: + def get_open_files(self) -> List[Tuple[str, str]]: + out = subprocess.run( + ("lsof", "-Ffn0", "-p", str(os.getpid())), + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=True, + universal_newlines=True, + ).stdout + + def isopen(line: str) -> bool: + return line.startswith("f") and ( + "deleted" not in line + and "mem" not in line + and "txt" not in line + and "cwd" not in line + ) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split("\0") + fd = fields[0][1:] + filename = fields[1][1:] + if filename in IGNORE_PAM: + continue + if filename.startswith("/"): + open_files.append((fd, filename)) + + return open_files + + def matching_platform(self) -> bool: + try: + subprocess.run(("lsof", "-v"), check=True) + except (OSError, subprocess.CalledProcessError): + return False + else: + return True + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: + lines1 = self.get_open_files() + yield + if hasattr(sys, "pypy_version_info"): + gc.collect() + lines2 = self.get_open_files() + + new_fds = {t[0] for t in lines2} - {t[0] for t in lines1} + leaked_files = [t for t in lines2 if t[0] in new_fds] + if leaked_files: + error = [ + "***** %s FD leakage detected" % len(leaked_files), + *(str(f) for f in leaked_files), + "*** Before:", + *(str(f) for f in lines1), + "*** After:", + *(str(f) for f in lines2), + "***** %s FD leakage detected" % len(leaked_files), + "*** function %s:%s: %s " % item.location, + "See issue #2366", + ] + item.warn(PytestWarning("\n".join(error))) + + +# used at least by pytest-xdist plugin + + +@fixture +def _pytest(request: FixtureRequest) -> "PytestArg": + """Return a helper which offers a gethookrecorder(hook) method which + returns a HookRecorder instance which helps to make assertions about called + hooks.""" + return PytestArg(request) + + +class PytestArg: + def __init__(self, request: FixtureRequest) -> None: + self._request = request + + def gethookrecorder(self, hook) -> "HookRecorder": + hookrecorder = HookRecorder(hook._pm) + self._request.addfinalizer(hookrecorder.finish_recording) + return hookrecorder + + +def get_public_names(values: Iterable[str]) -> List[str]: + """Only return names from iterator values without a leading underscore.""" + return [x for x in values if x[0] != "_"] + + +class ParsedCall: + def __init__(self, name: str, kwargs) -> None: + self.__dict__.update(kwargs) + self._name = name + + def __repr__(self) -> str: + d = self.__dict__.copy() + del d["_name"] + return f"" + + if TYPE_CHECKING: + # The class has undetermined attributes, this tells mypy about it. + def __getattr__(self, key: str): + ... + + +class HookRecorder: + """Record all hooks called in a plugin manager. + + This wraps all the hook calls in the plugin manager, recording each call + before propagating the normal calls. + """ + + def __init__(self, pluginmanager: PytestPluginManager) -> None: + self._pluginmanager = pluginmanager + self.calls: List[ParsedCall] = [] + self.ret: Optional[Union[int, ExitCode]] = None + + def before(hook_name: str, hook_impls, kwargs) -> None: + self.calls.append(ParsedCall(hook_name, kwargs)) + + def after(outcome, hook_name: str, hook_impls, kwargs) -> None: + pass + + self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) + + def finish_recording(self) -> None: + self._undo_wrapping() + + def getcalls(self, names: Union[str, Iterable[str]]) -> List[ParsedCall]: + if isinstance(names, str): + names = names.split() + return [call for call in self.calls if call._name in names] + + def assert_contains(self, entries: Sequence[Tuple[str, str]]) -> None: + __tracebackhide__ = True + i = 0 + entries = list(entries) + backlocals = sys._getframe(1).f_locals + while entries: + name, check = entries.pop(0) + for ind, call in enumerate(self.calls[i:]): + if call._name == name: + print("NAMEMATCH", name, call) + if eval(check, backlocals, call.__dict__): + print("CHECKERMATCH", repr(check), "->", call) + else: + print("NOCHECKERMATCH", repr(check), "-", call) + continue + i += ind + 1 + break + print("NONAMEMATCH", name, "with", call) + else: + fail(f"could not find {name!r} check {check!r}") + + def popcall(self, name: str) -> ParsedCall: + __tracebackhide__ = True + for i, call in enumerate(self.calls): + if call._name == name: + del self.calls[i] + return call + lines = [f"could not find call {name!r}, in:"] + lines.extend([" %s" % x for x in self.calls]) + fail("\n".join(lines)) + + def getcall(self, name: str) -> ParsedCall: + values = self.getcalls(name) + assert len(values) == 1, (name, values) + return values[0] + + # functionality for test reports + + @overload + def getreports( + self, names: "Literal['pytest_collectreport']", + ) -> Sequence[CollectReport]: + ... + + @overload + def getreports( + self, names: "Literal['pytest_runtest_logreport']", + ) -> Sequence[TestReport]: + ... + + @overload + def getreports( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + ... + + def getreports( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + return [x.report for x in self.getcalls(names)] + + def matchreport( + self, + inamepart: str = "", + names: Union[str, Iterable[str]] = ( + "pytest_runtest_logreport", + "pytest_collectreport", + ), + when: Optional[str] = None, + ) -> Union[CollectReport, TestReport]: + """Return a testreport whose dotted import path matches.""" + values = [] + for rep in self.getreports(names=names): + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those + continue + if when and rep.when != when: + continue + if not inamepart or inamepart in rep.nodeid.split("::"): + values.append(rep) + if not values: + raise ValueError( + "could not find test report matching %r: " + "no test reports at all!" % (inamepart,) + ) + if len(values) > 1: + raise ValueError( + "found 2 or more testreports matching {!r}: {}".format( + inamepart, values + ) + ) + return values[0] + + @overload + def getfailures( + self, names: "Literal['pytest_collectreport']", + ) -> Sequence[CollectReport]: + ... + + @overload + def getfailures( + self, names: "Literal['pytest_runtest_logreport']", + ) -> Sequence[TestReport]: + ... + + @overload + def getfailures( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + ... + + def getfailures( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + return [rep for rep in self.getreports(names) if rep.failed] + + def getfailedcollections(self) -> Sequence[CollectReport]: + return self.getfailures("pytest_collectreport") + + def listoutcomes( + self, + ) -> Tuple[ + Sequence[TestReport], + Sequence[Union[CollectReport, TestReport]], + Sequence[Union[CollectReport, TestReport]], + ]: + passed = [] + skipped = [] + failed = [] + for rep in self.getreports( + ("pytest_collectreport", "pytest_runtest_logreport") + ): + if rep.passed: + if rep.when == "call": + assert isinstance(rep, TestReport) + passed.append(rep) + elif rep.skipped: + skipped.append(rep) + else: + assert rep.failed, f"Unexpected outcome: {rep!r}" + failed.append(rep) + return passed, skipped, failed + + def countoutcomes(self) -> List[int]: + return [len(x) for x in self.listoutcomes()] + + def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: + __tracebackhide__ = True + from _pytest.pytester_assertions import assertoutcome + + outcomes = self.listoutcomes() + assertoutcome( + outcomes, passed=passed, skipped=skipped, failed=failed, + ) + + def clear(self) -> None: + self.calls[:] = [] + + +@fixture +def linecomp() -> "LineComp": + """A :class: `LineComp` instance for checking that an input linearly + contains a sequence of strings.""" + return LineComp() + + +@fixture(name="LineMatcher") +def LineMatcher_fixture(request: FixtureRequest) -> Type["LineMatcher"]: + """A reference to the :class: `LineMatcher`. + + This is instantiable with a list of lines (without their trailing newlines). + This is useful for testing large texts, such as the output of commands. + """ + return LineMatcher + + +@fixture +def pytester(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> "Pytester": + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to ``path`` and environment variables during initialization. + + It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path` + fixture but provides methods which aid in testing pytest itself. + """ + return Pytester(request, tmp_path_factory, _ispytest=True) + + +@fixture +def testdir(pytester: "Pytester") -> "Testdir": + """ + Identical to :fixture:`pytester`, and provides an instance whose methods return + legacy ``py.path.local`` objects instead when applicable. + + New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. + """ + return Testdir(pytester, _ispytest=True) + + +@fixture +def _sys_snapshot() -> Generator[None, None, None]: + snappaths = SysPathsSnapshot() + snapmods = SysModulesSnapshot() + yield + snapmods.restore() + snappaths.restore() + + +@fixture +def _config_for_test() -> Generator[Config, None, None]: + from _pytest.config import get_config + + config = get_config() + yield config + config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. + + +# Regex to match the session duration string in the summary: "74.34s". +rex_session_duration = re.compile(r"\d+\.\d\ds") +# Regex to match all the counts and phrases in the summary line: "34 passed, 111 skipped". +rex_outcome = re.compile(r"(\d+) (\w+)") + + +class RunResult: + """The result of running a command.""" + + def __init__( + self, + ret: Union[int, ExitCode], + outlines: List[str], + errlines: List[str], + duration: float, + ) -> None: + try: + self.ret: Union[int, ExitCode] = ExitCode(ret) + """The return value.""" + except ValueError: + self.ret = ret + self.outlines = outlines + """List of lines captured from stdout.""" + self.errlines = errlines + """List of lines captured from stderr.""" + self.stdout = LineMatcher(outlines) + """:class:`LineMatcher` of stdout. + + Use e.g. :func:`str(stdout) ` to reconstruct stdout, or the commonly used + :func:`stdout.fnmatch_lines() ` method. + """ + self.stderr = LineMatcher(errlines) + """:class:`LineMatcher` of stderr.""" + self.duration = duration + """Duration in seconds.""" + + def __repr__(self) -> str: + return ( + "" + % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) + ) + + def parseoutcomes(self) -> Dict[str, int]: + """Return a dictionary of outcome noun -> count from parsing the terminal + output that the test process produced. + + The returned nouns will always be in plural form:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + return self.parse_summary_nouns(self.outlines) + + @classmethod + def parse_summary_nouns(cls, lines) -> Dict[str, int]: + """Extract the nouns from a pytest terminal summary line. + + It always returns the plural noun for consistency:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + for line in reversed(lines): + if rex_session_duration.search(line): + outcomes = rex_outcome.findall(line) + ret = {noun: int(count) for (count, noun) in outcomes} + break + else: + raise ValueError("Pytest terminal summary report not found") + + to_plural = { + "warning": "warnings", + "error": "errors", + } + return {to_plural.get(k, k): v for k, v in ret.items()} + + def assert_outcomes( + self, + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + ) -> None: + """Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + __tracebackhide__ = True + from _pytest.pytester_assertions import assert_outcomes + + outcomes = self.parseoutcomes() + assert_outcomes( + outcomes, + passed=passed, + skipped=skipped, + failed=failed, + errors=errors, + xpassed=xpassed, + xfailed=xfailed, + ) + + +class CwdSnapshot: + def __init__(self) -> None: + self.__saved = os.getcwd() + + def restore(self) -> None: + os.chdir(self.__saved) + + +class SysModulesSnapshot: + def __init__(self, preserve: Optional[Callable[[str], bool]] = None) -> None: + self.__preserve = preserve + self.__saved = dict(sys.modules) + + def restore(self) -> None: + if self.__preserve: + self.__saved.update( + (k, m) for k, m in sys.modules.items() if self.__preserve(k) + ) + sys.modules.clear() + sys.modules.update(self.__saved) + + +class SysPathsSnapshot: + def __init__(self) -> None: + self.__saved = list(sys.path), list(sys.meta_path) + + def restore(self) -> None: + sys.path[:], sys.meta_path[:] = self.__saved + + +@final +class Pytester: + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to ``path`` and environment variables during initialization. + + Attributes: + + :ivar Path path: temporary directory path used to create files/run tests from, etc. + + :ivar plugins: + A list of plugins to use with :py:meth:`parseconfig` and + :py:meth:`runpytest`. Initially this is an empty list but plugins can + be added to the list. The type of items to add to the list depends on + the method using them so refer to them for details. + """ + + __test__ = False + + CLOSE_STDIN = object + + class TimeoutExpired(Exception): + pass + + def __init__( + self, + request: FixtureRequest, + tmp_path_factory: TempPathFactory, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._request = request + self._mod_collections: WeakKeyDictionary[ + Collector, List[Union[Item, Collector]] + ] = (WeakKeyDictionary()) + if request.function: + name: str = request.function.__name__ + else: + name = request.node.name + self._name = name + self._path: Path = tmp_path_factory.mktemp(name, numbered=True) + self.plugins: List[Union[str, _PluggyPlugin]] = [] + self._cwd_snapshot = CwdSnapshot() + self._sys_path_snapshot = SysPathsSnapshot() + self._sys_modules_snapshot = self.__take_sys_modules_snapshot() + self.chdir() + self._request.addfinalizer(self._finalize) + self._method = self._request.config.getoption("--runpytest") + self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) + + self._monkeypatch = mp = MonkeyPatch() + mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) + # Ensure no unexpected caching via tox. + mp.delenv("TOX_ENV_DIR", raising=False) + # Discard outer pytest options. + mp.delenv("PYTEST_ADDOPTS", raising=False) + # Ensure no user config is used. + tmphome = str(self.path) + mp.setenv("HOME", tmphome) + mp.setenv("USERPROFILE", tmphome) + # Do not use colors for inner runs by default. + mp.setenv("PY_COLORS", "0") + + @property + def path(self) -> Path: + """Temporary directory where files are created and pytest is executed.""" + return self._path + + def __repr__(self) -> str: + return f"" + + def _finalize(self) -> None: + """ + Clean up global state artifacts. + + Some methods modify the global interpreter state and this tries to + clean this up. It does not remove the temporary directory however so + it can be looked at after the test run has finished. + """ + self._sys_modules_snapshot.restore() + self._sys_path_snapshot.restore() + self._cwd_snapshot.restore() + self._monkeypatch.undo() + + def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: + # Some zope modules used by twisted-related tests keep internal state + # and can't be deleted; we had some trouble in the past with + # `zope.interface` for example. + # + # Preserve readline due to https://bugs.python.org/issue41033. + # pexpect issues a SIGWINCH. + def preserve_module(name): + return name.startswith(("zope", "readline")) + + return SysModulesSnapshot(preserve=preserve_module) + + def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: + """Create a new :py:class:`HookRecorder` for a PluginManager.""" + pluginmanager.reprec = reprec = HookRecorder(pluginmanager) + self._request.addfinalizer(reprec.finish_recording) + return reprec + + def chdir(self) -> None: + """Cd into the temporary directory. + + This is done automatically upon instantiation. + """ + os.chdir(self.path) + + def _makefile( + self, + ext: str, + lines: Sequence[Union[Any, bytes]], + files: Dict[str, str], + encoding: str = "utf-8", + ) -> Path: + items = list(files.items()) + + def to_text(s: Union[Any, bytes]) -> str: + return s.decode(encoding) if isinstance(s, bytes) else str(s) + + if lines: + source = "\n".join(to_text(x) for x in lines) + basename = self._name + items.insert(0, (basename, source)) + + ret = None + for basename, value in items: + p = self.path.joinpath(basename).with_suffix(ext) + p.parent.mkdir(parents=True, exist_ok=True) + source_ = Source(value) + source = "\n".join(to_text(line) for line in source_.lines) + p.write_text(source.strip(), encoding=encoding) + if ret is None: + ret = p + assert ret is not None + return ret + + def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: + r"""Create new file(s) in the test directory. + + :param str ext: + The extension the file(s) should use, including the dot, e.g. `.py`. + :param args: + All args are treated as strings and joined using newlines. + The result is written as contents to the file. The name of the + file is based on the test function requesting this fixture. + :param kwargs: + Each keyword is the name of a file, while the value of it will + be written as contents of the file. + + Examples: + + .. code-block:: python + + pytester.makefile(".txt", "line1", "line2") + + pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") + + """ + return self._makefile(ext, args, kwargs) + + def makeconftest(self, source: str) -> Path: + """Write a contest.py file with 'source' as contents.""" + return self.makepyfile(conftest=source) + + def makeini(self, source: str) -> Path: + """Write a tox.ini file with 'source' as contents.""" + return self.makefile(".ini", tox=source) + + def getinicfg(self, source: str) -> SectionWrapper: + """Return the pytest section from the tox.ini config file.""" + p = self.makeini(source) + return IniConfig(str(p))["pytest"] + + def makepyprojecttoml(self, source: str) -> Path: + """Write a pyproject.toml file with 'source' as contents. + + .. versionadded:: 6.0 + """ + return self.makefile(".toml", pyproject=source) + + def makepyfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .py extension. + + Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.py. + pytester.makepyfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.makepyfile(custom="foobar") + # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. + + """ + return self._makefile(".py", args, kwargs) + + def maketxtfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .txt extension. + + Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.txt. + pytester.maketxtfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.maketxtfile(custom="foobar") + # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. + + """ + return self._makefile(".txt", args, kwargs) + + def syspathinsert( + self, path: Optional[Union[str, "os.PathLike[str]"]] = None + ) -> None: + """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`. + + This is undone automatically when this object dies at the end of each + test. + """ + if path is None: + path = self.path + + self._monkeypatch.syspath_prepend(str(path)) + + def mkdir(self, name: str) -> Path: + """Create a new (sub)directory.""" + p = self.path / name + p.mkdir() + return p + + def mkpydir(self, name: str) -> Path: + """Create a new python package. + + This creates a (sub)directory with an empty ``__init__.py`` file so it + gets recognised as a Python package. + """ + p = self.path / name + p.mkdir() + p.joinpath("__init__.py").touch() + return p + + def copy_example(self, name: Optional[str] = None) -> Path: + """Copy file from project's directory into the testdir. + + :param str name: The name of the file to copy. + :return: path to the copied directory (inside ``self.path``). + + """ + example_dir = self._request.config.getini("pytester_example_dir") + if example_dir is None: + raise ValueError("pytester_example_dir is unset, can't copy examples") + example_dir = Path(str(self._request.config.rootdir)) / example_dir + + for extra_element in self._request.node.iter_markers("pytester_example_path"): + assert extra_element.args + example_dir = example_dir.joinpath(*extra_element.args) + + if name is None: + func_name = self._name + maybe_dir = example_dir / func_name + maybe_file = example_dir / (func_name + ".py") + + if maybe_dir.is_dir(): + example_path = maybe_dir + elif maybe_file.is_file(): + example_path = maybe_file + else: + raise LookupError( + f"{func_name} can't be found as module or package in {example_dir}" + ) + else: + example_path = example_dir.joinpath(name) + + if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): + # TODO: py.path.local.copy can copy files to existing directories, + # while with shutil.copytree the destination directory cannot exist, + # we will need to roll our own in order to drop py.path.local completely + py.path.local(example_path).copy(py.path.local(self.path)) + return self.path + elif example_path.is_file(): + result = self.path.joinpath(example_path.name) + shutil.copy(example_path, result) + return result + else: + raise LookupError( + f'example "{example_path}" is not found as a file or directory' + ) + + Session = Session + + def getnode( + self, config: Config, arg: Union[str, "os.PathLike[str]"] + ) -> Optional[Union[Collector, Item]]: + """Return the collection node of a file. + + :param _pytest.config.Config config: + A pytest config. + See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. + :param py.path.local arg: + Path to the file. + """ + session = Session.from_config(config) + assert "::" not in str(arg) + p = py.path.local(arg) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([str(p)], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def getpathnode(self, path: Union[str, "os.PathLike[str]"]): + """Return the collection node of a file. + + This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to + create the (configured) pytest Config instance. + + :param py.path.local path: Path to the file. + """ + path = py.path.local(path) + config = self.parseconfigure(path) + session = Session.from_config(config) + x = session.fspath.bestrelpath(path) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([x], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def genitems(self, colitems: Sequence[Union[Item, Collector]]) -> List[Item]: + """Generate all test items from a collection node. + + This recurses into the collection node and returns a list of all the + test items contained within. + """ + session = colitems[0].session + result: List[Item] = [] + for colitem in colitems: + result.extend(session.genitems(colitem)) + return result + + def runitem(self, source: str) -> Any: + """Run the "test_func" Item. + + The calling test instance (class containing the test method) must + provide a ``.getrunner()`` method which should return a runner which + can run the test protocol for a single item, e.g. + :py:func:`_pytest.runner.runtestprotocol`. + """ + # used from runner functional tests + item = self.getitem(source) + # the test class where we are called from wants to provide the runner + testclassinstance = self._request.instance + runner = testclassinstance.getrunner() + return runner(item) + + def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: + """Run a test module in process using ``pytest.main()``. + + This run writes "source" into a temporary file and runs + ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance + for the result. + + :param source: The source code of the test module. + + :param cmdlineargs: Any extra command line arguments to use. + + :returns: :py:class:`HookRecorder` instance of the result. + """ + p = self.makepyfile(source) + values = list(cmdlineargs) + [p] + return self.inline_run(*values) + + def inline_genitems(self, *args) -> Tuple[List[Item], HookRecorder]: + """Run ``pytest.main(['--collectonly'])`` in-process. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself like :py:meth:`inline_run`, but returns a + tuple of the collected items and a :py:class:`HookRecorder` instance. + """ + rec = self.inline_run("--collect-only", *args) + items = [x.item for x in rec.getcalls("pytest_itemcollected")] + return items, rec + + def inline_run( + self, + *args: Union[str, "os.PathLike[str]"], + plugins=(), + no_reraise_ctrlc: bool = False, + ) -> HookRecorder: + """Run ``pytest.main()`` in-process, returning a HookRecorder. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself. This means it can return a + :py:class:`HookRecorder` instance which gives more detailed results + from that run than can be done by matching stdout/stderr from + :py:meth:`runpytest`. + + :param args: + Command line arguments to pass to :py:func:`pytest.main`. + :param plugins: + Extra plugin instances the ``pytest.main()`` instance should use. + :param no_reraise_ctrlc: + Typically we reraise keyboard interrupts from the child run. If + True, the KeyboardInterrupt exception is captured. + + :returns: A :py:class:`HookRecorder` instance. + """ + # (maybe a cpython bug?) the importlib cache sometimes isn't updated + # properly between file creation and inline_run (especially if imports + # are interspersed with file creation) + importlib.invalidate_caches() + + plugins = list(plugins) + finalizers = [] + try: + # Any sys.module or sys.path changes done while running pytest + # inline should be reverted after the test run completes to avoid + # clashing with later inline tests run within the same pytest test, + # e.g. just because they use matching test module names. + finalizers.append(self.__take_sys_modules_snapshot().restore) + finalizers.append(SysPathsSnapshot().restore) + + # Important note: + # - our tests should not leave any other references/registrations + # laying around other than possibly loaded test modules + # referenced from sys.modules, as nothing will clean those up + # automatically + + rec = [] + + class Collect: + def pytest_configure(x, config: Config) -> None: + rec.append(self.make_hook_recorder(config.pluginmanager)) + + plugins.append(Collect()) + ret = main([str(x) for x in args], plugins=plugins) + if len(rec) == 1: + reprec = rec.pop() + else: + + class reprec: # type: ignore + pass + + reprec.ret = ret # type: ignore + + # Typically we reraise keyboard interrupts from the child run + # because it's our user requesting interruption of the testing. + if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: + calls = reprec.getcalls("pytest_keyboard_interrupt") + if calls and calls[-1].excinfo.type == KeyboardInterrupt: + raise KeyboardInterrupt() + return reprec + finally: + for finalizer in finalizers: + finalizer() + + def runpytest_inprocess( + self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any + ) -> RunResult: + """Return result of running pytest in-process, providing a similar + interface to what self.runpytest() provides.""" + syspathinsert = kwargs.pop("syspathinsert", False) + + if syspathinsert: + self.syspathinsert() + now = timing.time() + capture = _get_multicapture("sys") + capture.start_capturing() + try: + try: + reprec = self.inline_run(*args, **kwargs) + except SystemExit as e: + ret = e.args[0] + try: + ret = ExitCode(e.args[0]) + except ValueError: + pass + + class reprec: # type: ignore + ret = ret + + except Exception: + traceback.print_exc() + + class reprec: # type: ignore + ret = ExitCode(3) + + finally: + out, err = capture.readouterr() + capture.stop_capturing() + sys.stdout.write(out) + sys.stderr.write(err) + + assert reprec.ret is not None + res = RunResult( + reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now + ) + res.reprec = reprec # type: ignore + return res + + def runpytest( + self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any + ) -> RunResult: + """Run pytest inline or in a subprocess, depending on the command line + option "--runpytest" and return a :py:class:`RunResult`.""" + new_args = self._ensure_basetemp(args) + if self._method == "inprocess": + return self.runpytest_inprocess(*new_args, **kwargs) + elif self._method == "subprocess": + return self.runpytest_subprocess(*new_args, **kwargs) + raise RuntimeError(f"Unrecognized runpytest option: {self._method}") + + def _ensure_basetemp( + self, args: Sequence[Union[str, "os.PathLike[str]"]] + ) -> List[Union[str, "os.PathLike[str]"]]: + new_args = list(args) + for x in new_args: + if str(x).startswith("--basetemp"): + break + else: + new_args.append("--basetemp=%s" % self.path.parent.joinpath("basetemp")) + return new_args + + def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + """Return a new pytest Config instance from given commandline args. + + This invokes the pytest bootstrapping code in _pytest.config to create + a new :py:class:`_pytest.core.PluginManager` and call the + pytest_cmdline_parse hook to create a new + :py:class:`_pytest.config.Config` instance. + + If :py:attr:`plugins` has been populated they should be plugin modules + to be registered with the PluginManager. + """ + import _pytest.config + + new_args = self._ensure_basetemp(args) + new_args = [str(x) for x in new_args] + + config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] + # we don't know what the test will do with this half-setup config + # object and thus we make sure it gets unconfigured properly in any + # case (otherwise capturing could still be active, for example) + self._request.addfinalizer(config._ensure_unconfigure) + return config + + def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + """Return a new pytest configured Config instance. + + Returns a new :py:class:`_pytest.config.Config` instance like + :py:meth:`parseconfig`, but also calls the pytest_configure hook. + """ + config = self.parseconfig(*args) + config._do_configure() + return config + + def getitem(self, source: str, funcname: str = "test_func") -> Item: + """Return the test item for a test function. + + Writes the source to a python file and runs pytest's collection on + the resulting module, returning the test item for the requested + function name. + + :param source: + The module source. + :param funcname: + The name of the test function for which to return a test item. + """ + items = self.getitems(source) + for item in items: + if item.name == funcname: + return item + assert 0, "{!r} item not found in module:\n{}\nitems: {}".format( + funcname, source, items + ) + + def getitems(self, source: str) -> List[Item]: + """Return all test items collected from the module. + + Writes the source to a Python file and runs pytest's collection on + the resulting module, returning all test items contained within. + """ + modcol = self.getmodulecol(source) + return self.genitems([modcol]) + + def getmodulecol( + self, source: Union[str, Path], configargs=(), *, withinit: bool = False + ): + """Return the module collection node for ``source``. + + Writes ``source`` to a file using :py:meth:`makepyfile` and then + runs the pytest collection on it, returning the collection node for the + test module. + + :param source: + The source code of the module to collect. + + :param configargs: + Any extra arguments to pass to :py:meth:`parseconfigure`. + + :param withinit: + Whether to also write an ``__init__.py`` file to the same + directory to ensure it is a package. + """ + if isinstance(source, Path): + path = self.path.joinpath(source) + assert not withinit, "not supported for paths" + else: + kw = {self._name: str(source)} + path = self.makepyfile(**kw) + if withinit: + self.makepyfile(__init__="#") + self.config = config = self.parseconfigure(path, *configargs) + return self.getnode(config, path) + + def collect_by_name( + self, modcol: Collector, name: str + ) -> Optional[Union[Item, Collector]]: + """Return the collection node for name from the module collection. + + Searchs a module collection node for a collection node matching the + given name. + + :param modcol: A module collection node; see :py:meth:`getmodulecol`. + :param name: The name of the node to return. + """ + if modcol not in self._mod_collections: + self._mod_collections[modcol] = list(modcol.collect()) + for colitem in self._mod_collections[modcol]: + if colitem.name == name: + return colitem + return None + + def popen( + self, + cmdargs, + stdout: Union[int, TextIO] = subprocess.PIPE, + stderr: Union[int, TextIO] = subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """Invoke subprocess.Popen. + + Calls subprocess.Popen making sure the current working directory is + in the PYTHONPATH. + + You probably want to use :py:meth:`run` instead. + """ + env = os.environ.copy() + env["PYTHONPATH"] = os.pathsep.join( + filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) + ) + kw["env"] = env + + if stdin is self.CLOSE_STDIN: + kw["stdin"] = subprocess.PIPE + elif isinstance(stdin, bytes): + kw["stdin"] = subprocess.PIPE + else: + kw["stdin"] = stdin + + popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) + if stdin is self.CLOSE_STDIN: + assert popen.stdin is not None + popen.stdin.close() + elif isinstance(stdin, bytes): + assert popen.stdin is not None + popen.stdin.write(stdin) + + return popen + + def run( + self, + *cmdargs: Union[str, "os.PathLike[str]"], + timeout: Optional[float] = None, + stdin=CLOSE_STDIN, + ) -> RunResult: + """Run a command with arguments. + + Run a process using subprocess.Popen saving the stdout and stderr. + + :param cmdargs: + The sequence of arguments to pass to `subprocess.Popen()`, with path-like objects + being converted to ``str`` automatically. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :param stdin: + Optional standard input. Bytes are being send, closing + the pipe, otherwise it is passed through to ``popen``. + Defaults to ``CLOSE_STDIN``, which translates to using a pipe + (``subprocess.PIPE``) that gets closed. + + :rtype: RunResult + """ + __tracebackhide__ = True + + # TODO: Remove type ignore in next mypy release. + # https://github.com/python/typeshed/pull/4582 + cmdargs = tuple( + os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs # type: ignore[misc] + ) + p1 = self.path.joinpath("stdout") + p2 = self.path.joinpath("stderr") + print("running:", *cmdargs) + print(" in:", Path.cwd()) + + with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: + now = timing.time() + popen = self.popen( + cmdargs, + stdin=stdin, + stdout=f1, + stderr=f2, + close_fds=(sys.platform != "win32"), + ) + if popen.stdin is not None: + popen.stdin.close() + + def handle_timeout() -> None: + __tracebackhide__ = True + + timeout_message = ( + "{seconds} second timeout expired running:" + " {command}".format(seconds=timeout, command=cmdargs) + ) + + popen.kill() + popen.wait() + raise self.TimeoutExpired(timeout_message) + + if timeout is None: + ret = popen.wait() + else: + try: + ret = popen.wait(timeout) + except subprocess.TimeoutExpired: + handle_timeout() + + with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: + out = f1.read().splitlines() + err = f2.read().splitlines() + + self._dump_lines(out, sys.stdout) + self._dump_lines(err, sys.stderr) + + with contextlib.suppress(ValueError): + ret = ExitCode(ret) + return RunResult(ret, out, err, timing.time() - now) + + def _dump_lines(self, lines, fp): + try: + for line in lines: + print(line, file=fp) + except UnicodeEncodeError: + print(f"couldn't print to {fp} because of encoding") + + def _getpytestargs(self) -> Tuple[str, ...]: + return sys.executable, "-mpytest" + + def runpython(self, script) -> RunResult: + """Run a python script using sys.executable as interpreter. + + :rtype: RunResult + """ + return self.run(sys.executable, script) + + def runpython_c(self, command): + """Run python -c "command". + + :rtype: RunResult + """ + return self.run(sys.executable, "-c", command) + + def runpytest_subprocess(self, *args, timeout: Optional[float] = None) -> RunResult: + """Run pytest as a subprocess with given arguments. + + Any plugins added to the :py:attr:`plugins` list will be added using the + ``-p`` command line option. Additionally ``--basetemp`` is used to put + any temporary files and directories in a numbered directory prefixed + with "runpytest-" to not conflict with the normal numbered pytest + location for temporary files and directories. + + :param args: + The sequence of arguments to pass to the pytest subprocess. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + + :rtype: RunResult + """ + __tracebackhide__ = True + p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) + args = ("--basetemp=%s" % p,) + args + plugins = [x for x in self.plugins if isinstance(x, str)] + if plugins: + args = ("-p", plugins[0]) + args + args = self._getpytestargs() + args + return self.run(*args, timeout=timeout) + + def spawn_pytest( + self, string: str, expect_timeout: float = 10.0 + ) -> "pexpect.spawn": + """Run pytest using pexpect. + + This makes sure to use the right pytest and sets up the temporary + directory locations. + + The pexpect child is returned. + """ + basetemp = self.path / "temp-pexpect" + basetemp.mkdir(mode=0o700) + invoke = " ".join(map(str, self._getpytestargs())) + cmd = f"{invoke} --basetemp={basetemp} {string}" + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": + """Run a command using pexpect. + + The pexpect child is returned. + """ + pexpect = importorskip("pexpect", "3.0") + if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): + skip("pypy-64 bit not supported") + if not hasattr(pexpect, "spawn"): + skip("pexpect.spawn not available") + logfile = self.path.joinpath("spawn.out").open("wb") + + child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) + self._request.addfinalizer(logfile.close) + return child + + +class LineComp: + def __init__(self) -> None: + self.stringio = StringIO() + """:class:`python:io.StringIO()` instance used for input.""" + + def assert_contains_lines(self, lines2: Sequence[str]) -> None: + """Assert that ``lines2`` are contained (linearly) in :attr:`stringio`'s value. + + Lines are matched using :func:`LineMatcher.fnmatch_lines`. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) + self.stringio.seek(0) + lines1 = val.split("\n") + LineMatcher(lines1).fnmatch_lines(lines2) + + +@final +@attr.s(repr=False, str=False, init=False) +class Testdir: + """ + Similar to :class:`Pytester`, but this class works with legacy py.path.local objects instead. + + All methods just forward to an internal :class:`Pytester` instance, converting results + to `py.path.local` objects as necessary. + """ + + __test__ = False + + CLOSE_STDIN = Pytester.CLOSE_STDIN + TimeoutExpired = Pytester.TimeoutExpired + Session = Pytester.Session + + def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pytester = pytester + + @property + def tmpdir(self) -> py.path.local: + """Temporary directory where tests are executed.""" + return py.path.local(self._pytester.path) + + @property + def test_tmproot(self) -> py.path.local: + return py.path.local(self._pytester._test_tmproot) + + @property + def request(self): + return self._pytester._request + + @property + def plugins(self): + return self._pytester.plugins + + @plugins.setter + def plugins(self, plugins): + self._pytester.plugins = plugins + + @property + def monkeypatch(self) -> MonkeyPatch: + return self._pytester._monkeypatch + + def make_hook_recorder(self, pluginmanager) -> HookRecorder: + """See :meth:`Pytester.make_hook_recorder`.""" + return self._pytester.make_hook_recorder(pluginmanager) + + def chdir(self) -> None: + """See :meth:`Pytester.chdir`.""" + return self._pytester.chdir() + + def finalize(self) -> None: + """See :meth:`Pytester._finalize`.""" + return self._pytester._finalize() + + def makefile(self, ext, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.makefile`.""" + return py.path.local(str(self._pytester.makefile(ext, *args, **kwargs))) + + def makeconftest(self, source) -> py.path.local: + """See :meth:`Pytester.makeconftest`.""" + return py.path.local(str(self._pytester.makeconftest(source))) + + def makeini(self, source) -> py.path.local: + """See :meth:`Pytester.makeini`.""" + return py.path.local(str(self._pytester.makeini(source))) + + def getinicfg(self, source: str) -> SectionWrapper: + """See :meth:`Pytester.getinicfg`.""" + return self._pytester.getinicfg(source) + + def makepyprojecttoml(self, source) -> py.path.local: + """See :meth:`Pytester.makepyprojecttoml`.""" + return py.path.local(str(self._pytester.makepyprojecttoml(source))) + + def makepyfile(self, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.makepyfile`.""" + return py.path.local(str(self._pytester.makepyfile(*args, **kwargs))) + + def maketxtfile(self, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.maketxtfile`.""" + return py.path.local(str(self._pytester.maketxtfile(*args, **kwargs))) + + def syspathinsert(self, path=None) -> None: + """See :meth:`Pytester.syspathinsert`.""" + return self._pytester.syspathinsert(path) + + def mkdir(self, name) -> py.path.local: + """See :meth:`Pytester.mkdir`.""" + return py.path.local(str(self._pytester.mkdir(name))) + + def mkpydir(self, name) -> py.path.local: + """See :meth:`Pytester.mkpydir`.""" + return py.path.local(str(self._pytester.mkpydir(name))) + + def copy_example(self, name=None) -> py.path.local: + """See :meth:`Pytester.copy_example`.""" + return py.path.local(str(self._pytester.copy_example(name))) + + def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: + """See :meth:`Pytester.getnode`.""" + return self._pytester.getnode(config, arg) + + def getpathnode(self, path): + """See :meth:`Pytester.getpathnode`.""" + return self._pytester.getpathnode(path) + + def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]: + """See :meth:`Pytester.genitems`.""" + return self._pytester.genitems(colitems) + + def runitem(self, source): + """See :meth:`Pytester.runitem`.""" + return self._pytester.runitem(source) + + def inline_runsource(self, source, *cmdlineargs): + """See :meth:`Pytester.inline_runsource`.""" + return self._pytester.inline_runsource(source, *cmdlineargs) + + def inline_genitems(self, *args): + """See :meth:`Pytester.inline_genitems`.""" + return self._pytester.inline_genitems(*args) + + def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): + """See :meth:`Pytester.inline_run`.""" + return self._pytester.inline_run( + *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc + ) + + def runpytest_inprocess(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest_inprocess`.""" + return self._pytester.runpytest_inprocess(*args, **kwargs) + + def runpytest(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest`.""" + return self._pytester.runpytest(*args, **kwargs) + + def parseconfig(self, *args) -> Config: + """See :meth:`Pytester.parseconfig`.""" + return self._pytester.parseconfig(*args) + + def parseconfigure(self, *args) -> Config: + """See :meth:`Pytester.parseconfigure`.""" + return self._pytester.parseconfigure(*args) + + def getitem(self, source, funcname="test_func"): + """See :meth:`Pytester.getitem`.""" + return self._pytester.getitem(source, funcname) + + def getitems(self, source): + """See :meth:`Pytester.getitems`.""" + return self._pytester.getitems(source) + + def getmodulecol(self, source, configargs=(), withinit=False): + """See :meth:`Pytester.getmodulecol`.""" + return self._pytester.getmodulecol( + source, configargs=configargs, withinit=withinit + ) + + def collect_by_name( + self, modcol: Collector, name: str + ) -> Optional[Union[Item, Collector]]: + """See :meth:`Pytester.collect_by_name`.""" + return self._pytester.collect_by_name(modcol, name) + + def popen( + self, + cmdargs, + stdout: Union[int, TextIO] = subprocess.PIPE, + stderr: Union[int, TextIO] = subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """See :meth:`Pytester.popen`.""" + return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) + + def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: + """See :meth:`Pytester.run`.""" + return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) + + def runpython(self, script) -> RunResult: + """See :meth:`Pytester.runpython`.""" + return self._pytester.runpython(script) + + def runpython_c(self, command): + """See :meth:`Pytester.runpython_c`.""" + return self._pytester.runpython_c(command) + + def runpytest_subprocess(self, *args, timeout=None) -> RunResult: + """See :meth:`Pytester.runpytest_subprocess`.""" + return self._pytester.runpytest_subprocess(*args, timeout=timeout) + + def spawn_pytest( + self, string: str, expect_timeout: float = 10.0 + ) -> "pexpect.spawn": + """See :meth:`Pytester.spawn_pytest`.""" + return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": + """See :meth:`Pytester.spawn`.""" + return self._pytester.spawn(cmd, expect_timeout=expect_timeout) + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return str(self.tmpdir) + + +class LineMatcher: + """Flexible matching of text. + + This is a convenience class to test large texts like the output of + commands. + + The constructor takes a list of lines without their trailing newlines, i.e. + ``text.splitlines()``. + """ + + def __init__(self, lines: List[str]) -> None: + self.lines = lines + self._log_output: List[str] = [] + + def __str__(self) -> str: + """Return the entire original text. + + .. versionadded:: 6.2 + You can use :meth:`str` in older versions. + """ + return "\n".join(self.lines) + + def _getlines(self, lines2: Union[str, Sequence[str], Source]) -> Sequence[str]: + if isinstance(lines2, str): + lines2 = Source(lines2) + if isinstance(lines2, Source): + lines2 = lines2.strip().lines + return lines2 + + def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, fnmatch) + + def re_match_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:re.match`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) + + def _match_lines_random( + self, lines2: Sequence[str], match_func: Callable[[str, str], bool] + ) -> None: + __tracebackhide__ = True + lines2 = self._getlines(lines2) + for line in lines2: + for x in self.lines: + if line == x or match_func(x, line): + self._log("matched: ", repr(line)) + break + else: + msg = "line %r not found in output" % line + self._log(msg) + self._fail(msg) + + def get_lines_after(self, fnline: str) -> Sequence[str]: + """Return all lines following the given line in the text. + + The given line can contain glob wildcards. + """ + for i, line in enumerate(self.lines): + if fnline == line or fnmatch(line, fnline): + return self.lines[i + 1 :] + raise ValueError("line %r not found in output" % fnline) + + def _log(self, *args) -> None: + self._log_output.append(" ".join(str(x) for x in args)) + + @property + def _log_text(self) -> str: + return "\n".join(self._log_output) + + def fnmatch_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). + + The argument is a list of lines which have to match and can use glob + wildcards. If they do not match a pytest.fail() is called. The + matches and non-matches are also shown as part of the error message. + + :param lines2: String patterns to match. + :param consecutive: Match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) + + def re_match_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:re.match`). + + The argument is a list of lines which have to match using ``re.match``. + If they do not match a pytest.fail() is called. + + The matches and non-matches are also shown as part of the error message. + + :param lines2: string patterns to match. + :param consecutive: match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines( + lines2, + lambda name, pat: bool(re.match(pat, name)), + "re.match", + consecutive=consecutive, + ) + + def _match_lines( + self, + lines2: Sequence[str], + match_func: Callable[[str, str], bool], + match_nickname: str, + *, + consecutive: bool = False, + ) -> None: + """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``. + + :param Sequence[str] lines2: + List of string patterns to match. The actual format depends on + ``match_func``. + :param match_func: + A callable ``match_func(line, pattern)`` where line is the + captured line from stdout/stderr and pattern is the matching + pattern. + :param str match_nickname: + The nickname for the match function that will be logged to stdout + when a match occurs. + :param consecutive: + Match lines consecutively? + """ + if not isinstance(lines2, collections.abc.Sequence): + raise TypeError("invalid type for lines2: {}".format(type(lines2).__name__)) + lines2 = self._getlines(lines2) + lines1 = self.lines[:] + extralines = [] + __tracebackhide__ = True + wnick = len(match_nickname) + 1 + started = False + for line in lines2: + nomatchprinted = False + while lines1: + nextline = lines1.pop(0) + if line == nextline: + self._log("exact match:", repr(line)) + started = True + break + elif match_func(nextline, line): + self._log("%s:" % match_nickname, repr(line)) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + started = True + break + else: + if consecutive and started: + msg = f"no consecutive match: {line!r}" + self._log(msg) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + self._fail(msg) + if not nomatchprinted: + self._log( + "{:>{width}}".format("nomatch:", width=wnick), repr(line) + ) + nomatchprinted = True + self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) + extralines.append(nextline) + else: + msg = f"remains unmatched: {line!r}" + self._log(msg) + self._fail(msg) + self._log_output = [] + + def no_fnmatch_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + self._no_match_line(pat, fnmatch, "fnmatch") + + def no_re_match_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``re.match``. + + :param str pat: The regular expression to match lines. + """ + __tracebackhide__ = True + self._no_match_line( + pat, lambda name, pat: bool(re.match(pat, name)), "re.match" + ) + + def _no_match_line( + self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str + ) -> None: + """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + nomatch_printed = False + wnick = len(match_nickname) + 1 + for line in self.lines: + if match_func(line, pat): + msg = f"{match_nickname}: {pat!r}" + self._log(msg) + self._log("{:>{width}}".format("with:", width=wnick), repr(line)) + self._fail(msg) + else: + if not nomatch_printed: + self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) + nomatch_printed = True + self._log("{:>{width}}".format("and:", width=wnick), repr(line)) + self._log_output = [] + + def _fail(self, msg: str) -> None: + __tracebackhide__ = True + log_text = self._log_text + self._log_output = [] + fail(log_text) + + def str(self) -> str: + """Return the entire original text.""" + return str(self) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester_assertions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester_assertions.py new file mode 100644 index 0000000..630c1d3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/pytester_assertions.py @@ -0,0 +1,66 @@ +"""Helper plugin for pytester; should not be loaded on its own.""" +# This plugin contains assertions used by pytester. pytester cannot +# contain them itself, since it is imported by the `pytest` module, +# hence cannot be subject to assertion rewriting, which requires a +# module to not be already imported. +from typing import Dict +from typing import Sequence +from typing import Tuple +from typing import Union + +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +def assertoutcome( + outcomes: Tuple[ + Sequence[TestReport], + Sequence[Union[CollectReport, TestReport]], + Sequence[Union[CollectReport, TestReport]], + ], + passed: int = 0, + skipped: int = 0, + failed: int = 0, +) -> None: + __tracebackhide__ = True + + realpassed, realskipped, realfailed = outcomes + obtained = { + "passed": len(realpassed), + "skipped": len(realskipped), + "failed": len(realfailed), + } + expected = {"passed": passed, "skipped": skipped, "failed": failed} + assert obtained == expected, outcomes + + +def assert_outcomes( + outcomes: Dict[str, int], + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, +) -> None: + """Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + __tracebackhide__ = True + + obtained = { + "passed": outcomes.get("passed", 0), + "skipped": outcomes.get("skipped", 0), + "failed": outcomes.get("failed", 0), + "errors": outcomes.get("errors", 0), + "xpassed": outcomes.get("xpassed", 0), + "xfailed": outcomes.get("xfailed", 0), + } + expected = { + "passed": passed, + "skipped": skipped, + "failed": failed, + "errors": errors, + "xpassed": xpassed, + "xfailed": xfailed, + } + assert obtained == expected diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python.py new file mode 100644 index 0000000..e48e753 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python.py @@ -0,0 +1,1689 @@ +"""Python test discovery, setup and run of test functions.""" +import enum +import fnmatch +import inspect +import itertools +import os +import sys +import types +import warnings +from collections import Counter +from collections import defaultdict +from functools import partial +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import py + +import _pytest +from _pytest import fixtures +from _pytest import nodes +from _pytest._code import filter_traceback +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest._io.saferepr import saferepr +from _pytest.compat import ascii_escaped +from _pytest.compat import final +from _pytest.compat import get_default_arg_names +from _pytest.compat import get_real_func +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import is_async_function +from _pytest.compat import is_generator +from _pytest.compat import NOTSET +from _pytest.compat import REGEX_TYPE +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.compat import STRING_TYPES +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH +from _pytest.fixtures import FuncFixtureInfo +from _pytest.main import Session +from _pytest.mark import MARK_GEN +from _pytest.mark import ParameterSet +from _pytest.mark.structures import get_unpacked_marks +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import normalize_mark_list +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportPathMismatchError +from _pytest.pathlib import parts +from _pytest.pathlib import visit +from _pytest.warning_types import PytestCollectionWarning +from _pytest.warning_types import PytestUnhandledCoroutineWarning + +if TYPE_CHECKING: + from typing_extensions import Literal + from _pytest.fixtures import _Scope + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--fixtures", + "--funcargs", + action="store_true", + dest="showfixtures", + default=False, + help="show available fixtures, sorted by plugin appearance " + "(fixtures with leading '_' are only shown with '-v')", + ) + group.addoption( + "--fixtures-per-test", + action="store_true", + dest="show_fixtures_per_test", + default=False, + help="show fixtures per test", + ) + parser.addini( + "python_files", + type="args", + # NOTE: default is also used in AssertionRewritingHook. + default=["test_*.py", "*_test.py"], + help="glob-style file patterns for Python test module discovery", + ) + parser.addini( + "python_classes", + type="args", + default=["Test"], + help="prefixes or glob names for Python test class discovery", + ) + parser.addini( + "python_functions", + type="args", + default=["test"], + help="prefixes or glob names for Python test function and method discovery", + ) + parser.addini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", + type="bool", + default=False, + help="disable string escape non-ascii characters, might cause unwanted " + "side effects(use at your own risk)", + ) + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.showfixtures: + showfixtures(config) + return 0 + if config.option.show_fixtures_per_test: + show_fixtures_per_test(config) + return 0 + return None + + +def pytest_generate_tests(metafunc: "Metafunc") -> None: + for marker in metafunc.definition.iter_markers(name="parametrize"): + # TODO: Fix this type-ignore (overlapping kwargs). + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) # type: ignore[misc] + + +def pytest_configure(config: Config) -> None: + config.addinivalue_line( + "markers", + "parametrize(argnames, argvalues): call a test function multiple " + "times passing in different arguments in turn. argvalues generally " + "needs to be a list of values if argnames specifies only one name " + "or a list of tuples of values if argnames specifies multiple names. " + "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " + "decorated test function, one with arg1=1 and another with arg1=2." + "see https://docs.pytest.org/en/stable/parametrize.html for more info " + "and examples.", + ) + config.addinivalue_line( + "markers", + "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " + "all of the specified fixtures. see " + "https://docs.pytest.org/en/stable/fixture.html#usefixtures ", + ) + + +def async_warn_and_skip(nodeid: str) -> None: + msg = "async def functions are not natively supported and have been skipped.\n" + msg += ( + "You need to install a suitable plugin for your async framework, for example:\n" + ) + msg += " - anyio\n" + msg += " - pytest-asyncio\n" + msg += " - pytest-tornasync\n" + msg += " - pytest-trio\n" + msg += " - pytest-twisted" + warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid))) + skip(msg="async def function and no async plugin installed (see warnings)") + + +@hookimpl(trylast=True) +def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: + testfunction = pyfuncitem.obj + if is_async_function(testfunction): + async_warn_and_skip(pyfuncitem.nodeid) + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + result = testfunction(**testargs) + if hasattr(result, "__await__") or hasattr(result, "__aiter__"): + async_warn_and_skip(pyfuncitem.nodeid) + return True + + +def pytest_collect_file( + path: py.path.local, parent: nodes.Collector +) -> Optional["Module"]: + ext = path.ext + if ext == ".py": + if not parent.session.isinitpath(path): + if not path_matches_patterns( + path, parent.config.getini("python_files") + ["__init__.py"] + ): + return None + ihook = parent.session.gethookproxy(path) + module: Module = ihook.pytest_pycollect_makemodule(path=path, parent=parent) + return module + return None + + +def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool: + """Return whether path matches any of the patterns in the list of globs given.""" + return any(path.fnmatch(pattern) for pattern in patterns) + + +def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module": + if path.basename == "__init__.py": + pkg: Package = Package.from_parent(parent, fspath=path) + return pkg + mod: Module = Module.from_parent(parent, fspath=path) + return mod + + +@hookimpl(trylast=True) +def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object): + # Nothing was collected elsewhere, let's do it here. + if safe_isclass(obj): + if collector.istestclass(obj, name): + return Class.from_parent(collector, name=name, obj=obj) + elif collector.istestfunction(obj, name): + # mock seems to store unbound methods (issue473), normalize it. + obj = getattr(obj, "__func__", obj) + # We need to try and unwrap the function if it's a functools.partial + # or a functools.wrapped. + # We mustn't if it's been wrapped with mock.patch (python 2 only). + if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))): + filename, lineno = getfslineno(obj) + warnings.warn_explicit( + message=PytestCollectionWarning( + "cannot collect %r because it is not a function." % name + ), + category=None, + filename=str(filename), + lineno=lineno + 1, + ) + elif getattr(obj, "__test__", True): + if is_generator(obj): + res = Function.from_parent(collector, name=name) + reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format( + name=name + ) + res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) + res.warn(PytestCollectionWarning(reason)) + else: + res = list(collector._genfunctions(name, obj)) + return res + + +class PyobjMixin: + _ALLOW_MARKERS = True + + # Function and attributes that the mixin needs (for type-checking only). + if TYPE_CHECKING: + name: str = "" + parent: Optional[nodes.Node] = None + own_markers: List[Mark] = [] + + def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]: + ... + + def listchain(self) -> List[nodes.Node]: + ... + + @property + def module(self): + """Python module object this node was collected from (can be None).""" + node = self.getparent(Module) + return node.obj if node is not None else None + + @property + def cls(self): + """Python class object this node was collected from (can be None).""" + node = self.getparent(Class) + return node.obj if node is not None else None + + @property + def instance(self): + """Python instance object this node was collected from (can be None).""" + node = self.getparent(Instance) + return node.obj if node is not None else None + + @property + def obj(self): + """Underlying Python object.""" + obj = getattr(self, "_obj", None) + if obj is None: + self._obj = obj = self._getobj() + # XXX evil hack + # used to avoid Instance collector marker duplication + if self._ALLOW_MARKERS: + self.own_markers.extend(get_unpacked_marks(self.obj)) + return obj + + @obj.setter + def obj(self, value): + self._obj = value + + def _getobj(self): + """Get the underlying Python object. May be overwritten by subclasses.""" + # TODO: Improve the type of `parent` such that assert/ignore aren't needed. + assert self.parent is not None + obj = self.parent.obj # type: ignore[attr-defined] + return getattr(obj, self.name) + + def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str: + """Return Python path relative to the containing module.""" + chain = self.listchain() + chain.reverse() + parts = [] + for node in chain: + if isinstance(node, Instance): + continue + name = node.name + if isinstance(node, Module): + name = os.path.splitext(name)[0] + if stopatmodule: + if includemodule: + parts.append(name) + break + parts.append(name) + parts.reverse() + return ".".join(parts) + + def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]: + # XXX caching? + obj = self.obj + compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None) + if isinstance(compat_co_firstlineno, int): + # nose compatibility + file_path = sys.modules[obj.__module__].__file__ + if file_path.endswith(".pyc"): + file_path = file_path[:-1] + fspath: Union[py.path.local, str] = file_path + lineno = compat_co_firstlineno + else: + fspath, lineno = getfslineno(obj) + modpath = self.getmodpath() + assert isinstance(lineno, int) + return fspath, lineno, modpath + + +# As an optimization, these builtin attribute names are pre-ignored when +# iterating over an object during collection -- the pytest_pycollect_makeitem +# hook is not called for them. +# fmt: off +class _EmptyClass: pass # noqa: E701 +IGNORED_ATTRIBUTES = frozenset.union( # noqa: E305 + frozenset(), + # Module. + dir(types.ModuleType("empty_module")), + # Some extra module attributes the above doesn't catch. + {"__builtins__", "__file__", "__cached__"}, + # Class. + dir(_EmptyClass), + # Instance. + dir(_EmptyClass()), +) +del _EmptyClass +# fmt: on + + +class PyCollector(PyobjMixin, nodes.Collector): + def funcnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_functions", name) + + def isnosetest(self, obj: object) -> bool: + """Look for the __test__ attribute, which is applied by the + @nose.tools.istest decorator. + """ + # We explicitly check for "is True" here to not mistakenly treat + # classes with a custom __getattr__ returning something truthy (like a + # function) as test classes. + return safe_getattr(obj, "__test__", False) is True + + def classnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_classes", name) + + def istestfunction(self, obj: object, name: str) -> bool: + if self.funcnamefilter(name) or self.isnosetest(obj): + if isinstance(obj, staticmethod): + # staticmethods need to be unwrapped. + obj = safe_getattr(obj, "__func__", False) + return ( + safe_getattr(obj, "__call__", False) + and fixtures.getfixturemarker(obj) is None + ) + else: + return False + + def istestclass(self, obj: object, name: str) -> bool: + return self.classnamefilter(name) or self.isnosetest(obj) + + def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool: + """Check if the given name matches the prefix or glob-pattern defined + in ini configuration.""" + for option in self.config.getini(option_name): + if name.startswith(option): + return True + # Check that name looks like a glob-string before calling fnmatch + # because this is called for every name in each collected module, + # and fnmatch is somewhat expensive to call. + elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( + name, option + ): + return True + return False + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + if not getattr(self.obj, "__test__", True): + return [] + + # NB. we avoid random getattrs and peek in the __dict__ instead + # (XXX originally introduced from a PyPy need, still true?) + dicts = [getattr(self.obj, "__dict__", {})] + for basecls in self.obj.__class__.__mro__: + dicts.append(basecls.__dict__) + seen: Set[str] = set() + values: List[Union[nodes.Item, nodes.Collector]] = [] + ihook = self.ihook + for dic in dicts: + # Note: seems like the dict can change during iteration - + # be careful not to remove the list() without consideration. + for name, obj in list(dic.items()): + if name in IGNORED_ATTRIBUTES: + continue + if name in seen: + continue + seen.add(name) + res = ihook.pytest_pycollect_makeitem( + collector=self, name=name, obj=obj + ) + if res is None: + continue + elif isinstance(res, list): + values.extend(res) + else: + values.append(res) + + def sort_key(item): + fspath, lineno, _ = item.reportinfo() + return (str(fspath), lineno) + + values.sort(key=sort_key) + return values + + def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]: + modulecol = self.getparent(Module) + assert modulecol is not None + module = modulecol.obj + clscol = self.getparent(Class) + cls = clscol and clscol.obj or None + fm = self.session._fixturemanager + + definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) + fixtureinfo = definition._fixtureinfo + + metafunc = Metafunc( + definition, fixtureinfo, self.config, cls=cls, module=module + ) + methods = [] + if hasattr(module, "pytest_generate_tests"): + methods.append(module.pytest_generate_tests) + if cls is not None and hasattr(cls, "pytest_generate_tests"): + methods.append(cls().pytest_generate_tests) + + self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) + + if not metafunc._calls: + yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) + else: + # Add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs. + fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm) + + # Add_funcarg_pseudo_fixture_def may have shadowed some fixtures + # with direct parametrization, so make sure we update what the + # function really needs. + fixtureinfo.prune_dependency_tree() + + for callspec in metafunc._calls: + subname = f"{name}[{callspec.id}]" + yield Function.from_parent( + self, + name=subname, + callspec=callspec, + callobj=funcobj, + fixtureinfo=fixtureinfo, + keywords={callspec.id: True}, + originalname=name, + ) + + +class Module(nodes.File, PyCollector): + """Collector for test classes and functions.""" + + def _getobj(self): + return self._importtestmodule() + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + self._inject_setup_module_fixture() + self._inject_setup_function_fixture() + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def _inject_setup_module_fixture(self) -> None: + """Inject a hidden autouse, module scoped fixture into the collected module object + that invokes setUpModule/tearDownModule if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_module = _get_first_non_fixture_func( + self.obj, ("setUpModule", "setup_module") + ) + teardown_module = _get_first_non_fixture_func( + self.obj, ("tearDownModule", "teardown_module") + ) + + if setup_module is None and teardown_module is None: + return + + @fixtures.fixture( + autouse=True, + scope="module", + # Use a unique name to speed up lookup. + name=f"xunit_setup_module_fixture_{self.obj.__name__}", + ) + def xunit_setup_module_fixture(request) -> Generator[None, None, None]: + if setup_module is not None: + _call_with_optional_argument(setup_module, request.module) + yield + if teardown_module is not None: + _call_with_optional_argument(teardown_module, request.module) + + self.obj.__pytest_setup_module = xunit_setup_module_fixture + + def _inject_setup_function_fixture(self) -> None: + """Inject a hidden autouse, function scoped fixture into the collected module object + that invokes setup_function/teardown_function if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) + teardown_function = _get_first_non_fixture_func( + self.obj, ("teardown_function",) + ) + if setup_function is None and teardown_function is None: + return + + @fixtures.fixture( + autouse=True, + scope="function", + # Use a unique name to speed up lookup. + name=f"xunit_setup_function_fixture_{self.obj.__name__}", + ) + def xunit_setup_function_fixture(request) -> Generator[None, None, None]: + if request.instance is not None: + # in this case we are bound to an instance, so we need to let + # setup_method handle this + yield + return + if setup_function is not None: + _call_with_optional_argument(setup_function, request.function) + yield + if teardown_function is not None: + _call_with_optional_argument(teardown_function, request.function) + + self.obj.__pytest_setup_function = xunit_setup_function_fixture + + def _importtestmodule(self): + # We assume we are only called once per module. + importmode = self.config.getoption("--import-mode") + try: + mod = import_path(self.fspath, mode=importmode) + except SyntaxError as e: + raise self.CollectError( + ExceptionInfo.from_current().getrepr(style="short") + ) from e + except ImportPathMismatchError as e: + raise self.CollectError( + "import file mismatch:\n" + "imported module %r has this __file__ attribute:\n" + " %s\n" + "which is not the same as the test file we want to collect:\n" + " %s\n" + "HINT: remove __pycache__ / .pyc files and/or use a " + "unique basename for your test file modules" % e.args + ) from e + except ImportError as e: + exc_info = ExceptionInfo.from_current() + if self.config.getoption("verbose") < 2: + exc_info.traceback = exc_info.traceback.filter(filter_traceback) + exc_repr = ( + exc_info.getrepr(style="short") + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + raise self.CollectError( + "ImportError while importing test module '{fspath}'.\n" + "Hint: make sure your test modules/packages have valid Python names.\n" + "Traceback:\n" + "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) + ) from e + except skip.Exception as e: + if e.allow_module_level: + raise + raise self.CollectError( + "Using pytest.skip outside of a test is not allowed. " + "To decorate a test function, use the @pytest.mark.skip " + "or @pytest.mark.skipif decorators instead, and to skip a " + "module use `pytestmark = pytest.mark.{skip,skipif}." + ) from e + self.config.pluginmanager.consider_module(mod) + return mod + + +class Package(Module): + def __init__( + self, + fspath: py.path.local, + parent: nodes.Collector, + # NOTE: following args are unused: + config=None, + session=None, + nodeid=None, + ) -> None: + # NOTE: Could be just the following, but kept as-is for compat. + # nodes.FSCollector.__init__(self, fspath, parent=parent) + session = parent.session + nodes.FSCollector.__init__( + self, fspath, parent=parent, config=config, session=session, nodeid=nodeid + ) + self.name = os.path.basename(str(fspath.dirname)) + + def setup(self) -> None: + # Not using fixtures to call setup_module here because autouse fixtures + # from packages are not called automatically (#4085). + setup_module = _get_first_non_fixture_func( + self.obj, ("setUpModule", "setup_module") + ) + if setup_module is not None: + _call_with_optional_argument(setup_module, self.obj) + + teardown_module = _get_first_non_fixture_func( + self.obj, ("tearDownModule", "teardown_module") + ) + if teardown_module is not None: + func = partial(_call_with_optional_argument, teardown_module, self.obj) + self.addfinalizer(func) + + def gethookproxy(self, fspath: py.path.local): + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.gethookproxy(fspath) + + def isinitpath(self, path: py.path.local) -> bool: + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.isinitpath(path) + + def _recurse(self, direntry: "os.DirEntry[str]") -> bool: + if direntry.name == "__pycache__": + return False + path = py.path.local(direntry.path) + ihook = self.session.gethookproxy(path.dirpath()) + if ihook.pytest_ignore_collect(path=path, config=self.config): + return False + norecursepatterns = self.config.getini("norecursedirs") + if any(path.check(fnmatch=pat) for pat in norecursepatterns): + return False + return True + + def _collectfile( + self, path: py.path.local, handle_dupes: bool = True + ) -> Sequence[nodes.Collector]: + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() + ) + ihook = self.session.gethookproxy(path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(path=path, config=self.config): + return () + + if handle_dupes: + keepduplicates = self.config.getoption("keepduplicates") + if not keepduplicates: + duplicate_paths = self.config.pluginmanager._duplicatepaths + if path in duplicate_paths: + return () + else: + duplicate_paths.add(path) + + return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + this_path = self.fspath.dirpath() + init_module = this_path.join("__init__.py") + if init_module.check(file=1) and path_matches_patterns( + init_module, self.config.getini("python_files") + ): + yield Module.from_parent(self, fspath=init_module) + pkg_prefixes: Set[py.path.local] = set() + for direntry in visit(str(this_path), recurse=self._recurse): + path = py.path.local(direntry.path) + + # We will visit our own __init__.py file, in which case we skip it. + if direntry.is_file(): + if direntry.name == "__init__.py" and path.dirpath() == this_path: + continue + + parts_ = parts(direntry.path) + if any( + str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path + for pkg_prefix in pkg_prefixes + ): + continue + + if direntry.is_file(): + yield from self._collectfile(path) + elif not direntry.is_dir(): + # Broken symlink or invalid/missing file. + continue + elif path.join("__init__.py").check(file=1): + pkg_prefixes.add(path) + + +def _call_with_optional_argument(func, arg) -> None: + """Call the given function with the given argument if func accepts one argument, otherwise + calls func without arguments.""" + arg_count = func.__code__.co_argcount + if inspect.ismethod(func): + arg_count -= 1 + if arg_count: + func(arg) + else: + func() + + +def _get_first_non_fixture_func(obj: object, names: Iterable[str]): + """Return the attribute from the given object to be used as a setup/teardown + xunit-style function, but only if not marked as a fixture to avoid calling it twice.""" + for name in names: + meth = getattr(obj, name, None) + if meth is not None and fixtures.getfixturemarker(meth) is None: + return meth + + +class Class(PyCollector): + """Collector for test methods.""" + + @classmethod + def from_parent(cls, parent, *, name, obj=None): + """The public constructor.""" + return super().from_parent(name=name, parent=parent) + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + if not safe_getattr(self.obj, "__test__", True): + return [] + if hasinit(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + "cannot collect test class %r because it has a " + "__init__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) + ) + ) + return [] + elif hasnew(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + "cannot collect test class %r because it has a " + "__new__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) + ) + ) + return [] + + self._inject_setup_class_fixture() + self._inject_setup_method_fixture() + + return [Instance.from_parent(self, name="()")] + + def _inject_setup_class_fixture(self) -> None: + """Inject a hidden autouse, class scoped fixture into the collected class object + that invokes setup_class/teardown_class if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) + teardown_class = getattr(self.obj, "teardown_class", None) + if setup_class is None and teardown_class is None: + return + + @fixtures.fixture( + autouse=True, + scope="class", + # Use a unique name to speed up lookup. + name=f"xunit_setup_class_fixture_{self.obj.__qualname__}", + ) + def xunit_setup_class_fixture(cls) -> Generator[None, None, None]: + if setup_class is not None: + func = getimfunc(setup_class) + _call_with_optional_argument(func, self.obj) + yield + if teardown_class is not None: + func = getimfunc(teardown_class) + _call_with_optional_argument(func, self.obj) + + self.obj.__pytest_setup_class = xunit_setup_class_fixture + + def _inject_setup_method_fixture(self) -> None: + """Inject a hidden autouse, function scoped fixture into the collected class object + that invokes setup_method/teardown_method if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_method = _get_first_non_fixture_func(self.obj, ("setup_method",)) + teardown_method = getattr(self.obj, "teardown_method", None) + if setup_method is None and teardown_method is None: + return + + @fixtures.fixture( + autouse=True, + scope="function", + # Use a unique name to speed up lookup. + name=f"xunit_setup_method_fixture_{self.obj.__qualname__}", + ) + def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]: + method = request.function + if setup_method is not None: + func = getattr(self, "setup_method") + _call_with_optional_argument(func, method) + yield + if teardown_method is not None: + func = getattr(self, "teardown_method") + _call_with_optional_argument(func, method) + + self.obj.__pytest_setup_method = xunit_setup_method_fixture + + +class Instance(PyCollector): + _ALLOW_MARKERS = False # hack, destroy later + # Instances share the object with their parents in a way + # that duplicates markers instances if not taken out + # can be removed at node structure reorganization time. + + def _getobj(self): + # TODO: Improve the type of `parent` such that assert/ignore aren't needed. + assert self.parent is not None + obj = self.parent.obj # type: ignore[attr-defined] + return obj() + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def newinstance(self): + self.obj = self._getobj() + return self.obj + + +def hasinit(obj: object) -> bool: + init: object = getattr(obj, "__init__", None) + if init: + return init != object.__init__ + return False + + +def hasnew(obj: object) -> bool: + new: object = getattr(obj, "__new__", None) + if new: + return new != object.__new__ + return False + + +@final +class CallSpec2: + def __init__(self, metafunc: "Metafunc") -> None: + self.metafunc = metafunc + self.funcargs: Dict[str, object] = {} + self._idlist: List[str] = [] + self.params: Dict[str, object] = {} + # Used for sorting parametrized resources. + self._arg2scopenum: Dict[str, int] = {} + self.marks: List[Mark] = [] + self.indices: Dict[str, int] = {} + + def copy(self) -> "CallSpec2": + cs = CallSpec2(self.metafunc) + cs.funcargs.update(self.funcargs) + cs.params.update(self.params) + cs.marks.extend(self.marks) + cs.indices.update(self.indices) + cs._arg2scopenum.update(self._arg2scopenum) + cs._idlist = list(self._idlist) + return cs + + def _checkargnotcontained(self, arg: str) -> None: + if arg in self.params or arg in self.funcargs: + raise ValueError(f"duplicate {arg!r}") + + def getparam(self, name: str) -> object: + try: + return self.params[name] + except KeyError as e: + raise ValueError(name) from e + + @property + def id(self) -> str: + return "-".join(map(str, self._idlist)) + + def setmulti2( + self, + valtypes: Mapping[str, "Literal['params', 'funcargs']"], + argnames: Sequence[str], + valset: Iterable[object], + id: str, + marks: Iterable[Union[Mark, MarkDecorator]], + scopenum: int, + param_index: int, + ) -> None: + for arg, val in zip(argnames, valset): + self._checkargnotcontained(arg) + valtype_for_arg = valtypes[arg] + if valtype_for_arg == "params": + self.params[arg] = val + elif valtype_for_arg == "funcargs": + self.funcargs[arg] = val + else: # pragma: no cover + assert False, f"Unhandled valtype for arg: {valtype_for_arg}" + self.indices[arg] = param_index + self._arg2scopenum[arg] = scopenum + self._idlist.append(id) + self.marks.extend(normalize_mark_list(marks)) + + +@final +class Metafunc: + """Objects passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook. + + They help to inspect a test function and to generate tests according to + test configuration or values specified in the class or module where a + test function is defined. + """ + + def __init__( + self, + definition: "FunctionDefinition", + fixtureinfo: fixtures.FuncFixtureInfo, + config: Config, + cls=None, + module=None, + ) -> None: + #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. + self.definition = definition + + #: Access to the :class:`_pytest.config.Config` object for the test session. + self.config = config + + #: The module object where the test function is defined in. + self.module = module + + #: Underlying Python test function. + self.function = definition.obj + + #: Set of fixture names required by the test function. + self.fixturenames = fixtureinfo.names_closure + + #: Class object where the test function is defined in or ``None``. + self.cls = cls + + self._calls: List[CallSpec2] = [] + self._arg2fixturedefs = fixtureinfo.name2fixturedefs + + def parametrize( + self, + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], + indirect: Union[bool, Sequence[str]] = False, + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = None, + scope: "Optional[_Scope]" = None, + *, + _param_mark: Optional[Mark] = None, + ) -> None: + """Add new invocations to the underlying test function using the list + of argvalues for the given argnames. Parametrization is performed + during the collection phase. If you need to setup expensive resources + see about setting indirect to do it rather at test setup time. + + :param argnames: + A comma-separated string denoting one or more argument names, or + a list/tuple of argument strings. + + :param argvalues: + The list of argvalues determines how often a test is invoked with + different argument values. + + If only one argname was specified argvalues is a list of values. + If N argnames were specified, argvalues must be a list of + N-tuples, where each tuple-element specifies a value for its + respective argname. + + :param indirect: + A list of arguments' names (subset of argnames) or a boolean. + If True the list contains all names from the argnames. Each + argvalue corresponding to an argname in this list will + be passed as request.param to its respective argname fixture + function so that it can perform more expensive setups during the + setup phase of a test rather than at collection time. + + :param ids: + Sequence of (or generator for) ids for ``argvalues``, + or a callable to return part of the id for each argvalue. + + With sequences (and generators like ``itertools.count()``) the + returned ids should be of type ``string``, ``int``, ``float``, + ``bool``, or ``None``. + They are mapped to the corresponding index in ``argvalues``. + ``None`` means to use the auto-generated id. + + If it is a callable it will be called for each entry in + ``argvalues``, and the return value is used as part of the + auto-generated id for the whole set (where parts are joined with + dashes ("-")). + This is useful to provide more specific ids for certain items, e.g. + dates. Returning ``None`` will use an auto-generated id. + + If no ids are provided they will be generated automatically from + the argvalues. + + :param scope: + If specified it denotes the scope of the parameters. + The scope is used for grouping tests by parameter instances. + It will also override any fixture-function defined scope, allowing + to set a dynamic scope using test context or configuration. + """ + from _pytest.fixtures import scope2index + + argnames, parameters = ParameterSet._for_parametrize( + argnames, + argvalues, + self.function, + self.config, + nodeid=self.definition.nodeid, + ) + del argvalues + + if "request" in argnames: + fail( + "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", + pytrace=False, + ) + + if scope is None: + scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + + self._validate_if_using_arg_names(argnames, indirect) + + arg_values_types = self._resolve_arg_value_types(argnames, indirect) + + # Use any already (possibly) generated ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from: + generated_ids = _param_mark._param_ids_from._param_ids_generated + if generated_ids is not None: + ids = generated_ids + + ids = self._resolve_arg_ids( + argnames, ids, parameters, nodeid=self.definition.nodeid + ) + + # Store used (possibly generated) ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from and generated_ids is None: + object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) + + scopenum = scope2index( + scope, descr=f"parametrize() call in {self.function.__name__}" + ) + + # Create the new calls: if we are parametrize() multiple times (by applying the decorator + # more than once) then we accumulate those calls generating the cartesian product + # of all calls. + newcalls = [] + for callspec in self._calls or [CallSpec2(self)]: + for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)): + newcallspec = callspec.copy() + newcallspec.setmulti2( + arg_values_types, + argnames, + param_set.values, + param_id, + param_set.marks, + scopenum, + param_index, + ) + newcalls.append(newcallspec) + self._calls = newcalls + + def _resolve_arg_ids( + self, + argnames: Sequence[str], + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ], + parameters: Sequence[ParameterSet], + nodeid: str, + ) -> List[str]: + """Resolve the actual ids for the given argnames, based on the ``ids`` parameter given + to ``parametrize``. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param ids: The ids parameter of the parametrized call (see docs). + :param List[ParameterSet] parameters: The list of parameter values, same size as ``argnames``. + :param str str: The nodeid of the item that generated this parametrized call. + :rtype: List[str] + :returns: The list of ids for each argname given. + """ + if ids is None: + idfn = None + ids_ = None + elif callable(ids): + idfn = ids + ids_ = None + else: + idfn = None + ids_ = self._validate_ids(ids, parameters, self.function.__name__) + return idmaker(argnames, parameters, idfn, ids_, self.config, nodeid=nodeid) + + def _validate_ids( + self, + ids: Iterable[Union[None, str, float, int, bool]], + parameters: Sequence[ParameterSet], + func_name: str, + ) -> List[Union[None, str]]: + try: + num_ids = len(ids) # type: ignore[arg-type] + except TypeError: + try: + iter(ids) + except TypeError as e: + raise TypeError("ids must be a callable or an iterable") from e + num_ids = len(parameters) + + # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 + if num_ids != len(parameters) and num_ids != 0: + msg = "In {}: {} parameter sets specified, with different number of ids: {}" + fail(msg.format(func_name, len(parameters), num_ids), pytrace=False) + + new_ids = [] + for idx, id_value in enumerate(itertools.islice(ids, num_ids)): + if id_value is None or isinstance(id_value, str): + new_ids.append(id_value) + elif isinstance(id_value, (float, int, bool)): + new_ids.append(str(id_value)) + else: + msg = ( # type: ignore[unreachable] + "In {}: ids must be list of string/float/int/bool, " + "found: {} (type: {!r}) at index {}" + ) + fail( + msg.format(func_name, saferepr(id_value), type(id_value), idx), + pytrace=False, + ) + return new_ids + + def _resolve_arg_value_types( + self, argnames: Sequence[str], indirect: Union[bool, Sequence[str]], + ) -> Dict[str, "Literal['params', 'funcargs']"]: + """Resolve if each parametrized argument must be considered a + parameter to a fixture or a "funcarg" to the function, based on the + ``indirect`` parameter of the parametrized() call. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. + :rtype: Dict[str, str] + A dict mapping each arg name to either: + * "params" if the argname should be the parameter of a fixture of the same name. + * "funcargs" if the argname should be a parameter to the parametrized test function. + """ + if isinstance(indirect, bool): + valtypes: Dict[str, Literal["params", "funcargs"]] = dict.fromkeys( + argnames, "params" if indirect else "funcargs" + ) + elif isinstance(indirect, Sequence): + valtypes = dict.fromkeys(argnames, "funcargs") + for arg in indirect: + if arg not in argnames: + fail( + "In {}: indirect fixture '{}' doesn't exist".format( + self.function.__name__, arg + ), + pytrace=False, + ) + valtypes[arg] = "params" + else: + fail( + "In {func}: expected Sequence or boolean for indirect, got {type}".format( + type=type(indirect).__name__, func=self.function.__name__ + ), + pytrace=False, + ) + return valtypes + + def _validate_if_using_arg_names( + self, argnames: Sequence[str], indirect: Union[bool, Sequence[str]], + ) -> None: + """Check if all argnames are being used, by default values, or directly/indirectly. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. + :raises ValueError: If validation fails. + """ + default_arg_names = set(get_default_arg_names(self.function)) + func_name = self.function.__name__ + for arg in argnames: + if arg not in self.fixturenames: + if arg in default_arg_names: + fail( + "In {}: function already takes an argument '{}' with a default value".format( + func_name, arg + ), + pytrace=False, + ) + else: + if isinstance(indirect, Sequence): + name = "fixture" if arg in indirect else "argument" + else: + name = "fixture" if indirect else "argument" + fail( + f"In {func_name}: function uses no {name} '{arg}'", + pytrace=False, + ) + + +def _find_parametrized_scope( + argnames: Sequence[str], + arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], + indirect: Union[bool, Sequence[str]], +) -> "fixtures._Scope": + """Find the most appropriate scope for a parametrized call based on its arguments. + + When there's at least one direct argument, always use "function" scope. + + When a test function is parametrized and all its arguments are indirect + (e.g. fixtures), return the most narrow scope based on the fixtures used. + + Related to issue #1832, based on code posted by @Kingdread. + """ + if isinstance(indirect, Sequence): + all_arguments_are_fixtures = len(indirect) == len(argnames) + else: + all_arguments_are_fixtures = bool(indirect) + + if all_arguments_are_fixtures: + fixturedefs = arg2fixturedefs or {} + used_scopes = [ + fixturedef[0].scope + for name, fixturedef in fixturedefs.items() + if name in argnames + ] + if used_scopes: + # Takes the most narrow scope from used fixtures. + for scope in reversed(fixtures.scopes): + if scope in used_scopes: + return scope + + return "function" + + +def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> str: + if config is None: + escape_option = False + else: + escape_option = config.getini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" + ) + # TODO: If escaping is turned off and the user passes bytes, + # will return a bytes. For now we ignore this but the + # code *probably* doesn't handle this case. + return val if escape_option else ascii_escaped(val) # type: ignore + + +def _idval( + val: object, + argname: str, + idx: int, + idfn: Optional[Callable[[Any], Optional[object]]], + nodeid: Optional[str], + config: Optional[Config], +) -> str: + if idfn: + try: + generated_id = idfn(val) + if generated_id is not None: + val = generated_id + except Exception as e: + prefix = f"{nodeid}: " if nodeid is not None else "" + msg = "error raised while trying to determine id of parameter '{}' at position {}" + msg = prefix + msg.format(argname, idx) + raise ValueError(msg) from e + elif config: + hook_id: Optional[str] = config.hook.pytest_make_parametrize_id( + config=config, val=val, argname=argname + ) + if hook_id: + return hook_id + + if isinstance(val, STRING_TYPES): + return _ascii_escaped_by_config(val, config) + elif val is None or isinstance(val, (float, int, bool)): + return str(val) + elif isinstance(val, REGEX_TYPE): + return ascii_escaped(val.pattern) + elif val is NOTSET: + # Fallback to default. Note that NOTSET is an enum.Enum. + pass + elif isinstance(val, enum.Enum): + return str(val) + elif isinstance(getattr(val, "__name__", None), str): + # Name of a class, function, module, etc. + name: str = getattr(val, "__name__") + return name + return str(argname) + str(idx) + + +def _idvalset( + idx: int, + parameterset: ParameterSet, + argnames: Iterable[str], + idfn: Optional[Callable[[Any], Optional[object]]], + ids: Optional[List[Union[None, str]]], + nodeid: Optional[str], + config: Optional[Config], +) -> str: + if parameterset.id is not None: + return parameterset.id + id = None if ids is None or idx >= len(ids) else ids[idx] + if id is None: + this_id = [ + _idval(val, argname, idx, idfn, nodeid=nodeid, config=config) + for val, argname in zip(parameterset.values, argnames) + ] + return "-".join(this_id) + else: + return _ascii_escaped_by_config(id, config) + + +def idmaker( + argnames: Iterable[str], + parametersets: Iterable[ParameterSet], + idfn: Optional[Callable[[Any], Optional[object]]] = None, + ids: Optional[List[Union[None, str]]] = None, + config: Optional[Config] = None, + nodeid: Optional[str] = None, +) -> List[str]: + resolved_ids = [ + _idvalset( + valindex, parameterset, argnames, idfn, ids, config=config, nodeid=nodeid + ) + for valindex, parameterset in enumerate(parametersets) + ] + + # All IDs must be unique! + unique_ids = set(resolved_ids) + if len(unique_ids) != len(resolved_ids): + + # Record the number of occurrences of each test ID. + test_id_counts = Counter(resolved_ids) + + # Map the test ID to its next suffix. + test_id_suffixes: Dict[str, int] = defaultdict(int) + + # Suffix non-unique IDs to make them unique. + for index, test_id in enumerate(resolved_ids): + if test_id_counts[test_id] > 1: + resolved_ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id]) + test_id_suffixes[test_id] += 1 + + return resolved_ids + + +def show_fixtures_per_test(config): + from _pytest.main import wrap_session + + return wrap_session(config, _show_fixtures_per_test) + + +def _show_fixtures_per_test(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + def get_best_relpath(func): + loc = getlocation(func, str(curdir)) + return curdir.bestrelpath(py.path.local(loc)) + + def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None: + argname = fixture_def.argname + if verbose <= 0 and argname.startswith("_"): + return + if verbose > 0: + bestrel = get_best_relpath(fixture_def.func) + funcargspec = f"{argname} -- {bestrel}" + else: + funcargspec = argname + tw.line(funcargspec, green=True) + fixture_doc = inspect.getdoc(fixture_def.func) + if fixture_doc: + write_docstring(tw, fixture_doc) + else: + tw.line(" no docstring available", red=True) + + def write_item(item: nodes.Item) -> None: + # Not all items have _fixtureinfo attribute. + info: Optional[FuncFixtureInfo] = getattr(item, "_fixtureinfo", None) + if info is None or not info.name2fixturedefs: + # This test item does not use any fixtures. + return + tw.line() + tw.sep("-", f"fixtures used by {item.name}") + # TODO: Fix this type ignore. + tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined] + # dict key not used in loop but needed for sorting. + for _, fixturedefs in sorted(info.name2fixturedefs.items()): + assert fixturedefs is not None + if not fixturedefs: + continue + # Last item is expected to be the one used by the test item. + write_fixture(fixturedefs[-1]) + + for session_item in session.items: + write_item(session_item) + + +def showfixtures(config: Config) -> Union[int, ExitCode]: + from _pytest.main import wrap_session + + return wrap_session(config, _showfixtures_main) + + +def _showfixtures_main(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + fm = session._fixturemanager + + available = [] + seen: Set[Tuple[str, str]] = set() + + for argname, fixturedefs in fm._arg2fixturedefs.items(): + assert fixturedefs is not None + if not fixturedefs: + continue + for fixturedef in fixturedefs: + loc = getlocation(fixturedef.func, str(curdir)) + if (fixturedef.argname, loc) in seen: + continue + seen.add((fixturedef.argname, loc)) + available.append( + ( + len(fixturedef.baseid), + fixturedef.func.__module__, + curdir.bestrelpath(py.path.local(loc)), + fixturedef.argname, + fixturedef, + ) + ) + + available.sort() + currentmodule = None + for baseid, module, bestrel, argname, fixturedef in available: + if currentmodule != module: + if not module.startswith("_pytest."): + tw.line() + tw.sep("-", f"fixtures defined from {module}") + currentmodule = module + if verbose <= 0 and argname[0] == "_": + continue + tw.write(argname, green=True) + if fixturedef.scope != "function": + tw.write(" [%s scope]" % fixturedef.scope, cyan=True) + if verbose > 0: + tw.write(" -- %s" % bestrel, yellow=True) + tw.write("\n") + loc = getlocation(fixturedef.func, str(curdir)) + doc = inspect.getdoc(fixturedef.func) + if doc: + write_docstring(tw, doc) + else: + tw.line(f" {loc}: no docstring available", red=True) + tw.line() + + +def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: + for line in doc.split("\n"): + tw.line(indent + line) + + +class Function(PyobjMixin, nodes.Item): + """An Item responsible for setting up and executing a Python test function. + + param name: + The full function name, including any decorations like those + added by parametrization (``my_func[my_param]``). + param parent: + The parent Node. + param config: + The pytest Config object. + param callspec: + If given, this is function has been parametrized and the callspec contains + meta information about the parametrization. + param callobj: + If given, the object which will be called when the Function is invoked, + otherwise the callobj will be obtained from ``parent`` using ``originalname``. + param keywords: + Keywords bound to the function object for "-k" matching. + param session: + The pytest Session object. + param fixtureinfo: + Fixture information already resolved at this fixture node.. + param originalname: + The attribute name to use for accessing the underlying function object. + Defaults to ``name``. Set this if name is different from the original name, + for example when it contains decorations like those added by parametrization + (``my_func[my_param]``). + """ + + # Disable since functions handle it themselves. + _ALLOW_MARKERS = False + + def __init__( + self, + name: str, + parent, + config: Optional[Config] = None, + callspec: Optional[CallSpec2] = None, + callobj=NOTSET, + keywords=None, + session: Optional[Session] = None, + fixtureinfo: Optional[FuncFixtureInfo] = None, + originalname: Optional[str] = None, + ) -> None: + super().__init__(name, parent, config=config, session=session) + + if callobj is not NOTSET: + self.obj = callobj + + #: Original function name, without any decorations (for example + #: parametrization adds a ``"[...]"`` suffix to function names), used to access + #: the underlying function object from ``parent`` (in case ``callobj`` is not given + #: explicitly). + #: + #: .. versionadded:: 3.0 + self.originalname = originalname or name + + # Note: when FunctionDefinition is introduced, we should change ``originalname`` + # to a readonly property that returns FunctionDefinition.name. + + self.keywords.update(self.obj.__dict__) + self.own_markers.extend(get_unpacked_marks(self.obj)) + if callspec: + self.callspec = callspec + # this is total hostile and a mess + # keywords are broken by design by now + # this will be redeemed later + for mark in callspec.marks: + # feel free to cry, this was broken for years before + # and keywords cant fix it per design + self.keywords[mark.name] = mark + self.own_markers.extend(normalize_mark_list(callspec.marks)) + if keywords: + self.keywords.update(keywords) + + # todo: this is a hell of a hack + # https://github.com/pytest-dev/pytest/issues/4569 + + self.keywords.update( + { + mark.name: True + for mark in self.iter_markers() + if mark.name not in self.keywords + } + ) + + if fixtureinfo is None: + fixtureinfo = self.session._fixturemanager.getfixtureinfo( + self, self.obj, self.cls, funcargs=True + ) + self._fixtureinfo: FuncFixtureInfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + @classmethod + def from_parent(cls, parent, **kw): # todo: determine sound type limitations + """The public constructor.""" + return super().from_parent(parent=parent, **kw) + + def _initrequest(self) -> None: + self.funcargs: Dict[str, object] = {} + self._request = fixtures.FixtureRequest(self, _ispytest=True) + + @property + def function(self): + """Underlying python 'function' object.""" + return getimfunc(self.obj) + + def _getobj(self): + assert self.parent is not None + return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined] + + @property + def _pyfuncitem(self): + """(compatonly) for code expecting pytest-2.2 style request objects.""" + return self + + def runtest(self) -> None: + """Execute the underlying test function.""" + self.ihook.pytest_pyfunc_call(pyfuncitem=self) + + def setup(self) -> None: + if isinstance(self.parent, Instance): + self.parent.newinstance() + self.obj = self._getobj() + self._request._fillfixtures() + + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): + code = _pytest._code.Code.from_function(get_real_func(self.obj)) + path, firstlineno = code.path, code.firstlineno + traceback = excinfo.traceback + ntraceback = traceback.cut(path=path, firstlineno=firstlineno) + if ntraceback == traceback: + ntraceback = ntraceback.cut(path=path) + if ntraceback == traceback: + ntraceback = ntraceback.filter(filter_traceback) + if not ntraceback: + ntraceback = traceback + + excinfo.traceback = ntraceback.filter() + # issue364: mark all but first and last frames to + # only show a single-line message for each frame. + if self.config.getoption("tbstyle", "auto") == "auto": + if len(excinfo.traceback) > 2: + for entry in excinfo.traceback[1:-1]: + entry.set_repr_style("short") + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException], + ) -> Union[str, TerminalRepr]: + style = self.config.getoption("tbstyle", "auto") + if style == "auto": + style = "long" + return self._repr_failure_py(excinfo, style=style) + + +class FunctionDefinition(Function): + """ + This class is a step gap solution until we evolve to have actual function definition nodes + and manage to get rid of ``metafunc``. + """ + + def runtest(self) -> None: + raise RuntimeError("function definitions are not supposed to be run as tests") + + setup = runtest diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python_api.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python_api.py new file mode 100644 index 0000000..81ce4f8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/python_api.py @@ -0,0 +1,786 @@ +import math +import pprint +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sized +from decimal import Decimal +from numbers import Complex +from types import TracebackType +from typing import Any +from typing import Callable +from typing import cast +from typing import Generic +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +if TYPE_CHECKING: + from numpy import ndarray + + +import _pytest._code +from _pytest.compat import final +from _pytest.compat import STRING_TYPES +from _pytest.outcomes import fail + + +def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: + at_str = f" at {at}" if at else "" + return TypeError( + "cannot make approximate comparisons to non-numeric values: {!r} {}".format( + value, at_str + ) + ) + + +# builtin pytest.approx helper + + +class ApproxBase: + """Provide shared utilities for making approximate comparisons between + numbers or sequences of numbers.""" + + # Tell numpy to use our `__eq__` operator instead of its. + __array_ufunc__ = None + __array_priority__ = 100 + + def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: + __tracebackhide__ = True + self.expected = expected + self.abs = abs + self.rel = rel + self.nan_ok = nan_ok + self._check_type() + + def __repr__(self) -> str: + raise NotImplementedError + + def __eq__(self, actual) -> bool: + return all( + a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) + ) + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + def __ne__(self, actual) -> bool: + return not (actual == self) + + def _approx_scalar(self, x) -> "ApproxScalar": + return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + + def _yield_comparisons(self, actual): + """Yield all the pairs of numbers to be compared. + + This is used to implement the `__eq__` method. + """ + raise NotImplementedError + + def _check_type(self) -> None: + """Raise a TypeError if the expected value is not a valid type.""" + # This is only a concern if the expected value is a sequence. In every + # other case, the approx() function ensures that the expected value has + # a numeric type. For this reason, the default is to do nothing. The + # classes that deal with sequences should reimplement this method to + # raise if there are any non-numeric elements in the sequence. + pass + + +def _recursive_list_map(f, x): + if isinstance(x, list): + return list(_recursive_list_map(f, xi) for xi in x) + else: + return f(x) + + +class ApproxNumpy(ApproxBase): + """Perform approximate comparisons where the expected value is numpy array.""" + + def __repr__(self) -> str: + list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) + return f"approx({list_scalars!r})" + + def __eq__(self, actual) -> bool: + import numpy as np + + # self.expected is supposed to always be an array here. + + if not np.isscalar(actual): + try: + actual = np.asarray(actual) + except Exception as e: + raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e + + if not np.isscalar(actual) and actual.shape != self.expected.shape: + return False + + return ApproxBase.__eq__(self, actual) + + def _yield_comparisons(self, actual): + import numpy as np + + # `actual` can either be a numpy array or a scalar, it is treated in + # `__eq__` before being passed to `ApproxBase.__eq__`, which is the + # only method that calls this one. + + if np.isscalar(actual): + for i in np.ndindex(self.expected.shape): + yield actual, self.expected[i].item() + else: + for i in np.ndindex(self.expected.shape): + yield actual[i].item(), self.expected[i].item() + + +class ApproxMapping(ApproxBase): + """Perform approximate comparisons where the expected value is a mapping + with numeric values (the keys can be anything).""" + + def __repr__(self) -> str: + return "approx({!r})".format( + {k: self._approx_scalar(v) for k, v in self.expected.items()} + ) + + def __eq__(self, actual) -> bool: + try: + if set(actual.keys()) != set(self.expected.keys()): + return False + except AttributeError: + return False + + return ApproxBase.__eq__(self, actual) + + def _yield_comparisons(self, actual): + for k in self.expected.keys(): + yield actual[k], self.expected[k] + + def _check_type(self) -> None: + __tracebackhide__ = True + for key, value in self.expected.items(): + if isinstance(value, type(self.expected)): + msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" + raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) + + +class ApproxSequencelike(ApproxBase): + """Perform approximate comparisons where the expected value is a sequence of numbers.""" + + def __repr__(self) -> str: + seq_type = type(self.expected) + if seq_type not in (tuple, list, set): + seq_type = list + return "approx({!r})".format( + seq_type(self._approx_scalar(x) for x in self.expected) + ) + + def __eq__(self, actual) -> bool: + try: + if len(actual) != len(self.expected): + return False + except TypeError: + return False + return ApproxBase.__eq__(self, actual) + + def _yield_comparisons(self, actual): + return zip(actual, self.expected) + + def _check_type(self) -> None: + __tracebackhide__ = True + for index, x in enumerate(self.expected): + if isinstance(x, type(self.expected)): + msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" + raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) + + +class ApproxScalar(ApproxBase): + """Perform approximate comparisons where the expected value is a single number.""" + + # Using Real should be better than this Union, but not possible yet: + # https://github.com/python/typeshed/pull/3108 + DEFAULT_ABSOLUTE_TOLERANCE: Union[float, Decimal] = 1e-12 + DEFAULT_RELATIVE_TOLERANCE: Union[float, Decimal] = 1e-6 + + def __repr__(self) -> str: + """Return a string communicating both the expected value and the + tolerance for the comparison being made. + + For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. + """ + + # Don't show a tolerance for values that aren't compared using + # tolerances, i.e. non-numerics and infinities. Need to call abs to + # handle complex numbers, e.g. (inf + 1j). + if (not isinstance(self.expected, (Complex, Decimal))) or math.isinf( + abs(self.expected) # type: ignore[arg-type] + ): + return str(self.expected) + + # If a sensible tolerance can't be calculated, self.tolerance will + # raise a ValueError. In this case, display '???'. + try: + vetted_tolerance = f"{self.tolerance:.1e}" + if ( + isinstance(self.expected, Complex) + and self.expected.imag + and not math.isinf(self.tolerance) + ): + vetted_tolerance += " ∠ ±180°" + except ValueError: + vetted_tolerance = "???" + + return f"{self.expected} ± {vetted_tolerance}" + + def __eq__(self, actual) -> bool: + """Return whether the given value is equal to the expected value + within the pre-specified tolerance.""" + asarray = _as_numpy_array(actual) + if asarray is not None: + # Call ``__eq__()`` manually to prevent infinite-recursion with + # numpy<1.13. See #3748. + return all(self.__eq__(a) for a in asarray.flat) + + # Short-circuit exact equality. + if actual == self.expected: + return True + + # If either type is non-numeric, fall back to strict equality. + # NB: we need Complex, rather than just Number, to ensure that __abs__, + # __sub__, and __float__ are defined. + if not ( + isinstance(self.expected, (Complex, Decimal)) + and isinstance(actual, (Complex, Decimal)) + ): + return False + + # Allow the user to control whether NaNs are considered equal to each + # other or not. The abs() calls are for compatibility with complex + # numbers. + if math.isnan(abs(self.expected)): # type: ignore[arg-type] + return self.nan_ok and math.isnan(abs(actual)) # type: ignore[arg-type] + + # Infinity shouldn't be approximately equal to anything but itself, but + # if there's a relative tolerance, it will be infinite and infinity + # will seem approximately equal to everything. The equal-to-itself + # case would have been short circuited above, so here we can just + # return false if the expected value is infinite. The abs() call is + # for compatibility with complex numbers. + if math.isinf(abs(self.expected)): # type: ignore[arg-type] + return False + + # Return true if the two numbers are within the tolerance. + result: bool = abs(self.expected - actual) <= self.tolerance + return result + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def tolerance(self): + """Return the tolerance for the comparison. + + This could be either an absolute tolerance or a relative tolerance, + depending on what the user specified or which would be larger. + """ + + def set_default(x, default): + return x if x is not None else default + + # Figure out what the absolute tolerance should be. ``self.abs`` is + # either None or a value specified by the user. + absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) + + if absolute_tolerance < 0: + raise ValueError( + f"absolute tolerance can't be negative: {absolute_tolerance}" + ) + if math.isnan(absolute_tolerance): + raise ValueError("absolute tolerance can't be NaN.") + + # If the user specified an absolute tolerance but not a relative one, + # just return the absolute tolerance. + if self.rel is None: + if self.abs is not None: + return absolute_tolerance + + # Figure out what the relative tolerance should be. ``self.rel`` is + # either None or a value specified by the user. This is done after + # we've made sure the user didn't ask for an absolute tolerance only, + # because we don't want to raise errors about the relative tolerance if + # we aren't even going to use it. + relative_tolerance = set_default( + self.rel, self.DEFAULT_RELATIVE_TOLERANCE + ) * abs(self.expected) + + if relative_tolerance < 0: + raise ValueError( + f"relative tolerance can't be negative: {absolute_tolerance}" + ) + if math.isnan(relative_tolerance): + raise ValueError("relative tolerance can't be NaN.") + + # Return the larger of the relative and absolute tolerances. + return max(relative_tolerance, absolute_tolerance) + + +class ApproxDecimal(ApproxScalar): + """Perform approximate comparisons where the expected value is a Decimal.""" + + DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") + DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") + + +def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: + """Assert that two numbers (or two sets of numbers) are equal to each other + within some tolerance. + + Due to the `intricacies of floating-point arithmetic`__, numbers that we + would intuitively expect to be equal are not always so:: + + >>> 0.1 + 0.2 == 0.3 + False + + __ https://docs.python.org/3/tutorial/floatingpoint.html + + This problem is commonly encountered when writing tests, e.g. when making + sure that floating-point values are what you expect them to be. One way to + deal with this problem is to assert that two floating-point numbers are + equal to within some appropriate tolerance:: + + >>> abs((0.1 + 0.2) - 0.3) < 1e-6 + True + + However, comparisons like this are tedious to write and difficult to + understand. Furthermore, absolute comparisons like the one above are + usually discouraged because there's no tolerance that works well for all + situations. ``1e-6`` is good for numbers around ``1``, but too small for + very big numbers and too big for very small ones. It's better to express + the tolerance as a fraction of the expected value, but relative comparisons + like that are even more difficult to write correctly and concisely. + + The ``approx`` class performs floating-point comparisons using a syntax + that's as intuitive as possible:: + + >>> from pytest import approx + >>> 0.1 + 0.2 == approx(0.3) + True + + The same syntax also works for sequences of numbers:: + + >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) + True + + Dictionary *values*:: + + >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) + True + + ``numpy`` arrays:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP + True + + And for a ``numpy`` array against a scalar:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP + True + + By default, ``approx`` considers numbers within a relative tolerance of + ``1e-6`` (i.e. one part in a million) of its expected value to be equal. + This treatment would lead to surprising results if the expected value was + ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. + To handle this case less surprisingly, ``approx`` also considers numbers + within an absolute tolerance of ``1e-12`` of its expected value to be + equal. Infinity and NaN are special cases. Infinity is only considered + equal to itself, regardless of the relative tolerance. NaN is not + considered equal to anything by default, but you can make it be equal to + itself by setting the ``nan_ok`` argument to True. (This is meant to + facilitate comparing arrays that use NaN to mean "no data".) + + Both the relative and absolute tolerances can be changed by passing + arguments to the ``approx`` constructor:: + + >>> 1.0001 == approx(1) + False + >>> 1.0001 == approx(1, rel=1e-3) + True + >>> 1.0001 == approx(1, abs=1e-3) + True + + If you specify ``abs`` but not ``rel``, the comparison will not consider + the relative tolerance at all. In other words, two numbers that are within + the default relative tolerance of ``1e-6`` will still be considered unequal + if they exceed the specified absolute tolerance. If you specify both + ``abs`` and ``rel``, the numbers will be considered equal if either + tolerance is met:: + + >>> 1 + 1e-8 == approx(1) + True + >>> 1 + 1e-8 == approx(1, abs=1e-12) + False + >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) + True + + You can also use ``approx`` to compare nonnumeric types, or dicts and + sequences containing nonnumeric types, in which case it falls back to + strict equality. This can be useful for comparing dicts and sequences that + can contain optional values:: + + >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) + True + >>> [None, 1.0000005] == approx([None,1]) + True + >>> ["foo", 1.0000005] == approx([None,1]) + False + + If you're thinking about using ``approx``, then you might want to know how + it compares to other good ways of comparing floating-point numbers. All of + these algorithms are based on relative and absolute tolerances and should + agree for the most part, but they do have meaningful differences: + + - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative + tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute + tolerance is met. Because the relative tolerance is calculated w.r.t. + both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor + ``b`` is a "reference value"). You have to specify an absolute tolerance + if you want to compare to ``0.0`` because there is no tolerance by + default. `More information...`__ + + __ https://docs.python.org/3/library/math.html#math.isclose + + - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference + between ``a`` and ``b`` is less that the sum of the relative tolerance + w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance + is only calculated w.r.t. ``b``, this test is asymmetric and you can + think of ``b`` as the reference value. Support for comparing sequences + is provided by ``numpy.allclose``. `More information...`__ + + __ https://numpy.org/doc/stable/reference/generated/numpy.isclose.html + + - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` + are within an absolute tolerance of ``1e-7``. No relative tolerance is + considered and the absolute tolerance cannot be changed, so this function + is not appropriate for very large or very small numbers. Also, it's only + available in subclasses of ``unittest.TestCase`` and it's ugly because it + doesn't follow PEP8. `More information...`__ + + __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual + + - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative + tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. + Because the relative tolerance is only calculated w.r.t. ``b``, this test + is asymmetric and you can think of ``b`` as the reference value. In the + special case that you explicitly specify an absolute tolerance but not a + relative tolerance, only the absolute tolerance is considered. + + .. warning:: + + .. versionchanged:: 3.2 + + In order to avoid inconsistent behavior, ``TypeError`` is + raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. + The example below illustrates the problem:: + + assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) + assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) + + In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` + to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to + comparison. This is because the call hierarchy of rich comparisons + follows a fixed behavior. `More information...`__ + + __ https://docs.python.org/3/reference/datamodel.html#object.__ge__ + + .. versionchanged:: 3.7.1 + ``approx`` raises ``TypeError`` when it encounters a dict value or + sequence element of nonnumeric type. + + .. versionchanged:: 6.1.0 + ``approx`` falls back to strict equality for nonnumeric types instead + of raising ``TypeError``. + """ + + # Delegate the comparison to a class that knows how to deal with the type + # of the expected value (e.g. int, float, list, dict, numpy.array, etc). + # + # The primary responsibility of these classes is to implement ``__eq__()`` + # and ``__repr__()``. The former is used to actually check if some + # "actual" value is equivalent to the given expected value within the + # allowed tolerance. The latter is used to show the user the expected + # value and tolerance, in the case that a test failed. + # + # The actual logic for making approximate comparisons can be found in + # ApproxScalar, which is used to compare individual numbers. All of the + # other Approx classes eventually delegate to this class. The ApproxBase + # class provides some convenient methods and overloads, but isn't really + # essential. + + __tracebackhide__ = True + + if isinstance(expected, Decimal): + cls: Type[ApproxBase] = ApproxDecimal + elif isinstance(expected, Mapping): + cls = ApproxMapping + elif _is_numpy_array(expected): + expected = _as_numpy_array(expected) + cls = ApproxNumpy + elif ( + isinstance(expected, Iterable) + and isinstance(expected, Sized) + # Type ignored because the error is wrong -- not unreachable. + and not isinstance(expected, STRING_TYPES) # type: ignore[unreachable] + ): + cls = ApproxSequencelike + else: + cls = ApproxScalar + + return cls(expected, rel, abs, nan_ok) + + +def _is_numpy_array(obj: object) -> bool: + """ + Return true if the given object is implicitly convertible to ndarray, + and numpy is already imported. + """ + return _as_numpy_array(obj) is not None + + +def _as_numpy_array(obj: object) -> Optional["ndarray"]: + """ + Return an ndarray if the given object is implicitly convertible to ndarray, + and numpy is already imported, otherwise None. + """ + import sys + + np: Any = sys.modules.get("numpy") + if np is not None: + # avoid infinite recursion on numpy scalars, which have __array__ + if np.isscalar(obj): + return None + elif isinstance(obj, np.ndarray): + return obj + elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + return np.asarray(obj) + return None + + +# builtin pytest.raises helper + +_E = TypeVar("_E", bound=BaseException) + + +@overload +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + *, + match: Optional[Union[str, Pattern[str]]] = ..., +) -> "RaisesContext[_E]": + ... + + +@overload +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + func: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> _pytest._code.ExceptionInfo[_E]: + ... + + +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], *args: Any, **kwargs: Any +) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]: + r"""Assert that a code block/function call raises ``expected_exception`` + or raise a failure exception otherwise. + + :kwparam match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception using ``re.search``. To match a literal + string that may contain `special characters`__, the pattern can + first be escaped with ``re.escape``. + + (This is only used when ``pytest.raises`` is used as a context manager, + and passed through to the function otherwise. + When using ``pytest.raises`` as a function, you can use: + ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) + + __ https://docs.python.org/3/library/re.html#regular-expression-syntax + + .. currentmodule:: _pytest._code + + Use ``pytest.raises`` as a context manager, which will capture the exception of the given + type:: + + >>> import pytest + >>> with pytest.raises(ZeroDivisionError): + ... 1/0 + + If the code block does not raise the expected exception (``ZeroDivisionError`` in the example + above), or no exception at all, the check will fail instead. + + You can also use the keyword argument ``match`` to assert that the + exception matches a text or regex:: + + >>> with pytest.raises(ValueError, match='must be 0 or None'): + ... raise ValueError("value must be 0 or None") + + >>> with pytest.raises(ValueError, match=r'must be \d+$'): + ... raise ValueError("value must be 42") + + The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the + details of the captured exception:: + + >>> with pytest.raises(ValueError) as exc_info: + ... raise ValueError("value must be 42") + >>> assert exc_info.type is ValueError + >>> assert exc_info.value.args[0] == "value must be 42" + + .. note:: + + When using ``pytest.raises`` as a context manager, it's worthwhile to + note that normal context manager rules apply and that the exception + raised *must* be the final line in the scope of the context manager. + Lines of code after that, within the scope of the context manager will + not be executed. For example:: + + >>> value = 15 + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... assert exc_info.type is ValueError # this will not execute + + Instead, the following approach must be taken (note the difference in + scope):: + + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... + >>> assert exc_info.type is ValueError + + **Using with** ``pytest.mark.parametrize`` + + When using :ref:`pytest.mark.parametrize ref` + it is possible to parametrize tests such that + some runs raise an exception and others do not. + + See :ref:`parametrizing_conditional_raising` for an example. + + **Legacy form** + + It is possible to specify a callable by passing a to-be-called lambda:: + + >>> raises(ZeroDivisionError, lambda: 1/0) + + + or you can specify an arbitrary callable with arguments:: + + >>> def f(x): return 1/x + ... + >>> raises(ZeroDivisionError, f, 0) + + >>> raises(ZeroDivisionError, f, x=0) + + + The form above is fully supported but discouraged for new code because the + context manager form is regarded as more readable and less error-prone. + + .. note:: + Similar to caught exception objects in Python, explicitly clearing + local references to returned ``ExceptionInfo`` objects can + help the Python interpreter speed up its garbage collection. + + Clearing those references breaks a reference cycle + (``ExceptionInfo`` --> caught exception --> frame stack raising + the exception --> current frame stack --> local variables --> + ``ExceptionInfo``) which makes Python keep all objects referenced + from that cycle (including all local variables in the current + frame) alive until the next cyclic garbage collection run. + More detailed information can be found in the official Python + documentation for :ref:`the try statement `. + """ + __tracebackhide__ = True + + if isinstance(expected_exception, type): + excepted_exceptions: Tuple[Type[_E], ...] = (expected_exception,) + else: + excepted_exceptions = expected_exception + for exc in excepted_exceptions: + if not isinstance(exc, type) or not issubclass(exc, BaseException): # type: ignore[unreachable] + msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] + not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ + raise TypeError(msg.format(not_a)) + + message = f"DID NOT RAISE {expected_exception}" + + if not args: + match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None) + if kwargs: + msg = "Unexpected keyword arguments passed to pytest.raises: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + return RaisesContext(expected_exception, message, match) + else: + func = args[0] + if not callable(func): + raise TypeError( + "{!r} object (type: {}) must be callable".format(func, type(func)) + ) + try: + func(*args[1:], **kwargs) + except expected_exception as e: + # We just caught the exception - there is a traceback. + assert e.__traceback__ is not None + return _pytest._code.ExceptionInfo.from_exc_info( + (type(e), e, e.__traceback__) + ) + fail(message) + + +# This doesn't work with mypy for now. Use fail.Exception instead. +raises.Exception = fail.Exception # type: ignore + + +@final +class RaisesContext(Generic[_E]): + def __init__( + self, + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + message: str, + match_expr: Optional[Union[str, Pattern[str]]] = None, + ) -> None: + self.expected_exception = expected_exception + self.message = message + self.match_expr = match_expr + self.excinfo: Optional[_pytest._code.ExceptionInfo[_E]] = None + + def __enter__(self) -> _pytest._code.ExceptionInfo[_E]: + self.excinfo = _pytest._code.ExceptionInfo.for_later() + return self.excinfo + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + fail(self.message) + assert self.excinfo is not None + if not issubclass(exc_type, self.expected_exception): + return False + # Cast to narrow the exception type now that it's verified. + exc_info = cast(Tuple[Type[_E], _E, TracebackType], (exc_type, exc_val, exc_tb)) + self.excinfo.fill_unfilled(exc_info) + if self.match_expr is not None: + self.excinfo.match(self.match_expr) + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/recwarn.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/recwarn.py new file mode 100644 index 0000000..d872d9d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/recwarn.py @@ -0,0 +1,296 @@ +"""Record warnings during test function execution.""" +import re +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Tuple +from typing import Type +from typing import TypeVar +from typing import Union + +from _pytest.compat import final +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.outcomes import fail + + +T = TypeVar("T") + + +@fixture +def recwarn() -> Generator["WarningsRecorder", None, None]: + """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. + + See http://docs.python.org/library/warnings.html for information + on warning categories. + """ + wrec = WarningsRecorder(_ispytest=True) + with wrec: + warnings.simplefilter("default") + yield wrec + + +@overload +def deprecated_call( + *, match: Optional[Union[str, Pattern[str]]] = ... +) -> "WarningsRecorder": + ... + + +@overload +def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: + ... + + +def deprecated_call( + func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any +) -> Union["WarningsRecorder", Any]: + """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning``. + + This function can be used as a context manager:: + + >>> import warnings + >>> def api_call_v2(): + ... warnings.warn('use v3 of this api', DeprecationWarning) + ... return 200 + + >>> import pytest + >>> with pytest.deprecated_call(): + ... assert api_call_v2() == 200 + + It can also be used by passing a function and ``*args`` and ``**kwargs``, + in which case it will ensure calling ``func(*args, **kwargs)`` produces one of + the warnings types above. The return value is the return value of the function. + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex. + + The context manager produces a list of :class:`warnings.WarningMessage` objects, + one for each warning raised. + """ + __tracebackhide__ = True + if func is not None: + args = (func,) + args + return warns((DeprecationWarning, PendingDeprecationWarning), *args, **kwargs) + + +@overload +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + *, + match: Optional[Union[str, Pattern[str]]] = ..., +) -> "WarningsChecker": + ... + + +@overload +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + func: Callable[..., T], + *args: Any, + **kwargs: Any, +) -> T: + ... + + +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + *args: Any, + match: Optional[Union[str, Pattern[str]]] = None, + **kwargs: Any, +) -> Union["WarningsChecker", Any]: + r"""Assert that code raises a particular class of warning. + + Specifically, the parameter ``expected_warning`` can be a warning class or + sequence of warning classes, and the inside the ``with`` block must issue a warning of that class or + classes. + + This helper produces a list of :class:`warnings.WarningMessage` objects, + one for each warning raised. + + This function can be used as a context manager, or any of the other ways + :func:`pytest.raises` can be used:: + + >>> import pytest + >>> with pytest.warns(RuntimeWarning): + ... warnings.warn("my warning", RuntimeWarning) + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex:: + + >>> with pytest.warns(UserWarning, match='must be 0 or None'): + ... warnings.warn("value must be 0 or None", UserWarning) + + >>> with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("value must be 42", UserWarning) + + >>> with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("this is not here", UserWarning) + Traceback (most recent call last): + ... + Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted... + + """ + __tracebackhide__ = True + if not args: + if kwargs: + msg = "Unexpected keyword arguments passed to pytest.warns: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) + else: + func = args[0] + if not callable(func): + raise TypeError( + "{!r} object (type: {}) must be callable".format(func, type(func)) + ) + with WarningsChecker(expected_warning, _ispytest=True): + return func(*args[1:], **kwargs) + + +class WarningsRecorder(warnings.catch_warnings): + """A context manager to record raised warnings. + + Adapted from `warnings.catch_warnings`. + """ + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + # Type ignored due to the way typeshed handles warnings.catch_warnings. + super().__init__(record=True) # type: ignore[call-arg] + self._entered = False + self._list: List[warnings.WarningMessage] = [] + + @property + def list(self) -> List["warnings.WarningMessage"]: + """The list of recorded warnings.""" + return self._list + + def __getitem__(self, i: int) -> "warnings.WarningMessage": + """Get a recorded warning by index.""" + return self._list[i] + + def __iter__(self) -> Iterator["warnings.WarningMessage"]: + """Iterate through the recorded warnings.""" + return iter(self._list) + + def __len__(self) -> int: + """The number of recorded warnings.""" + return len(self._list) + + def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage": + """Pop the first recorded warning, raise exception if not exists.""" + for i, w in enumerate(self._list): + if issubclass(w.category, cls): + return self._list.pop(i) + __tracebackhide__ = True + raise AssertionError("%r not found in warning list" % cls) + + def clear(self) -> None: + """Clear the list of recorded warnings.""" + self._list[:] = [] + + # Type ignored because it doesn't exactly warnings.catch_warnings.__enter__ + # -- it returns a List but we only emulate one. + def __enter__(self) -> "WarningsRecorder": # type: ignore + if self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot enter %r twice" % self) + _list = super().__enter__() + # record=True means it's None. + assert _list is not None + self._list = _list + warnings.simplefilter("always") + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if not self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot exit %r without entering first" % self) + + super().__exit__(exc_type, exc_val, exc_tb) + + # Built-in catch_warnings does not reset entered state so we do it + # manually here for this context manager to become reusable. + self._entered = False + + +@final +class WarningsChecker(WarningsRecorder): + def __init__( + self, + expected_warning: Optional[ + Union[Type[Warning], Tuple[Type[Warning], ...]] + ] = None, + match_expr: Optional[Union[str, Pattern[str]]] = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + super().__init__(_ispytest=True) + + msg = "exceptions must be derived from Warning, not %s" + if expected_warning is None: + expected_warning_tup = None + elif isinstance(expected_warning, tuple): + for exc in expected_warning: + if not issubclass(exc, Warning): + raise TypeError(msg % type(exc)) + expected_warning_tup = expected_warning + elif issubclass(expected_warning, Warning): + expected_warning_tup = (expected_warning,) + else: + raise TypeError(msg % type(expected_warning)) + + self.expected_warning = expected_warning_tup + self.match_expr = match_expr + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + super().__exit__(exc_type, exc_val, exc_tb) + + __tracebackhide__ = True + + # only check if we're not currently handling an exception + if exc_type is None and exc_val is None and exc_tb is None: + if self.expected_warning is not None: + if not any(issubclass(r.category, self.expected_warning) for r in self): + __tracebackhide__ = True + fail( + "DID NOT WARN. No warnings of type {} was emitted. " + "The list of emitted warnings is: {}.".format( + self.expected_warning, [each.message for each in self] + ) + ) + elif self.match_expr is not None: + for r in self: + if issubclass(r.category, self.expected_warning): + if re.compile(self.match_expr).search(str(r.message)): + break + else: + fail( + "DID NOT WARN. No warnings of type {} matching" + " ('{}') was emitted. The list of emitted warnings" + " is: {}.".format( + self.expected_warning, + self.match_expr, + [each.message for each in self], + ) + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/reports.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/reports.py new file mode 100644 index 0000000..58f1251 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/reports.py @@ -0,0 +1,572 @@ +from io import StringIO +from pathlib import Path +from pprint import pprint +from typing import Any +from typing import cast +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr +import py + +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprEntry +from _pytest._code.code import ReprEntryNative +from _pytest._code.code import ReprExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import ReprFuncArgs +from _pytest._code.code import ReprLocals +from _pytest._code.code import ReprTraceback +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import final +from _pytest.config import Config +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import skip + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Literal + + from _pytest.runner import CallInfo + + +def getworkerinfoline(node): + try: + return node._workerinfocache + except AttributeError: + d = node.workerinfo + ver = "%s.%s.%s" % d["version_info"][:3] + node._workerinfocache = s = "[{}] {} -- Python {} {}".format( + d["id"], d["sysplatform"], ver, d["executable"] + ) + return s + + +_R = TypeVar("_R", bound="BaseReport") + + +class BaseReport: + when: Optional[str] + location: Optional[Tuple[str, Optional[int], str]] + longrepr: Union[ + None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr + ] + sections: List[Tuple[str, str]] + nodeid: str + + def __init__(self, **kw: Any) -> None: + self.__dict__.update(kw) + + if TYPE_CHECKING: + # Can have arbitrary fields given to __init__(). + def __getattr__(self, key: str) -> Any: + ... + + def toterminal(self, out: TerminalWriter) -> None: + if hasattr(self, "node"): + out.line(getworkerinfoline(self.node)) + + longrepr = self.longrepr + if longrepr is None: + return + + if hasattr(longrepr, "toterminal"): + longrepr_terminal = cast(TerminalRepr, longrepr) + longrepr_terminal.toterminal(out) + else: + try: + s = str(longrepr) + except UnicodeEncodeError: + s = "" + out.line(s) + + def get_sections(self, prefix: str) -> Iterator[Tuple[str, str]]: + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + + @property + def longreprtext(self) -> str: + """Read-only property that returns the full string representation of + ``longrepr``. + + .. versionadded:: 3.0 + """ + file = StringIO() + tw = TerminalWriter(file) + tw.hasmarkup = False + self.toterminal(tw) + exc = file.getvalue() + return exc.strip() + + @property + def caplog(self) -> str: + """Return captured log lines, if log capturing is enabled. + + .. versionadded:: 3.5 + """ + return "\n".join( + content for (prefix, content) in self.get_sections("Captured log") + ) + + @property + def capstdout(self) -> str: + """Return captured text from stdout, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stdout") + ) + + @property + def capstderr(self) -> str: + """Return captured text from stderr, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stderr") + ) + + passed = property(lambda x: x.outcome == "passed") + failed = property(lambda x: x.outcome == "failed") + skipped = property(lambda x: x.outcome == "skipped") + + @property + def fspath(self) -> str: + return self.nodeid.split("::")[0] + + @property + def count_towards_summary(self) -> bool: + """**Experimental** Whether this report should be counted towards the + totals shown at the end of the test session: "1 passed, 1 failure, etc". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + return True + + @property + def head_line(self) -> Optional[str]: + """**Experimental** The head line shown with longrepr output for this + report, more commonly during traceback representation during + failures:: + + ________ Test.foo ________ + + + In the example above, the head_line is "Test.foo". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + if self.location is not None: + fspath, lineno, domain = self.location + return domain + return None + + def _get_verbose_word(self, config: Config): + _category, _short, verbose = config.hook.pytest_report_teststatus( + report=self, config=config + ) + return verbose + + def _to_json(self) -> Dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + return _report_to_json(self) + + @classmethod + def _from_json(cls: Type[_R], reportdict: Dict[str, object]) -> _R: + """Create either a TestReport or CollectReport, depending on the calling class. + + It is the callers responsibility to know which class to pass here. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + kwargs = _report_kwargs_from_json(reportdict) + return cls(**kwargs) + + +def _report_unserialization_failure( + type_name: str, report_class: Type[BaseReport], reportdict +) -> "NoReturn": + url = "https://github.com/pytest-dev/pytest/issues" + stream = StringIO() + pprint("-" * 100, stream=stream) + pprint("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream) + pprint("report_name: %s" % report_class, stream=stream) + pprint(reportdict, stream=stream) + pprint("Please report this bug at %s" % url, stream=stream) + pprint("-" * 100, stream=stream) + raise RuntimeError(stream.getvalue()) + + +@final +class TestReport(BaseReport): + """Basic test report object (also used for setup and teardown calls if + they fail).""" + + __test__ = False + + def __init__( + self, + nodeid: str, + location: Tuple[str, Optional[int], str], + keywords, + outcome: "Literal['passed', 'failed', 'skipped']", + longrepr: Union[ + None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr + ], + when: "Literal['setup', 'call', 'teardown']", + sections: Iterable[Tuple[str, str]] = (), + duration: float = 0, + user_properties: Optional[Iterable[Tuple[str, object]]] = None, + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: A (filesystempath, lineno, domaininfo) tuple indicating the + #: actual location of a test item - it might be different from the + #: collected one e.g. if a method is inherited from a different module. + self.location: Tuple[str, Optional[int], str] = location + + #: A name -> value dictionary containing all keywords and + #: markers associated with a test invocation. + self.keywords = keywords + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: One of 'setup', 'call', 'teardown' to indicate runtest phase. + self.when = when + + #: User properties is a list of tuples (name, value) that holds user + #: defined properties of the test. + self.user_properties = list(user_properties or []) + + #: List of pairs ``(str, str)`` of extra information which needs to + #: marshallable. Used by pytest to add captured text + #: from ``stdout`` and ``stderr``, but may be used by other plugins + #: to add arbitrary information to reports. + self.sections = list(sections) + + #: Time it took to run just the test. + self.duration = duration + + self.__dict__.update(extra) + + def __repr__(self) -> str: + return "<{} {!r} when={!r} outcome={!r}>".format( + self.__class__.__name__, self.nodeid, self.when, self.outcome + ) + + @classmethod + def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": + """Create and fill a TestReport with standard item and call info.""" + when = call.when + # Remove "collect" from the Literal type -- only for collection calls. + assert when != "collect" + duration = call.duration + keywords = {x: 1 for x in item.keywords} + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome: Literal["passed", "failed", "skipped"] = "passed" + longrepr: Union[ + None, + ExceptionInfo[BaseException], + Tuple[str, int, str], + str, + TerminalRepr, + ] = (None) + else: + if not isinstance(excinfo, ExceptionInfo): + outcome = "failed" + longrepr = excinfo + elif isinstance(excinfo.value, skip.Exception): + outcome = "skipped" + r = excinfo._getreprcrash() + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: # exception in setup or teardown + longrepr = item._repr_failure_py( + excinfo, style=item.config.getoption("tbstyle", "auto") + ) + for rwhen, key, content in item._report_sections: + sections.append((f"Captured {key} {rwhen}", content)) + return cls( + item.nodeid, + item.location, + keywords, + outcome, + longrepr, + when, + sections, + duration, + user_properties=item.user_properties, + ) + + +@final +class CollectReport(BaseReport): + """Collection report object.""" + + when = "collect" + + def __init__( + self, + nodeid: str, + outcome: "Literal['passed', 'skipped', 'failed']", + longrepr, + result: Optional[List[Union[Item, Collector]]], + sections: Iterable[Tuple[str, str]] = (), + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: The collected items and collection nodes. + self.result = result or [] + + #: List of pairs ``(str, str)`` of extra information which needs to + #: marshallable. + # Used by pytest to add captured text : from ``stdout`` and ``stderr``, + # but may be used by other plugins : to add arbitrary information to + # reports. + self.sections = list(sections) + + self.__dict__.update(extra) + + @property + def location(self): + return (self.fspath, None, self.fspath) + + def __repr__(self) -> str: + return "".format( + self.nodeid, len(self.result), self.outcome + ) + + +class CollectErrorRepr(TerminalRepr): + def __init__(self, msg: str) -> None: + self.longrepr = msg + + def toterminal(self, out: TerminalWriter) -> None: + out.line(self.longrepr, red=True) + + +def pytest_report_to_serializable( + report: Union[CollectReport, TestReport] +) -> Optional[Dict[str, Any]]: + if isinstance(report, (TestReport, CollectReport)): + data = report._to_json() + data["$report_type"] = report.__class__.__name__ + return data + # TODO: Check if this is actually reachable. + return None # type: ignore[unreachable] + + +def pytest_report_from_serializable( + data: Dict[str, Any], +) -> Optional[Union[CollectReport, TestReport]]: + if "$report_type" in data: + if data["$report_type"] == "TestReport": + return TestReport._from_json(data) + elif data["$report_type"] == "CollectReport": + return CollectReport._from_json(data) + assert False, "Unknown report_type unserialize data: {}".format( + data["$report_type"] + ) + return None + + +def _report_to_json(report: BaseReport) -> Dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def serialize_repr_entry( + entry: Union[ReprEntry, ReprEntryNative] + ) -> Dict[str, Any]: + data = attr.asdict(entry) + for key, value in data.items(): + if hasattr(value, "__dict__"): + data[key] = attr.asdict(value) + entry_data = {"type": type(entry).__name__, "data": data} + return entry_data + + def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]: + result = attr.asdict(reprtraceback) + result["reprentries"] = [ + serialize_repr_entry(x) for x in reprtraceback.reprentries + ] + return result + + def serialize_repr_crash( + reprcrash: Optional[ReprFileLocation], + ) -> Optional[Dict[str, Any]]: + if reprcrash is not None: + return attr.asdict(reprcrash) + else: + return None + + def serialize_exception_longrepr(rep: BaseReport) -> Dict[str, Any]: + assert rep.longrepr is not None + # TODO: Investigate whether the duck typing is really necessary here. + longrepr = cast(ExceptionRepr, rep.longrepr) + result: Dict[str, Any] = { + "reprcrash": serialize_repr_crash(longrepr.reprcrash), + "reprtraceback": serialize_repr_traceback(longrepr.reprtraceback), + "sections": longrepr.sections, + } + if isinstance(longrepr, ExceptionChainRepr): + result["chain"] = [] + for repr_traceback, repr_crash, description in longrepr.chain: + result["chain"].append( + ( + serialize_repr_traceback(repr_traceback), + serialize_repr_crash(repr_crash), + description, + ) + ) + else: + result["chain"] = None + return result + + d = report.__dict__.copy() + if hasattr(report.longrepr, "toterminal"): + if hasattr(report.longrepr, "reprtraceback") and hasattr( + report.longrepr, "reprcrash" + ): + d["longrepr"] = serialize_exception_longrepr(report) + else: + d["longrepr"] = str(report.longrepr) + else: + d["longrepr"] = report.longrepr + for name in d: + if isinstance(d[name], (py.path.local, Path)): + d[name] = str(d[name]) + elif name == "result": + d[name] = None # for now + return d + + +def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]: + """Return **kwargs that can be used to construct a TestReport or + CollectReport instance. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def deserialize_repr_entry(entry_data): + data = entry_data["data"] + entry_type = entry_data["type"] + if entry_type == "ReprEntry": + reprfuncargs = None + reprfileloc = None + reprlocals = None + if data["reprfuncargs"]: + reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) + if data["reprfileloc"]: + reprfileloc = ReprFileLocation(**data["reprfileloc"]) + if data["reprlocals"]: + reprlocals = ReprLocals(data["reprlocals"]["lines"]) + + reprentry: Union[ReprEntry, ReprEntryNative] = ReprEntry( + lines=data["lines"], + reprfuncargs=reprfuncargs, + reprlocals=reprlocals, + reprfileloc=reprfileloc, + style=data["style"], + ) + elif entry_type == "ReprEntryNative": + reprentry = ReprEntryNative(data["lines"]) + else: + _report_unserialization_failure(entry_type, TestReport, reportdict) + return reprentry + + def deserialize_repr_traceback(repr_traceback_dict): + repr_traceback_dict["reprentries"] = [ + deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"] + ] + return ReprTraceback(**repr_traceback_dict) + + def deserialize_repr_crash(repr_crash_dict: Optional[Dict[str, Any]]): + if repr_crash_dict is not None: + return ReprFileLocation(**repr_crash_dict) + else: + return None + + if ( + reportdict["longrepr"] + and "reprcrash" in reportdict["longrepr"] + and "reprtraceback" in reportdict["longrepr"] + ): + + reprtraceback = deserialize_repr_traceback( + reportdict["longrepr"]["reprtraceback"] + ) + reprcrash = deserialize_repr_crash(reportdict["longrepr"]["reprcrash"]) + if reportdict["longrepr"]["chain"]: + chain = [] + for repr_traceback_data, repr_crash_data, description in reportdict[ + "longrepr" + ]["chain"]: + chain.append( + ( + deserialize_repr_traceback(repr_traceback_data), + deserialize_repr_crash(repr_crash_data), + description, + ) + ) + exception_info: Union[ + ExceptionChainRepr, ReprExceptionInfo + ] = ExceptionChainRepr(chain) + else: + exception_info = ReprExceptionInfo(reprtraceback, reprcrash) + + for section in reportdict["longrepr"]["sections"]: + exception_info.addsection(*section) + reportdict["longrepr"] = exception_info + + return reportdict diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/runner.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/runner.py new file mode 100644 index 0000000..794690d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/runner.py @@ -0,0 +1,462 @@ +"""Basic collect and runtest protocol implementations.""" +import bdb +import os +import sys +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generic +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +from .reports import BaseReport +from .reports import CollectErrorRepr +from .reports import CollectReport +from .reports import TestReport +from _pytest import timing +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest.compat import final +from _pytest.config.argparsing import Parser +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.outcomes import Exit +from _pytest.outcomes import Skipped +from _pytest.outcomes import TEST_OUTCOME + +if TYPE_CHECKING: + from typing_extensions import Literal + + from _pytest.main import Session + from _pytest.terminal import TerminalReporter + +# +# pytest plugin hooks. + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "reporting", after="general") + group.addoption( + "--durations", + action="store", + type=int, + default=None, + metavar="N", + help="show N slowest setup/test durations (N=0 for all).", + ) + group.addoption( + "--durations-min", + action="store", + type=float, + default=0.005, + metavar="N", + help="Minimal duration in seconds for inclusion in slowest list. Default 0.005", + ) + + +def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: + durations = terminalreporter.config.option.durations + durations_min = terminalreporter.config.option.durations_min + verbose = terminalreporter.config.getvalue("verbose") + if durations is None: + return + tr = terminalreporter + dlist = [] + for replist in tr.stats.values(): + for rep in replist: + if hasattr(rep, "duration"): + dlist.append(rep) + if not dlist: + return + dlist.sort(key=lambda x: x.duration, reverse=True) # type: ignore[no-any-return] + if not durations: + tr.write_sep("=", "slowest durations") + else: + tr.write_sep("=", "slowest %s durations" % durations) + dlist = dlist[:durations] + + for i, rep in enumerate(dlist): + if verbose < 2 and rep.duration < durations_min: + tr.write_line("") + tr.write_line( + "(%s durations < %gs hidden. Use -vv to show these durations.)" + % (len(dlist) - i, durations_min) + ) + break + tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") + + +def pytest_sessionstart(session: "Session") -> None: + session._setupstate = SetupState() + + +def pytest_sessionfinish(session: "Session") -> None: + session._setupstate.teardown_all() + + +def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> bool: + ihook = item.ihook + ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) + runtestprotocol(item, nextitem=nextitem) + ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location) + return True + + +def runtestprotocol( + item: Item, log: bool = True, nextitem: Optional[Item] = None +) -> List[TestReport]: + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: # type: ignore[attr-defined] + item._initrequest() # type: ignore[attr-defined] + rep = call_and_report(item, "setup", log) + reports = [rep] + if rep.passed: + if item.config.getoption("setupshow", False): + show_test_item(item) + if not item.config.getoption("setuponly", False): + reports.append(call_and_report(item, "call", log)) + reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) + # After all teardown hooks have been called + # want funcargs and request info to go away. + if hasrequest: + item._request = False # type: ignore[attr-defined] + item.funcargs = None # type: ignore[attr-defined] + return reports + + +def show_test_item(item: Item) -> None: + """Show test function, parameters and the fixtures of the test item.""" + tw = item.config.get_terminal_writer() + tw.line() + tw.write(" " * 8) + tw.write(item.nodeid) + used_fixtures = sorted(getattr(item, "fixturenames", [])) + if used_fixtures: + tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) + tw.flush() + + +def pytest_runtest_setup(item: Item) -> None: + _update_current_test_var(item, "setup") + item.session._setupstate.prepare(item) + + +def pytest_runtest_call(item: Item) -> None: + _update_current_test_var(item, "call") + try: + del sys.last_type + del sys.last_value + del sys.last_traceback + except AttributeError: + pass + try: + item.runtest() + except Exception as e: + # Store trace info to allow postmortem debugging + sys.last_type = type(e) + sys.last_value = e + assert e.__traceback__ is not None + # Skip *this* frame + sys.last_traceback = e.__traceback__.tb_next + raise e + + +def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None: + _update_current_test_var(item, "teardown") + item.session._setupstate.teardown_exact(item, nextitem) + _update_current_test_var(item, None) + + +def _update_current_test_var( + item: Item, when: Optional["Literal['setup', 'call', 'teardown']"] +) -> None: + """Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage. + + If ``when`` is None, delete ``PYTEST_CURRENT_TEST`` from the environment. + """ + var_name = "PYTEST_CURRENT_TEST" + if when: + value = f"{item.nodeid} ({when})" + # don't allow null bytes on environment variables (see #2644, #2957) + value = value.replace("\x00", "(null)") + os.environ[var_name] = value + else: + os.environ.pop(var_name) + + +def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: + if report.when in ("setup", "teardown"): + if report.failed: + # category, shortletter, verbose-word + return "error", "E", "ERROR" + elif report.skipped: + return "skipped", "s", "SKIPPED" + else: + return "", "", "" + return None + + +# +# Implementation + + +def call_and_report( + item: Item, when: "Literal['setup', 'call', 'teardown']", log: bool = True, **kwds +) -> TestReport: + call = call_runtest_hook(item, when, **kwds) + hook = item.ihook + report: TestReport = hook.pytest_runtest_makereport(item=item, call=call) + if log: + hook.pytest_runtest_logreport(report=report) + if check_interactive_exception(call, report): + hook.pytest_exception_interact(node=item, call=call, report=report) + return report + + +def check_interactive_exception(call: "CallInfo[object]", report: BaseReport) -> bool: + """Check whether the call raised an exception that should be reported as + interactive.""" + if call.excinfo is None: + # Didn't raise. + return False + if hasattr(report, "wasxfail"): + # Exception was expected. + return False + if isinstance(call.excinfo.value, (Skipped, bdb.BdbQuit)): + # Special control flow exception. + return False + return True + + +def call_runtest_hook( + item: Item, when: "Literal['setup', 'call', 'teardown']", **kwds +) -> "CallInfo[None]": + if when == "setup": + ihook: Callable[..., None] = item.ihook.pytest_runtest_setup + elif when == "call": + ihook = item.ihook.pytest_runtest_call + elif when == "teardown": + ihook = item.ihook.pytest_runtest_teardown + else: + assert False, f"Unhandled runtest hook case: {when}" + reraise: Tuple[Type[BaseException], ...] = (Exit,) + if not item.config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + return CallInfo.from_call( + lambda: ihook(item=item, **kwds), when=when, reraise=reraise + ) + + +TResult = TypeVar("TResult", covariant=True) + + +@final +@attr.s(repr=False) +class CallInfo(Generic[TResult]): + """Result/Exception info a function invocation. + + :param T result: + The return value of the call, if it didn't raise. Can only be + accessed if excinfo is None. + :param Optional[ExceptionInfo] excinfo: + The captured exception of the call, if it raised. + :param float start: + The system time when the call started, in seconds since the epoch. + :param float stop: + The system time when the call ended, in seconds since the epoch. + :param float duration: + The call duration, in seconds. + :param str when: + The context of invocation: "setup", "call", "teardown", ... + """ + + _result = attr.ib(type="Optional[TResult]") + excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]]) + start = attr.ib(type=float) + stop = attr.ib(type=float) + duration = attr.ib(type=float) + when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']") + + @property + def result(self) -> TResult: + if self.excinfo is not None: + raise AttributeError(f"{self!r} has no valid result") + # The cast is safe because an exception wasn't raised, hence + # _result has the expected function return type (which may be + # None, that's why a cast and not an assert). + return cast(TResult, self._result) + + @classmethod + def from_call( + cls, + func: "Callable[[], TResult]", + when: "Literal['collect', 'setup', 'call', 'teardown']", + reraise: Optional[ + Union[Type[BaseException], Tuple[Type[BaseException], ...]] + ] = None, + ) -> "CallInfo[TResult]": + excinfo = None + start = timing.time() + precise_start = timing.perf_counter() + try: + result: Optional[TResult] = func() + except BaseException: + excinfo = ExceptionInfo.from_current() + if reraise is not None and isinstance(excinfo.value, reraise): + raise + result = None + # use the perf counter + precise_stop = timing.perf_counter() + duration = precise_stop - precise_start + stop = timing.time() + return cls( + start=start, + stop=stop, + duration=duration, + when=when, + result=result, + excinfo=excinfo, + ) + + def __repr__(self) -> str: + if self.excinfo is None: + return f"" + return f"" + + +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: + return TestReport.from_item_and_call(item, call) + + +def pytest_make_collect_report(collector: Collector) -> CollectReport: + call = CallInfo.from_call(lambda: list(collector.collect()), "collect") + longrepr: Union[None, Tuple[str, int, str], str, TerminalRepr] = None + if not call.excinfo: + outcome: Literal["passed", "skipped", "failed"] = "passed" + else: + skip_exceptions = [Skipped] + unittest = sys.modules.get("unittest") + if unittest is not None: + # Type ignored because unittest is loaded dynamically. + skip_exceptions.append(unittest.SkipTest) # type: ignore + if isinstance(call.excinfo.value, tuple(skip_exceptions)): + outcome = "skipped" + r_ = collector._repr_failure_py(call.excinfo, "line") + assert isinstance(r_, ExceptionChainRepr), repr(r_) + r = r_.reprcrash + assert r + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + errorinfo = collector.repr_failure(call.excinfo) + if not hasattr(errorinfo, "toterminal"): + assert isinstance(errorinfo, str) + errorinfo = CollectErrorRepr(errorinfo) + longrepr = errorinfo + result = call.result if not call.excinfo else None + rep = CollectReport(collector.nodeid, outcome, longrepr, result) + rep.call = call # type: ignore # see collect_one_node + return rep + + +class SetupState: + """Shared state for setting up/tearing down test items or collectors.""" + + def __init__(self): + self.stack: List[Node] = [] + self._finalizers: Dict[Node, List[Callable[[], object]]] = {} + + def addfinalizer(self, finalizer: Callable[[], object], colitem) -> None: + """Attach a finalizer to the given colitem.""" + assert colitem and not isinstance(colitem, tuple) + assert callable(finalizer) + # assert colitem in self.stack # some unit tests don't setup stack :/ + self._finalizers.setdefault(colitem, []).append(finalizer) + + def _pop_and_teardown(self): + colitem = self.stack.pop() + self._teardown_with_finalization(colitem) + + def _callfinalizers(self, colitem) -> None: + finalizers = self._finalizers.pop(colitem, None) + exc = None + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + + def _teardown_with_finalization(self, colitem) -> None: + self._callfinalizers(colitem) + colitem.teardown() + for colitem in self._finalizers: + assert colitem in self.stack + + def teardown_all(self) -> None: + while self.stack: + self._pop_and_teardown() + for key in list(self._finalizers): + self._teardown_with_finalization(key) + assert not self._finalizers + + def teardown_exact(self, item, nextitem) -> None: + needed_collectors = nextitem and nextitem.listchain() or [] + self._teardown_towards(needed_collectors) + + def _teardown_towards(self, needed_collectors) -> None: + exc = None + while self.stack: + if self.stack == needed_collectors[: len(self.stack)]: + break + try: + self._pop_and_teardown() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + + def prepare(self, colitem) -> None: + """Setup objects along the collector chain to the test-method.""" + + # Check if the last collection node has raised an error. + for col in self.stack: + if hasattr(col, "_prepare_exc"): + exc = col._prepare_exc # type: ignore[attr-defined] + raise exc + + needed_collectors = colitem.listchain() + for col in needed_collectors[len(self.stack) :]: + self.stack.append(col) + try: + col.setup() + except TEST_OUTCOME as e: + col._prepare_exc = e # type: ignore[attr-defined] + raise e + + +def collect_one_node(collector: Collector) -> CollectReport: + ihook = collector.ihook + ihook.pytest_collectstart(collector=collector) + rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) + call = rep.__dict__.pop("call", None) + if call and check_interactive_exception(call, rep): + ihook.pytest_exception_interact(node=collector, call=call, report=rep) + return rep diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setuponly.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setuponly.py new file mode 100644 index 0000000..44a1094 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setuponly.py @@ -0,0 +1,94 @@ +from typing import Generator +from typing import Optional +from typing import Union + +import pytest +from _pytest._io.saferepr import saferepr +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setuponly", + "--setup-only", + action="store_true", + help="only setup fixtures, do not execute tests.", + ) + group.addoption( + "--setupshow", + "--setup-show", + action="store_true", + help="show setup of fixtures while executing tests.", + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Generator[None, None, None]: + yield + if request.config.option.setupshow: + if hasattr(request, "param"): + # Save the fixture parameter so ._show_fixture_action() can + # display it now and during the teardown (in .finish()). + if fixturedef.ids: + if callable(fixturedef.ids): + param = fixturedef.ids(request.param) + else: + param = fixturedef.ids[request.param_index] + else: + param = request.param + fixturedef.cached_param = param # type: ignore[attr-defined] + _show_fixture_action(fixturedef, "SETUP") + + +def pytest_fixture_post_finalizer(fixturedef: FixtureDef[object]) -> None: + if fixturedef.cached_result is not None: + config = fixturedef._fixturemanager.config + if config.option.setupshow: + _show_fixture_action(fixturedef, "TEARDOWN") + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param # type: ignore[attr-defined] + + +def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None: + config = fixturedef._fixturemanager.config + capman = config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture() + + tw = config.get_terminal_writer() + tw.line() + tw.write(" " * 2 * fixturedef.scopenum) + tw.write( + "{step} {scope} {fixture}".format( + step=msg.ljust(8), # align the output to TEARDOWN + scope=fixturedef.scope[0].upper(), + fixture=fixturedef.argname, + ) + ) + + if msg == "SETUP": + deps = sorted(arg for arg in fixturedef.argnames if arg != "request") + if deps: + tw.write(" (fixtures used: {})".format(", ".join(deps))) + + if hasattr(fixturedef, "cached_param"): + tw.write("[{}]".format(saferepr(fixturedef.cached_param, maxsize=42))) # type: ignore[attr-defined] + + tw.flush() + + if capman: + capman.resume_global_capture() + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.setuponly: + config.option.setupshow = True + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setupplan.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setupplan.py new file mode 100644 index 0000000..9ba81cc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/setupplan.py @@ -0,0 +1,40 @@ +from typing import Optional +from typing import Union + +import pytest +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setupplan", + "--setup-plan", + action="store_true", + help="show what fixtures and tests would be executed but " + "don't execute anything.", + ) + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Optional[object]: + # Will return a dummy fixture if the setuponly option is provided. + if request.config.option.setupplan: + my_cache_key = fixturedef.cache_key(request) + fixturedef.cached_result = (None, my_cache_key, None) + return fixturedef.cached_result + return None + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.setupplan: + config.option.setuponly = True + config.option.setupshow = True + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/skipping.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/skipping.py new file mode 100644 index 0000000..9aacfec --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/skipping.py @@ -0,0 +1,324 @@ +"""Support for skip/xfail functions and markers.""" +import os +import platform +import sys +import traceback +from collections.abc import Mapping +from typing import Generator +from typing import Optional +from typing import Tuple +from typing import Type + +import attr + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.mark.structures import Mark +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.reports import BaseReport +from _pytest.runner import CallInfo +from _pytest.store import StoreKey + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--runxfail", + action="store_true", + dest="runxfail", + default=False, + help="report the results of xfail tests as if they were not marked", + ) + + parser.addini( + "xfail_strict", + "default for the strict parameter of xfail " + "markers when not given explicitly (default: False)", + default=False, + type="bool", + ) + + +def pytest_configure(config: Config) -> None: + if config.option.runxfail: + # yay a hack + import pytest + + old = pytest.xfail + config._cleanup.append(lambda: setattr(pytest, "xfail", old)) + + def nop(*args, **kwargs): + pass + + nop.Exception = xfail.Exception # type: ignore[attr-defined] + setattr(pytest, "xfail", nop) + + config.addinivalue_line( + "markers", + "skip(reason=None): skip the given test function with an optional reason. " + 'Example: skip(reason="no way of currently testing this") skips the ' + "test.", + ) + config.addinivalue_line( + "markers", + "skipif(condition, ..., *, reason=...): " + "skip the given test function if any of the conditions evaluate to True. " + "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " + "See https://docs.pytest.org/en/stable/reference.html#pytest-mark-skipif", + ) + config.addinivalue_line( + "markers", + "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): " + "mark the test function as an expected failure if any of the conditions " + "evaluate to True. Optionally specify a reason for better reporting " + "and run=False if you don't even want to execute the test function. " + "If only specific exception(s) are expected, you can list them in " + "raises, and if the test fails in other ways, it will be reported as " + "a true failure. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-xfail", + ) + + +def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, str]: + """Evaluate a single skipif/xfail condition. + + If an old-style string condition is given, it is eval()'d, otherwise the + condition is bool()'d. If this fails, an appropriately formatted pytest.fail + is raised. + + Returns (result, reason). The reason is only relevant if the result is True. + """ + # String condition. + if isinstance(condition, str): + globals_ = { + "os": os, + "sys": sys, + "platform": platform, + "config": item.config, + } + for dictionary in reversed( + item.ihook.pytest_markeval_namespace(config=item.config) + ): + if not isinstance(dictionary, Mapping): + raise ValueError( + "pytest_markeval_namespace() needs to return a dict, got {!r}".format( + dictionary + ) + ) + globals_.update(dictionary) + if hasattr(item, "obj"): + globals_.update(item.obj.__globals__) # type: ignore[attr-defined] + try: + filename = f"<{mark.name} condition>" + condition_code = compile(condition, filename, "eval") + result = eval(condition_code, globals_) + except SyntaxError as exc: + msglines = [ + "Error evaluating %r condition" % mark.name, + " " + condition, + " " + " " * (exc.offset or 0) + "^", + "SyntaxError: invalid syntax", + ] + fail("\n".join(msglines), pytrace=False) + except Exception as exc: + msglines = [ + "Error evaluating %r condition" % mark.name, + " " + condition, + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + # Boolean condition. + else: + try: + result = bool(condition) + except Exception as exc: + msglines = [ + "Error evaluating %r condition as a boolean" % mark.name, + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + reason = mark.kwargs.get("reason", None) + if reason is None: + if isinstance(condition, str): + reason = "condition: " + condition + else: + # XXX better be checked at collection time + msg = ( + "Error evaluating %r: " % mark.name + + "you need to specify reason=STRING when using booleans as conditions." + ) + fail(msg, pytrace=False) + + return result, reason + + +@attr.s(slots=True, frozen=True) +class Skip: + """The result of evaluate_skip_marks().""" + + reason = attr.ib(type=str) + + +def evaluate_skip_marks(item: Item) -> Optional[Skip]: + """Evaluate skip and skipif marks on item, returning Skip if triggered.""" + for mark in item.iter_markers(name="skipif"): + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Skip(reason) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Skip(reason) + + for mark in item.iter_markers(name="skip"): + if "reason" in mark.kwargs: + reason = mark.kwargs["reason"] + elif mark.args: + reason = mark.args[0] + else: + reason = "unconditional skip" + return Skip(reason) + + return None + + +@attr.s(slots=True, frozen=True) +class Xfail: + """The result of evaluate_xfail_marks().""" + + reason = attr.ib(type=str) + run = attr.ib(type=bool) + strict = attr.ib(type=bool) + raises = attr.ib(type=Optional[Tuple[Type[BaseException], ...]]) + + +def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: + """Evaluate xfail marks on item, returning Xfail if triggered.""" + for mark in item.iter_markers(name="xfail"): + run = mark.kwargs.get("run", True) + strict = mark.kwargs.get("strict", item.config.getini("xfail_strict")) + raises = mark.kwargs.get("raises", None) + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Xfail(reason, run, strict, raises) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Xfail(reason, run, strict, raises) + + return None + + +# Whether skipped due to skip or skipif marks. +skipped_by_mark_key = StoreKey[bool]() +# Saves the xfail mark evaluation. Can be refreshed during call if None. +xfailed_key = StoreKey[Optional[Xfail]]() +unexpectedsuccess_key = StoreKey[str]() + + +@hookimpl(tryfirst=True) +def pytest_runtest_setup(item: Item) -> None: + skipped = evaluate_skip_marks(item) + item._store[skipped_by_mark_key] = skipped is not None + if skipped: + skip(skipped.reason) + + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + +@hookimpl(hookwrapper=True) +def pytest_runtest_call(item: Item) -> Generator[None, None, None]: + xfailed = item._store.get(xfailed_key, None) + if xfailed is None: + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + yield + + # The test run may have added an xfail mark dynamically. + xfailed = item._store.get(xfailed_key, None) + if xfailed is None: + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + +@hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]): + outcome = yield + rep = outcome.get_result() + xfailed = item._store.get(xfailed_key, None) + # unittest special case, see setting of unexpectedsuccess_key + if unexpectedsuccess_key in item._store and rep.when == "call": + reason = item._store[unexpectedsuccess_key] + if reason: + rep.longrepr = f"Unexpected success: {reason}" + else: + rep.longrepr = "Unexpected success" + rep.outcome = "failed" + elif item.config.option.runxfail: + pass # don't interfere + elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): + assert call.excinfo.value.msg is not None + rep.wasxfail = "reason: " + call.excinfo.value.msg + rep.outcome = "skipped" + elif not rep.skipped and xfailed: + if call.excinfo: + raises = xfailed.raises + if raises is not None and not isinstance(call.excinfo.value, raises): + rep.outcome = "failed" + else: + rep.outcome = "skipped" + rep.wasxfail = xfailed.reason + elif call.when == "call": + if xfailed.strict: + rep.outcome = "failed" + rep.longrepr = "[XPASS(strict)] " + xfailed.reason + else: + rep.outcome = "passed" + rep.wasxfail = xfailed.reason + + if ( + item._store.get(skipped_by_mark_key, True) + and rep.skipped + and type(rep.longrepr) is tuple + ): + # Skipped by mark.skipif; change the location of the failure + # to point to the item definition, otherwise it will display + # the location of where the skip exception was raised within pytest. + _, _, reason = rep.longrepr + filename, line = item.reportinfo()[:2] + assert line is not None + rep.longrepr = str(filename), line + 1, reason + + +def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: + if hasattr(report, "wasxfail"): + if report.skipped: + return "xfailed", "x", "XFAIL" + elif report.passed: + return "xpassed", "X", "XPASS" + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/stepwise.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/stepwise.py new file mode 100644 index 0000000..197577c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/stepwise.py @@ -0,0 +1,119 @@ +from typing import List +from typing import Optional +from typing import TYPE_CHECKING + +import pytest +from _pytest import nodes +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.main import Session +from _pytest.reports import TestReport + +if TYPE_CHECKING: + from _pytest.cacheprovider import Cache + +STEPWISE_CACHE_DIR = "cache/stepwise" + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--sw", + "--stepwise", + action="store_true", + default=False, + dest="stepwise", + help="exit on test failure and continue from last failing test next time", + ) + group.addoption( + "--sw-skip", + "--stepwise-skip", + action="store_true", + default=False, + dest="stepwise_skip", + help="ignore the first failing test but stop on the next failing test", + ) + + +@pytest.hookimpl +def pytest_configure(config: Config) -> None: + # We should always have a cache as cache provider plugin uses tryfirst=True + if config.getoption("stepwise"): + config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") + + +def pytest_sessionfinish(session: Session) -> None: + if not session.config.getoption("stepwise"): + assert session.config.cache is not None + # Clear the list of failing tests if the plugin is not active. + session.config.cache.set(STEPWISE_CACHE_DIR, []) + + +class StepwisePlugin: + def __init__(self, config: Config) -> None: + self.config = config + self.session: Optional[Session] = None + self.report_status = "" + assert config.cache is not None + self.cache: Cache = config.cache + self.lastfailed: Optional[str] = self.cache.get(STEPWISE_CACHE_DIR, None) + self.skip: bool = config.getoption("stepwise_skip") + + def pytest_sessionstart(self, session: Session) -> None: + self.session = session + + def pytest_collection_modifyitems( + self, config: Config, items: List[nodes.Item] + ) -> None: + if not self.lastfailed: + self.report_status = "no previously failed tests, not skipping." + return + + # check all item nodes until we find a match on last failed + failed_index = None + for index, item in enumerate(items): + if item.nodeid == self.lastfailed: + failed_index = index + break + + # If the previously failed test was not found among the test items, + # do not skip any tests. + if failed_index is None: + self.report_status = "previously failed test not found, not skipping." + else: + self.report_status = f"skipping {failed_index} already passed items." + deselected = items[:failed_index] + del items[:failed_index] + config.hook.pytest_deselected(items=deselected) + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + if self.skip: + # Remove test from the failed ones (if it exists) and unset the skip option + # to make sure the following tests will not be skipped. + if report.nodeid == self.lastfailed: + self.lastfailed = None + + self.skip = False + else: + # Mark test as the last failing and interrupt the test session. + self.lastfailed = report.nodeid + assert self.session is not None + self.session.shouldstop = ( + "Test failed, continuing from this test next run." + ) + + else: + # If the test was actually run and did pass. + if report.when == "call": + # Remove test from the failed ones, if exists. + if report.nodeid == self.lastfailed: + self.lastfailed = None + + def pytest_report_collectionfinish(self) -> Optional[str]: + if self.config.getoption("verbose") >= 0 and self.report_status: + return f"stepwise: {self.report_status}" + return None + + def pytest_sessionfinish(self) -> None: + self.cache.set(STEPWISE_CACHE_DIR, self.lastfailed) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/store.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/store.py new file mode 100644 index 0000000..e5008cf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/store.py @@ -0,0 +1,125 @@ +from typing import Any +from typing import cast +from typing import Dict +from typing import Generic +from typing import TypeVar +from typing import Union + + +__all__ = ["Store", "StoreKey"] + + +T = TypeVar("T") +D = TypeVar("D") + + +class StoreKey(Generic[T]): + """StoreKey is an object used as a key to a Store. + + A StoreKey is associated with the type T of the value of the key. + + A StoreKey is unique and cannot conflict with another key. + """ + + __slots__ = () + + +class Store: + """Store is a type-safe heterogenous mutable mapping that + allows keys and value types to be defined separately from + where it (the Store) is created. + + Usually you will be given an object which has a ``Store``: + + .. code-block:: python + + store: Store = some_object.store + + If a module wants to store data in this Store, it creates StoreKeys + for its keys (at the module level): + + .. code-block:: python + + some_str_key = StoreKey[str]() + some_bool_key = StoreKey[bool]() + + To store information: + + .. code-block:: python + + # Value type must match the key. + store[some_str_key] = "value" + store[some_bool_key] = True + + To retrieve the information: + + .. code-block:: python + + # The static type of some_str is str. + some_str = store[some_str_key] + # The static type of some_bool is bool. + some_bool = store[some_bool_key] + + Why use this? + ------------- + + Problem: module Internal defines an object. Module External, which + module Internal doesn't know about, receives the object and wants to + attach information to it, to be retrieved later given the object. + + Bad solution 1: Module External assigns private attributes directly on + the object. This doesn't work well because the type checker doesn't + know about these attributes and it complains about undefined attributes. + + Bad solution 2: module Internal adds a ``Dict[str, Any]`` attribute to + the object. Module External stores its data in private keys of this dict. + This doesn't work well because retrieved values are untyped. + + Good solution: module Internal adds a ``Store`` to the object. Module + External mints StoreKeys for its own keys. Module External stores and + retrieves its data using these keys. + """ + + __slots__ = ("_store",) + + def __init__(self) -> None: + self._store: Dict[StoreKey[Any], object] = {} + + def __setitem__(self, key: StoreKey[T], value: T) -> None: + """Set a value for key.""" + self._store[key] = value + + def __getitem__(self, key: StoreKey[T]) -> T: + """Get the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + return cast(T, self._store[key]) + + def get(self, key: StoreKey[T], default: D) -> Union[T, D]: + """Get the value for key, or return default if the key wasn't set + before.""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key: StoreKey[T], default: T) -> T: + """Return the value of key if already set, otherwise set the value + of key to default and return default.""" + try: + return self[key] + except KeyError: + self[key] = default + return default + + def __delitem__(self, key: StoreKey[T]) -> None: + """Delete the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + del self._store[key] + + def __contains__(self, key: StoreKey[T]) -> bool: + """Return whether key was set.""" + return key in self._store diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/terminal.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/terminal.py new file mode 100644 index 0000000..fbfb09a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/terminal.py @@ -0,0 +1,1405 @@ +"""Terminal reporting of the full testing process. + +This is a good source for looking at the various reporting hooks. +""" +import argparse +import datetime +import inspect +import platform +import sys +import warnings +from collections import Counter +from functools import partial +from pathlib import Path +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Set +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import attr +import pluggy +import py + +import _pytest._version +from _pytest import nodes +from _pytest import timing +from _pytest._code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._io.wcwidth import wcswidth +from _pytest.compat import final +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.reports import BaseReport +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + +if TYPE_CHECKING: + from typing_extensions import Literal + + from _pytest.main import Session + + +REPORT_COLLECTING_RESOLUTION = 0.5 + +KNOWN_TYPES = ( + "failed", + "passed", + "skipped", + "deselected", + "xfailed", + "xpassed", + "warnings", + "error", +) + +_REPORTCHARS_DEFAULT = "fE" + + +class MoreQuietAction(argparse.Action): + """A modified copy of the argparse count action which counts down and updates + the legacy quiet attribute at the same time. + + Used to unify verbosity handling. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: object = None, + required: bool = False, + help: Optional[str] = None, + ) -> None: + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + default=default, + required=required, + help=help, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: Union[str, Sequence[object], None], + option_string: Optional[str] = None, + ) -> None: + new_count = getattr(namespace, self.dest, 0) - 1 + setattr(namespace, self.dest, new_count) + # todo Deprecate config.quiet + namespace.quiet = getattr(namespace, "quiet", 0) + 1 + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "reporting", after="general") + group._addoption( + "-v", + "--verbose", + action="count", + default=0, + dest="verbose", + help="increase verbosity.", + ) + group._addoption( + "--no-header", + action="store_true", + default=False, + dest="no_header", + help="disable header", + ) + group._addoption( + "--no-summary", + action="store_true", + default=False, + dest="no_summary", + help="disable summary", + ) + group._addoption( + "-q", + "--quiet", + action=MoreQuietAction, + default=0, + dest="verbose", + help="decrease verbosity.", + ) + group._addoption( + "--verbosity", + dest="verbose", + type=int, + default=0, + help="set verbosity. Default is 0.", + ) + group._addoption( + "-r", + action="store", + dest="reportchars", + default=_REPORTCHARS_DEFAULT, + metavar="chars", + help="show extra test summary info as specified by chars: (f)ailed, " + "(E)rror, (s)kipped, (x)failed, (X)passed, " + "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " + "(w)arnings are enabled by default (see --disable-warnings), " + "'N' can be used to reset the list. (default: 'fE').", + ) + group._addoption( + "--disable-warnings", + "--disable-pytest-warnings", + default=False, + dest="disable_warnings", + action="store_true", + help="disable warnings summary", + ) + group._addoption( + "-l", + "--showlocals", + action="store_true", + dest="showlocals", + default=False, + help="show locals in tracebacks (disabled by default).", + ) + group._addoption( + "--tb", + metavar="style", + action="store", + dest="tbstyle", + default="auto", + choices=["auto", "long", "short", "no", "line", "native"], + help="traceback print mode (auto/long/short/line/native/no).", + ) + group._addoption( + "--show-capture", + action="store", + dest="showcapture", + choices=["no", "stdout", "stderr", "log", "all"], + default="all", + help="Controls how captured stdout/stderr/log is shown on failed tests. " + "Default is 'all'.", + ) + group._addoption( + "--fulltrace", + "--full-trace", + action="store_true", + default=False, + help="don't cut any tracebacks (default is to cut).", + ) + group._addoption( + "--color", + metavar="color", + action="store", + dest="color", + default="auto", + choices=["yes", "no", "auto"], + help="color terminal output (yes/no/auto).", + ) + group._addoption( + "--code-highlight", + default="yes", + choices=["yes", "no"], + help="Whether code should be highlighted (only if --color is also enabled)", + ) + + parser.addini( + "console_output_style", + help='console output: "classic", or with additional progress information ("progress" (percentage) | "count").', + default="progress", + ) + + +def pytest_configure(config: Config) -> None: + reporter = TerminalReporter(config, sys.stdout) + config.pluginmanager.register(reporter, "terminalreporter") + if config.option.debug or config.option.traceconfig: + + def mywriter(tags, args): + msg = " ".join(map(str, args)) + reporter.write_line("[traceconfig] " + msg) + + config.trace.root.setprocessor("pytest:config", mywriter) + + +def getreportopt(config: Config) -> str: + reportchars: str = config.option.reportchars + + old_aliases = {"F", "S"} + reportopts = "" + for char in reportchars: + if char in old_aliases: + char = char.lower() + if char == "a": + reportopts = "sxXEf" + elif char == "A": + reportopts = "PpsxXEf" + elif char == "N": + reportopts = "" + elif char not in reportopts: + reportopts += char + + if not config.option.disable_warnings and "w" not in reportopts: + reportopts = "w" + reportopts + elif config.option.disable_warnings and "w" in reportopts: + reportopts = reportopts.replace("w", "") + + return reportopts + + +@hookimpl(trylast=True) # after _pytest.runner +def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]: + letter = "F" + if report.passed: + letter = "." + elif report.skipped: + letter = "s" + + outcome: str = report.outcome + if report.when in ("collect", "setup", "teardown") and outcome == "failed": + outcome = "error" + letter = "E" + + return outcome, letter, outcome.upper() + + +@attr.s +class WarningReport: + """Simple structure to hold warnings information captured by ``pytest_warning_recorded``. + + :ivar str message: + User friendly message about the warning. + :ivar str|None nodeid: + nodeid that generated the warning (see ``get_location``). + :ivar tuple|py.path.local fslocation: + File system location of the source of the warning (see ``get_location``). + """ + + message = attr.ib(type=str) + nodeid = attr.ib(type=Optional[str], default=None) + fslocation = attr.ib( + type=Optional[Union[Tuple[str, int], py.path.local]], default=None + ) + count_towards_summary = True + + def get_location(self, config: Config) -> Optional[str]: + """Return the more user-friendly information about the location of a warning, or None.""" + if self.nodeid: + return self.nodeid + if self.fslocation: + if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2: + filename, linenum = self.fslocation[:2] + relpath = bestrelpath( + config.invocation_params.dir, absolutepath(filename) + ) + return f"{relpath}:{linenum}" + else: + return str(self.fslocation) + return None + + +@final +class TerminalReporter: + def __init__(self, config: Config, file: Optional[TextIO] = None) -> None: + import _pytest.config + + self.config = config + self._numcollected = 0 + self._session: Optional[Session] = None + self._showfspath: Optional[bool] = None + + self.stats: Dict[str, List[Any]] = {} + self._main_color: Optional[str] = None + self._known_types: Optional[List[str]] = None + self.startdir = config.invocation_dir + self.startpath = config.invocation_params.dir + if file is None: + file = sys.stdout + self._tw = _pytest.config.create_terminal_writer(config, file) + self._screen_width = self._tw.fullwidth + self.currentfspath: Union[None, Path, str, int] = None + self.reportchars = getreportopt(config) + self.hasmarkup = self._tw.hasmarkup + self.isatty = file.isatty() + self._progress_nodeids_reported: Set[str] = set() + self._show_progress_info = self._determine_show_progress_info() + self._collect_report_last_write: Optional[float] = None + self._already_displayed_warnings: Optional[int] = None + self._keyboardinterrupt_memo: Optional[ExceptionRepr] = None + + def _determine_show_progress_info(self) -> "Literal['progress', 'count', False]": + """Return whether we should display progress information based on the current config.""" + # do not show progress if we are not capturing output (#3038) + if self.config.getoption("capture", "no") == "no": + return False + # do not show progress if we are showing fixture setup/teardown + if self.config.getoption("setupshow", False): + return False + cfg: str = self.config.getini("console_output_style") + if cfg == "progress": + return "progress" + elif cfg == "count": + return "count" + else: + return False + + @property + def verbosity(self) -> int: + verbosity: int = self.config.option.verbose + return verbosity + + @property + def showheader(self) -> bool: + return self.verbosity >= 0 + + @property + def no_header(self) -> bool: + return bool(self.config.option.no_header) + + @property + def no_summary(self) -> bool: + return bool(self.config.option.no_summary) + + @property + def showfspath(self) -> bool: + if self._showfspath is None: + return self.verbosity >= 0 + return self._showfspath + + @showfspath.setter + def showfspath(self, value: Optional[bool]) -> None: + self._showfspath = value + + @property + def showlongtestinfo(self) -> bool: + return self.verbosity > 0 + + def hasopt(self, char: str) -> bool: + char = {"xfailed": "x", "skipped": "s"}.get(char, char) + return char in self.reportchars + + def write_fspath_result(self, nodeid: str, res, **markup: bool) -> None: + fspath = self.config.rootpath / nodeid.split("::")[0] + if self.currentfspath is None or fspath != self.currentfspath: + if self.currentfspath is not None and self._show_progress_info: + self._write_progress_information_filling_space() + self.currentfspath = fspath + relfspath = bestrelpath(self.startpath, fspath) + self._tw.line() + self._tw.write(relfspath + " ") + self._tw.write(res, flush=True, **markup) + + def write_ensure_prefix(self, prefix: str, extra: str = "", **kwargs) -> None: + if self.currentfspath != prefix: + self._tw.line() + self.currentfspath = prefix + self._tw.write(prefix) + if extra: + self._tw.write(extra, **kwargs) + self.currentfspath = -2 + + def ensure_newline(self) -> None: + if self.currentfspath: + self._tw.line() + self.currentfspath = None + + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + self._tw.write(content, flush=flush, **markup) + + def flush(self) -> None: + self._tw.flush() + + def write_line(self, line: Union[str, bytes], **markup: bool) -> None: + if not isinstance(line, str): + line = str(line, errors="replace") + self.ensure_newline() + self._tw.line(line, **markup) + + def rewrite(self, line: str, **markup: bool) -> None: + """Rewinds the terminal cursor to the beginning and writes the given line. + + :param erase: + If True, will also add spaces until the full terminal width to ensure + previous lines are properly erased. + + The rest of the keyword arguments are markup instructions. + """ + erase = markup.pop("erase", False) + if erase: + fill_count = self._tw.fullwidth - len(line) - 1 + fill = " " * fill_count + else: + fill = "" + line = str(line) + self._tw.write("\r" + line + fill, **markup) + + def write_sep( + self, + sep: str, + title: Optional[str] = None, + fullwidth: Optional[int] = None, + **markup: bool, + ) -> None: + self.ensure_newline() + self._tw.sep(sep, title, fullwidth, **markup) + + def section(self, title: str, sep: str = "=", **kw: bool) -> None: + self._tw.sep(sep, title, **kw) + + def line(self, msg: str, **kw: bool) -> None: + self._tw.line(msg, **kw) + + def _add_stats(self, category: str, items: Sequence[Any]) -> None: + set_main_color = category not in self.stats + self.stats.setdefault(category, []).extend(items) + if set_main_color: + self._set_main_color() + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: + for line in str(excrepr).split("\n"): + self.write_line("INTERNALERROR> " + line) + return True + + def pytest_warning_recorded( + self, warning_message: warnings.WarningMessage, nodeid: str, + ) -> None: + from _pytest.warnings import warning_record_to_str + + fslocation = warning_message.filename, warning_message.lineno + message = warning_record_to_str(warning_message) + + warning_report = WarningReport( + fslocation=fslocation, message=message, nodeid=nodeid + ) + self._add_stats("warnings", [warning_report]) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: + if self.config.option.traceconfig: + msg = f"PLUGIN registered: {plugin}" + # XXX This event may happen during setup/teardown time + # which unfortunately captures our output here + # which garbles our output if we use self.write_line. + self.write_line(msg) + + def pytest_deselected(self, items: Sequence[Item]) -> None: + self._add_stats("deselected", items) + + def pytest_runtest_logstart( + self, nodeid: str, location: Tuple[str, Optional[int], str] + ) -> None: + # Ensure that the path is printed before the + # 1st test of a module starts running. + if self.showlongtestinfo: + line = self._locationline(nodeid, *location) + self.write_ensure_prefix(line, "") + self.flush() + elif self.showfspath: + self.write_fspath_result(nodeid, "") + self.flush() + + def pytest_runtest_logreport(self, report: TestReport) -> None: + self._tests_ran = True + rep = report + res: Tuple[ + str, str, Union[str, Tuple[str, Mapping[str, bool]]] + ] = self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + category, letter, word = res + if not isinstance(word, tuple): + markup = None + else: + word, markup = word + self._add_stats(category, [rep]) + if not letter and not word: + # Probably passed setup/teardown. + return + running_xdist = hasattr(rep, "node") + if markup is None: + was_xfail = hasattr(report, "wasxfail") + if rep.passed and not was_xfail: + markup = {"green": True} + elif rep.passed and was_xfail: + markup = {"yellow": True} + elif rep.failed: + markup = {"red": True} + elif rep.skipped: + markup = {"yellow": True} + else: + markup = {} + if self.verbosity <= 0: + self._tw.write(letter, **markup) + else: + self._progress_nodeids_reported.add(rep.nodeid) + line = self._locationline(rep.nodeid, *rep.location) + if not running_xdist: + self.write_ensure_prefix(line, word, **markup) + if rep.skipped or hasattr(report, "wasxfail"): + available_width = ( + (self._tw.fullwidth - self._tw.width_of_current_line) + - len(" [100%]") + - 1 + ) + reason = _get_raw_skip_reason(rep) + reason_ = _format_trimmed(" ({})", reason, available_width) + if reason and reason_ is not None: + self._tw.write(reason_) + if self._show_progress_info: + self._write_progress_information_filling_space() + else: + self.ensure_newline() + self._tw.write("[%s]" % rep.node.gateway.id) + if self._show_progress_info: + self._tw.write( + self._get_progress_information_message() + " ", cyan=True + ) + else: + self._tw.write(" ") + self._tw.write(word, **markup) + self._tw.write(" " + line) + self.currentfspath = -2 + self.flush() + + @property + def _is_last_item(self) -> bool: + assert self._session is not None + return len(self._progress_nodeids_reported) == self._session.testscollected + + def pytest_runtest_logfinish(self, nodeid: str) -> None: + assert self._session + if self.verbosity <= 0 and self._show_progress_info: + if self._show_progress_info == "count": + num_tests = self._session.testscollected + progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) + else: + progress_length = len(" [100%]") + + self._progress_nodeids_reported.add(nodeid) + + if self._is_last_item: + self._write_progress_information_filling_space() + else: + main_color, _ = self._get_main_color() + w = self._width_of_current_line + past_edge = w + progress_length + 1 >= self._screen_width + if past_edge: + msg = self._get_progress_information_message() + self._tw.write(msg + "\n", **{main_color: True}) + + def _get_progress_information_message(self) -> str: + assert self._session + collected = self._session.testscollected + if self._show_progress_info == "count": + if collected: + progress = self._progress_nodeids_reported + counter_format = "{{:{}d}}".format(len(str(collected))) + format_string = f" [{counter_format}/{{}}]" + return format_string.format(len(progress), collected) + return f" [ {collected} / {collected} ]" + else: + if collected: + return " [{:3d}%]".format( + len(self._progress_nodeids_reported) * 100 // collected + ) + return " [100%]" + + def _write_progress_information_filling_space(self) -> None: + color, _ = self._get_main_color() + msg = self._get_progress_information_message() + w = self._width_of_current_line + fill = self._tw.fullwidth - w - 1 + self.write(msg.rjust(fill), flush=True, **{color: True}) + + @property + def _width_of_current_line(self) -> int: + """Return the width of the current line.""" + return self._tw.width_of_current_line + + def pytest_collection(self) -> None: + if self.isatty: + if self.config.option.verbose >= 0: + self.write("collecting ... ", flush=True, bold=True) + self._collect_report_last_write = timing.time() + elif self.config.option.verbose >= 1: + self.write("collecting ... ", flush=True, bold=True) + + def pytest_collectreport(self, report: CollectReport) -> None: + if report.failed: + self._add_stats("error", [report]) + elif report.skipped: + self._add_stats("skipped", [report]) + items = [x for x in report.result if isinstance(x, Item)] + self._numcollected += len(items) + if self.isatty: + self.report_collect() + + def report_collect(self, final: bool = False) -> None: + if self.config.option.verbose < 0: + return + + if not final: + # Only write "collecting" report every 0.5s. + t = timing.time() + if ( + self._collect_report_last_write is not None + and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION + ): + return + self._collect_report_last_write = t + + errors = len(self.stats.get("error", [])) + skipped = len(self.stats.get("skipped", [])) + deselected = len(self.stats.get("deselected", [])) + selected = self._numcollected - errors - skipped - deselected + if final: + line = "collected " + else: + line = "collecting " + line += ( + str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") + ) + if errors: + line += " / %d error%s" % (errors, "s" if errors != 1 else "") + if deselected: + line += " / %d deselected" % deselected + if skipped: + line += " / %d skipped" % skipped + if self._numcollected > selected > 0: + line += " / %d selected" % selected + if self.isatty: + self.rewrite(line, bold=True, erase=True) + if final: + self.write("\n") + else: + self.write_line(line) + + @hookimpl(trylast=True) + def pytest_sessionstart(self, session: "Session") -> None: + self._session = session + self._sessionstarttime = timing.time() + if not self.showheader: + return + self.write_sep("=", "test session starts", bold=True) + verinfo = platform.python_version() + if not self.no_header: + msg = f"platform {sys.platform} -- Python {verinfo}" + pypy_version_info = getattr(sys, "pypy_version_info", None) + if pypy_version_info: + verinfo = ".".join(map(str, pypy_version_info[:3])) + msg += "[pypy-{}-{}]".format(verinfo, pypy_version_info[3]) + msg += ", pytest-{}, py-{}, pluggy-{}".format( + _pytest._version.version, py.__version__, pluggy.__version__ + ) + if ( + self.verbosity > 0 + or self.config.option.debug + or getattr(self.config.option, "pastebin", None) + ): + msg += " -- " + str(sys.executable) + self.write_line(msg) + lines = self.config.hook.pytest_report_header( + config=self.config, startdir=self.startdir + ) + self._write_report_lines_from_hooks(lines) + + def _write_report_lines_from_hooks( + self, lines: Sequence[Union[str, Sequence[str]]] + ) -> None: + for line_or_lines in reversed(lines): + if isinstance(line_or_lines, str): + self.write_line(line_or_lines) + else: + for line in line_or_lines: + self.write_line(line) + + def pytest_report_header(self, config: Config) -> List[str]: + line = "rootdir: %s" % config.rootpath + + if config.inipath: + line += ", configfile: " + bestrelpath(config.rootpath, config.inipath) + + testpaths: List[str] = config.getini("testpaths") + if config.invocation_params.dir == config.rootpath and config.args == testpaths: + line += ", testpaths: {}".format(", ".join(testpaths)) + + result = [line] + + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + result.append("plugins: %s" % ", ".join(_plugin_nameversions(plugininfo))) + return result + + def pytest_collection_finish(self, session: "Session") -> None: + self.report_collect(True) + + lines = self.config.hook.pytest_report_collectionfinish( + config=self.config, startdir=self.startdir, items=session.items + ) + self._write_report_lines_from_hooks(lines) + + if self.config.getoption("collectonly"): + if session.items: + if self.config.option.verbose > -1: + self._tw.line("") + self._printcollecteditems(session.items) + + failed = self.stats.get("failed") + if failed: + self._tw.sep("!", "collection failures") + for rep in failed: + rep.toterminal(self._tw) + + def _printcollecteditems(self, items: Sequence[Item]) -> None: + # To print out items and their parent collectors + # we take care to leave out Instances aka () + # because later versions are going to get rid of them anyway. + if self.config.option.verbose < 0: + if self.config.option.verbose < -1: + counts = Counter(item.nodeid.split("::", 1)[0] for item in items) + for name, count in sorted(counts.items()): + self._tw.line("%s: %d" % (name, count)) + else: + for item in items: + self._tw.line(item.nodeid) + return + stack: List[Node] = [] + indent = "" + for item in items: + needed_collectors = item.listchain()[1:] # strip root node + while stack: + if stack == needed_collectors[: len(stack)]: + break + stack.pop() + for col in needed_collectors[len(stack) :]: + stack.append(col) + if col.name == "()": # Skip Instances. + continue + indent = (len(stack) - 1) * " " + self._tw.line(f"{indent}{col}") + if self.config.option.verbose >= 1: + obj = getattr(col, "obj", None) + doc = inspect.getdoc(obj) if obj else None + if doc: + for line in doc.splitlines(): + self._tw.line("{}{}".format(indent + " ", line)) + + @hookimpl(hookwrapper=True) + def pytest_sessionfinish( + self, session: "Session", exitstatus: Union[int, ExitCode] + ): + outcome = yield + outcome.get_result() + self._tw.line("") + summary_exit_codes = ( + ExitCode.OK, + ExitCode.TESTS_FAILED, + ExitCode.INTERRUPTED, + ExitCode.USAGE_ERROR, + ExitCode.NO_TESTS_COLLECTED, + ) + if exitstatus in summary_exit_codes and not self.no_summary: + self.config.hook.pytest_terminal_summary( + terminalreporter=self, exitstatus=exitstatus, config=self.config + ) + if session.shouldfail: + self.write_sep("!", str(session.shouldfail), red=True) + if exitstatus == ExitCode.INTERRUPTED: + self._report_keyboardinterrupt() + self._keyboardinterrupt_memo = None + elif session.shouldstop: + self.write_sep("!", str(session.shouldstop), red=True) + self.summary_stats() + + @hookimpl(hookwrapper=True) + def pytest_terminal_summary(self) -> Generator[None, None, None]: + self.summary_errors() + self.summary_failures() + self.summary_warnings() + self.summary_passes() + yield + self.short_test_summary() + # Display any extra warnings from teardown here (if any). + self.summary_warnings() + + def pytest_keyboard_interrupt(self, excinfo: ExceptionInfo[BaseException]) -> None: + self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) + + def pytest_unconfigure(self) -> None: + if self._keyboardinterrupt_memo is not None: + self._report_keyboardinterrupt() + + def _report_keyboardinterrupt(self) -> None: + excrepr = self._keyboardinterrupt_memo + assert excrepr is not None + assert excrepr.reprcrash is not None + msg = excrepr.reprcrash.message + self.write_sep("!", msg) + if "KeyboardInterrupt" in msg: + if self.config.option.fulltrace: + excrepr.toterminal(self._tw) + else: + excrepr.reprcrash.toterminal(self._tw) + self._tw.line( + "(to show a full traceback on KeyboardInterrupt use --full-trace)", + yellow=True, + ) + + def _locationline(self, nodeid, fspath, lineno, domain): + def mkrel(nodeid): + line = self.config.cwd_relative_nodeid(nodeid) + if domain and line.endswith(domain): + line = line[: -len(domain)] + values = domain.split("[") + values[0] = values[0].replace(".", "::") # don't replace '.' in params + line += "[".join(values) + return line + + # collect_fspath comes from testid which has a "/"-normalized path. + + if fspath: + res = mkrel(nodeid) + if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( + "\\", nodes.SEP + ): + res += " <- " + bestrelpath(self.startpath, fspath) + else: + res = "[location]" + return res + " " + + def _getfailureheadline(self, rep): + head_line = rep.head_line + if head_line: + return head_line + return "test session" # XXX? + + def _getcrashline(self, rep): + try: + return str(rep.longrepr.reprcrash) + except AttributeError: + try: + return str(rep.longrepr)[:50] + except AttributeError: + return "" + + # + # Summaries for sessionfinish. + # + def getreports(self, name: str): + values = [] + for x in self.stats.get(name, []): + if not hasattr(x, "_pdbshown"): + values.append(x) + return values + + def summary_warnings(self) -> None: + if self.hasopt("w"): + all_warnings: Optional[List[WarningReport]] = self.stats.get("warnings") + if not all_warnings: + return + + final = self._already_displayed_warnings is not None + if final: + warning_reports = all_warnings[self._already_displayed_warnings :] + else: + warning_reports = all_warnings + self._already_displayed_warnings = len(warning_reports) + if not warning_reports: + return + + reports_grouped_by_message: Dict[str, List[WarningReport]] = {} + for wr in warning_reports: + reports_grouped_by_message.setdefault(wr.message, []).append(wr) + + def collapsed_location_report(reports: List[WarningReport]) -> str: + locations = [] + for w in reports: + location = w.get_location(self.config) + if location: + locations.append(location) + + if len(locations) < 10: + return "\n".join(map(str, locations)) + + counts_by_filename = Counter( + str(loc).split("::", 1)[0] for loc in locations + ) + return "\n".join( + "{}: {} warning{}".format(k, v, "s" if v > 1 else "") + for k, v in counts_by_filename.items() + ) + + title = "warnings summary (final)" if final else "warnings summary" + self.write_sep("=", title, yellow=True, bold=False) + for message, message_reports in reports_grouped_by_message.items(): + maybe_location = collapsed_location_report(message_reports) + if maybe_location: + self._tw.line(maybe_location) + lines = message.splitlines() + indented = "\n".join(" " + x for x in lines) + message = indented.rstrip() + else: + message = message.rstrip() + self._tw.line(message) + self._tw.line() + self._tw.line("-- Docs: https://docs.pytest.org/en/stable/warnings.html") + + def summary_passes(self) -> None: + if self.config.option.tbstyle != "no": + if self.hasopt("P"): + reports: List[TestReport] = self.getreports("passed") + if not reports: + return + self.write_sep("=", "PASSES") + for rep in reports: + if rep.sections: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, green=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def _get_teardown_reports(self, nodeid: str) -> List[TestReport]: + reports = self.getreports("") + return [ + report + for report in reports + if report.when == "teardown" and report.nodeid == nodeid + ] + + def _handle_teardown_sections(self, nodeid: str) -> None: + for report in self._get_teardown_reports(nodeid): + self.print_teardown_sections(report) + + def print_teardown_sections(self, rep: TestReport) -> None: + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + if "teardown" in secname: + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_failures(self) -> None: + if self.config.option.tbstyle != "no": + reports: List[BaseReport] = self.getreports("failed") + if not reports: + return + self.write_sep("=", "FAILURES") + if self.config.option.tbstyle == "line": + for rep in reports: + line = self._getcrashline(rep) + self.write_line(line) + else: + for rep in reports: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def summary_errors(self) -> None: + if self.config.option.tbstyle != "no": + reports: List[BaseReport] = self.getreports("error") + if not reports: + return + self.write_sep("=", "ERRORS") + for rep in self.stats["error"]: + msg = self._getfailureheadline(rep) + if rep.when == "collect": + msg = "ERROR collecting " + msg + else: + msg = f"ERROR at {rep.when} of {msg}" + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + + def _outrep_summary(self, rep: BaseReport) -> None: + rep.toterminal(self._tw) + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_stats(self) -> None: + if self.verbosity < -1: + return + + session_duration = timing.time() - self._sessionstarttime + (parts, main_color) = self.build_summary_stats_line() + line_parts = [] + + display_sep = self.verbosity >= 0 + if display_sep: + fullwidth = self._tw.fullwidth + for text, markup in parts: + with_markup = self._tw.markup(text, **markup) + if display_sep: + fullwidth += len(with_markup) - len(text) + line_parts.append(with_markup) + msg = ", ".join(line_parts) + + main_markup = {main_color: True} + duration = " in {}".format(format_session_duration(session_duration)) + duration_with_markup = self._tw.markup(duration, **main_markup) + if display_sep: + fullwidth += len(duration_with_markup) - len(duration) + msg += duration_with_markup + + if display_sep: + markup_for_end_sep = self._tw.markup("", **main_markup) + if markup_for_end_sep.endswith("\x1b[0m"): + markup_for_end_sep = markup_for_end_sep[:-4] + fullwidth += len(markup_for_end_sep) + msg += markup_for_end_sep + + if display_sep: + self.write_sep("=", msg, fullwidth=fullwidth, **main_markup) + else: + self.write_line(msg, **main_markup) + + def short_test_summary(self) -> None: + if not self.reportchars: + return + + def show_simple(stat, lines: List[str]) -> None: + failed = self.stats.get(stat, []) + if not failed: + return + termwidth = self._tw.fullwidth + config = self.config + for rep in failed: + line = _get_line_with_reprcrash_message(config, rep, termwidth) + lines.append(line) + + def show_xfailed(lines: List[str]) -> None: + xfailed = self.stats.get("xfailed", []) + for rep in xfailed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + lines.append(f"{verbose_word} {pos}") + reason = rep.wasxfail + if reason: + lines.append(" " + str(reason)) + + def show_xpassed(lines: List[str]) -> None: + xpassed = self.stats.get("xpassed", []) + for rep in xpassed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + reason = rep.wasxfail + lines.append(f"{verbose_word} {pos} {reason}") + + def show_skipped(lines: List[str]) -> None: + skipped: List[CollectReport] = self.stats.get("skipped", []) + fskips = _folded_skips(self.startpath, skipped) if skipped else [] + if not fskips: + return + verbose_word = skipped[0]._get_verbose_word(self.config) + for num, fspath, lineno, reason in fskips: + if reason.startswith("Skipped: "): + reason = reason[9:] + if lineno is not None: + lines.append( + "%s [%d] %s:%d: %s" + % (verbose_word, num, fspath, lineno, reason) + ) + else: + lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) + + REPORTCHAR_ACTIONS: Mapping[str, Callable[[List[str]], None]] = { + "x": show_xfailed, + "X": show_xpassed, + "f": partial(show_simple, "failed"), + "s": show_skipped, + "p": partial(show_simple, "passed"), + "E": partial(show_simple, "error"), + } + + lines: List[str] = [] + for char in self.reportchars: + action = REPORTCHAR_ACTIONS.get(char) + if action: # skipping e.g. "P" (passed with output) here. + action(lines) + + if lines: + self.write_sep("=", "short test summary info") + for line in lines: + self.write_line(line) + + def _get_main_color(self) -> Tuple[str, List[str]]: + if self._main_color is None or self._known_types is None or self._is_last_item: + self._set_main_color() + assert self._main_color + assert self._known_types + return self._main_color, self._known_types + + def _determine_main_color(self, unknown_type_seen: bool) -> str: + stats = self.stats + if "failed" in stats or "error" in stats: + main_color = "red" + elif "warnings" in stats or "xpassed" in stats or unknown_type_seen: + main_color = "yellow" + elif "passed" in stats or not self._is_last_item: + main_color = "green" + else: + main_color = "yellow" + return main_color + + def _set_main_color(self) -> None: + unknown_types: List[str] = [] + for found_type in self.stats.keys(): + if found_type: # setup/teardown reports have an empty key, ignore them + if found_type not in KNOWN_TYPES and found_type not in unknown_types: + unknown_types.append(found_type) + self._known_types = list(KNOWN_TYPES) + unknown_types + self._main_color = self._determine_main_color(bool(unknown_types)) + + def build_summary_stats_line(self) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: + """ + Build the parts used in the last summary stats line. + + The summary stats line is the line shown at the end, "=== 12 passed, 2 errors in Xs===". + + This function builds a list of the "parts" that make up for the text in that line, in + the example above it would be: + + [ + ("12 passed", {"green": True}), + ("2 errors", {"red": True} + ] + + That last dict for each line is a "markup dictionary", used by TerminalWriter to + color output. + + The final color of the line is also determined by this function, and is the second + element of the returned tuple. + """ + if self.config.getoption("collectonly"): + return self._build_collect_only_summary_stats_line() + else: + return self._build_normal_summary_stats_line() + + def _get_reports_to_display(self, key: str) -> List[Any]: + """Get test/collection reports for the given status key, such as `passed` or `error`.""" + reports = self.stats.get(key, []) + return [x for x in reports if getattr(x, "count_towards_summary", True)] + + def _build_normal_summary_stats_line( + self, + ) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: + main_color, known_types = self._get_main_color() + parts = [] + + for key in known_types: + reports = self._get_reports_to_display(key) + if reports: + count = len(reports) + color = _color_for_type.get(key, _color_for_type_default) + markup = {color: True, "bold": color == main_color} + parts.append(("%d %s" % pluralize(count, key), markup)) + + if not parts: + parts = [("no tests ran", {_color_for_type_default: True})] + + return parts, main_color + + def _build_collect_only_summary_stats_line( + self, + ) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: + deselected = len(self._get_reports_to_display("deselected")) + errors = len(self._get_reports_to_display("error")) + + if self._numcollected == 0: + parts = [("no tests collected", {"yellow": True})] + main_color = "yellow" + + elif deselected == 0: + main_color = "green" + collected_output = "%d %s collected" % pluralize(self._numcollected, "test") + parts = [(collected_output, {main_color: True})] + else: + all_tests_were_deselected = self._numcollected == deselected + if all_tests_were_deselected: + main_color = "yellow" + collected_output = f"no tests collected ({deselected} deselected)" + else: + main_color = "green" + selected = self._numcollected - deselected + collected_output = f"{selected}/{self._numcollected} tests collected ({deselected} deselected)" + + parts = [(collected_output, {main_color: True})] + + if errors: + main_color = _color_for_type["error"] + parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] + + return parts, main_color + + +def _get_pos(config: Config, rep: BaseReport): + nodeid = config.cwd_relative_nodeid(rep.nodeid) + return nodeid + + +def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]: + """Format msg into format, ellipsizing it if doesn't fit in available_width. + + Returns None if even the ellipsis can't fit. + """ + # Only use the first line. + i = msg.find("\n") + if i != -1: + msg = msg[:i] + + ellipsis = "..." + format_width = wcswidth(format.format("")) + if format_width + len(ellipsis) > available_width: + return None + + if format_width + wcswidth(msg) > available_width: + available_width -= len(ellipsis) + msg = msg[:available_width] + while format_width + wcswidth(msg) > available_width: + msg = msg[:-1] + msg += ellipsis + + return format.format(msg) + + +def _get_line_with_reprcrash_message( + config: Config, rep: BaseReport, termwidth: int +) -> str: + """Get summary line for a report, trying to add reprcrash message.""" + verbose_word = rep._get_verbose_word(config) + pos = _get_pos(config, rep) + + line = f"{verbose_word} {pos}" + line_width = wcswidth(line) + + try: + # Type ignored intentionally -- possible AttributeError expected. + msg = rep.longrepr.reprcrash.message # type: ignore[union-attr] + except AttributeError: + pass + else: + available_width = termwidth - line_width + msg = _format_trimmed(" - {}", msg, available_width) + if msg is not None: + line += msg + + return line + + +def _folded_skips( + startpath: Path, skipped: Sequence[CollectReport], +) -> List[Tuple[int, str, Optional[int], str]]: + d: Dict[Tuple[str, Optional[int], str], List[CollectReport]] = {} + for event in skipped: + assert event.longrepr is not None + assert isinstance(event.longrepr, tuple), (event, event.longrepr) + assert len(event.longrepr) == 3, (event, event.longrepr) + fspath, lineno, reason = event.longrepr + # For consistency, report all fspaths in relative form. + fspath = bestrelpath(startpath, Path(fspath)) + keywords = getattr(event, "keywords", {}) + # Folding reports with global pytestmark variable. + # This is a workaround, because for now we cannot identify the scope of a skip marker + # TODO: Revisit after marks scope would be fixed. + if ( + event.when == "setup" + and "skip" in keywords + and "pytestmark" not in keywords + ): + key: Tuple[str, Optional[int], str] = (fspath, None, reason) + else: + key = (fspath, lineno, reason) + d.setdefault(key, []).append(event) + values: List[Tuple[int, str, Optional[int], str]] = [] + for key, events in d.items(): + values.append((len(events), *key)) + return values + + +_color_for_type = { + "failed": "red", + "error": "red", + "warnings": "yellow", + "passed": "green", +} +_color_for_type_default = "yellow" + + +def pluralize(count: int, noun: str) -> Tuple[int, str]: + # No need to pluralize words such as `failed` or `passed`. + if noun not in ["error", "warnings", "test"]: + return count, noun + + # The `warnings` key is plural. To avoid API breakage, we keep it that way but + # set it to singular here so we can determine plurality in the same way as we do + # for `error`. + noun = noun.replace("warnings", "warning") + + return count, noun + "s" if count != 1 else noun + + +def _plugin_nameversions(plugininfo) -> List[str]: + values: List[str] = [] + for plugin, dist in plugininfo: + # Gets us name and version! + name = "{dist.project_name}-{dist.version}".format(dist=dist) + # Questionable convenience, but it keeps things short. + if name.startswith("pytest-"): + name = name[7:] + # We decided to print python package names they can have more than one plugin. + if name not in values: + values.append(name) + return values + + +def format_session_duration(seconds: float) -> str: + """Format the given seconds in a human readable manner to show in the final summary.""" + if seconds < 60: + return f"{seconds:.2f}s" + else: + dt = datetime.timedelta(seconds=int(seconds)) + return f"{seconds:.2f}s ({dt})" + + +def _get_raw_skip_reason(report: TestReport) -> str: + """Get the reason string of a skip/xfail/xpass test report. + + The string is just the part given by the user. + """ + if hasattr(report, "wasxfail"): + reason = cast(str, report.wasxfail) + if reason.startswith("reason: "): + reason = reason[len("reason: ") :] + return reason + else: + assert report.skipped + assert isinstance(report.longrepr, tuple) + _, _, reason = report.longrepr + if reason.startswith("Skipped: "): + reason = reason[len("Skipped: ") :] + elif reason == "Skipped": + reason = "" + return reason diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/threadexception.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/threadexception.py new file mode 100644 index 0000000..1c1f62f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/threadexception.py @@ -0,0 +1,90 @@ +import threading +import traceback +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Optional +from typing import Type + +import pytest + + +# Copied from cpython/Lib/test/support/threading_helper.py, with modifications. +class catch_threading_exception: + """Context manager catching threading.Thread exception using + threading.excepthook. + + Storing exc_value using a custom hook can create a reference cycle. The + reference cycle is broken explicitly when the context manager exits. + + Storing thread using a custom hook can resurrect it if it is set to an + object which is being finalized. Exiting the context manager clears the + stored object. + + Usage: + with threading_helper.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + # check the thread exception: use cm.args + ... + # cm.args attribute no longer exists at this point + # (to break a reference cycle) + """ + + def __init__(self) -> None: + # See https://github.com/python/typeshed/issues/4767 regarding the underscore. + self.args: Optional["threading._ExceptHookArgs"] = None + self._old_hook: Optional[Callable[["threading._ExceptHookArgs"], Any]] = None + + def _hook(self, args: "threading._ExceptHookArgs") -> None: + self.args = args + + def __enter__(self) -> "catch_threading_exception": + self._old_hook = threading.excepthook + threading.excepthook = self._hook + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + assert self._old_hook is not None + threading.excepthook = self._old_hook + self._old_hook = None + del self.args + + +def thread_exception_runtest_hook() -> Generator[None, None, None]: + with catch_threading_exception() as cm: + yield + if cm.args: + if cm.args.thread is not None: + thread_name = cm.args.thread.name + else: + thread_name = "" + msg = f"Exception in thread {thread_name}\n\n" + msg += "".join( + traceback.format_exception( + cm.args.exc_type, cm.args.exc_value, cm.args.exc_traceback, + ) + ) + warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + + +@pytest.hookimpl(hookwrapper=True, trylast=True) +def pytest_runtest_setup() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_call() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_teardown() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/timing.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/timing.py new file mode 100644 index 0000000..925163a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/timing.py @@ -0,0 +1,12 @@ +"""Indirection for time functions. + +We intentionally grab some "time" functions internally to avoid tests mocking "time" to affect +pytest runtime information (issue #185). + +Fixture "mock_timing" also interacts with this module for pytest's own tests. +""" +from time import perf_counter +from time import sleep +from time import time + +__all__ = ["perf_counter", "sleep", "time"] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/tmpdir.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/tmpdir.py new file mode 100644 index 0000000..a6bd383 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/tmpdir.py @@ -0,0 +1,254 @@ +"""Support for providing temporary directories to test functions.""" +import os +import re +import sys +import tempfile +from pathlib import Path +from typing import Optional + +import attr +import py + +from .pathlib import LOCK_TIMEOUT +from .pathlib import make_numbered_dir +from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf +from _pytest.compat import final +from _pytest.config import Config +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch + + +@final +@attr.s(init=False) +class TempPathFactory: + """Factory for temporary directories under the common base temp directory. + + The base directory can be configured using the ``--basetemp`` option. + """ + + _given_basetemp = attr.ib(type=Optional[Path]) + _trace = attr.ib() + _basetemp = attr.ib(type=Optional[Path]) + + def __init__( + self, + given_basetemp: Optional[Path], + trace, + basetemp: Optional[Path] = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + if given_basetemp is None: + self._given_basetemp = None + else: + # Use os.path.abspath() to get absolute path instead of resolve() as it + # does not work the same in all platforms (see #4427). + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). + self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) + self._trace = trace + self._basetemp = basetemp + + @classmethod + def from_config( + cls, config: Config, *, _ispytest: bool = False, + ) -> "TempPathFactory": + """Create a factory according to pytest configuration. + + :meta private: + """ + check_ispytest(_ispytest) + return cls( + given_basetemp=config.option.basetemp, + trace=config.trace.get("tmpdir"), + _ispytest=True, + ) + + def _ensure_relative_to_basetemp(self, basename: str) -> str: + basename = os.path.normpath(basename) + if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): + raise ValueError(f"{basename} is not a normalized and relative path") + return basename + + def mktemp(self, basename: str, numbered: bool = True) -> Path: + """Create a new temporary directory managed by the factory. + + :param basename: + Directory base name, must be a relative path. + + :param numbered: + If ``True``, ensure the directory is unique by adding a numbered + suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` + means that this function will create directories named ``"foo-0"``, + ``"foo-1"``, ``"foo-2"`` and so on. + + :returns: + The path to the new directory. + """ + basename = self._ensure_relative_to_basetemp(basename) + if not numbered: + p = self.getbasetemp().joinpath(basename) + p.mkdir(mode=0o700) + else: + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) + self._trace("mktemp", p) + return p + + def getbasetemp(self) -> Path: + """Return the base temporary directory, creating it if needed.""" + if self._basetemp is not None: + return self._basetemp + + if self._given_basetemp is not None: + basetemp = self._given_basetemp + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir(mode=0o700) + basetemp = basetemp.resolve() + else: + from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + temproot = Path(from_env or tempfile.gettempdir()).resolve() + user = get_user() or "unknown" + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.joinpath(f"pytest-of-{user}") + rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + if sys.platform != "win32": + uid = os.getuid() + rootdir_stat = rootdir.stat() + # getuid shouldn't fail, but cpython defines such a case. + # Let's hope for the best. + if uid != -1: + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", + root=rootdir, + keep=3, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, + ) + assert basetemp is not None, basetemp + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp + + +@final +@attr.s(init=False) +class TempdirFactory: + """Backward comptibility wrapper that implements :class:``py.path.local`` + for :class:``TempPathFactory``.""" + + _tmppath_factory = attr.ib(type=TempPathFactory) + + def __init__( + self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._tmppath_factory = tmppath_factory + + def mktemp(self, basename: str, numbered: bool = True) -> py.path.local: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" + return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve()) + + def getbasetemp(self) -> py.path.local: + """Backward compat wrapper for ``_tmppath_factory.getbasetemp``.""" + return py.path.local(self._tmppath_factory.getbasetemp().resolve()) + + +def get_user() -> Optional[str]: + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010).""" + import getpass + + try: + return getpass.getuser() + except (ImportError, KeyError): + return None + + +def pytest_configure(config: Config) -> None: + """Create a TempdirFactory and attach it to the config object. + + This is to comply with existing plugins which expect the handler to be + available at pytest_configure time, but ideally should be moved entirely + to the tmpdir_factory session fixture. + """ + mp = MonkeyPatch() + tmppath_handler = TempPathFactory.from_config(config, _ispytest=True) + t = TempdirFactory(tmppath_handler, _ispytest=True) + config._cleanup.append(mp.undo) + mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) + mp.setattr(config, "_tmpdirhandler", t, raising=False) + + +@fixture(scope="session") +def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: + """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmpdirhandler # type: ignore + + +@fixture(scope="session") +def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: + """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmp_path_factory # type: ignore + + +def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: + name = request.node.name + name = re.sub(r"[\W]", "_", name) + MAXVAL = 30 + name = name[:MAXVAL] + return factory.mktemp(name, numbered=True) + + +@fixture +def tmpdir(tmp_path: Path) -> py.path.local: + """Return a temporary directory path object which is unique to each test + function invocation, created as a sub directory of the base temporary + directory. + + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + + The returned object is a `py.path.local`_ path object. + + .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html + """ + return py.path.local(tmp_path) + + +@fixture +def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path: + """Return a temporary directory path object which is unique to each test + function invocation, created as a sub directory of the base temporary + directory. + + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + + The returned object is a :class:`pathlib.Path` object. + """ + + return _mk_tmp(request, tmp_path_factory) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unittest.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unittest.py new file mode 100644 index 0000000..55f15ef --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unittest.py @@ -0,0 +1,405 @@ +"""Discover and run std-library "unittest" style tests.""" +import sys +import traceback +import types +from typing import Any +from typing import Callable +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import _pytest._code +import pytest +from _pytest.compat import getimfunc +from _pytest.compat import is_async_function +from _pytest.config import hookimpl +from _pytest.fixtures import FixtureRequest +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import exit +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.python import Class +from _pytest.python import Function +from _pytest.python import PyCollector +from _pytest.runner import CallInfo +from _pytest.skipping import skipped_by_mark_key +from _pytest.skipping import unexpectedsuccess_key + +if TYPE_CHECKING: + import unittest + + from _pytest.fixtures import _Scope + + _SysExcInfoType = Union[ + Tuple[Type[BaseException], BaseException, types.TracebackType], + Tuple[None, None, None], + ] + + +def pytest_pycollect_makeitem( + collector: PyCollector, name: str, obj: object +) -> Optional["UnitTestCase"]: + # Has unittest been imported and is obj a subclass of its TestCase? + try: + ut = sys.modules["unittest"] + # Type ignored because `ut` is an opaque module. + if not issubclass(obj, ut.TestCase): # type: ignore + return None + except Exception: + return None + # Yes, so let's collect it. + item: UnitTestCase = UnitTestCase.from_parent(collector, name=name, obj=obj) + return item + + +class UnitTestCase(Class): + # Marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs. + nofuncargs = True + + def collect(self) -> Iterable[Union[Item, Collector]]: + from unittest import TestLoader + + cls = self.obj + if not getattr(cls, "__test__", True): + return + + skipped = _is_skipped(cls) + if not skipped: + self._inject_setup_teardown_fixtures(cls) + self._inject_setup_class_fixture() + + self.session._fixturemanager.parsefactories(self, unittest=True) + loader = TestLoader() + foundsomething = False + for name in loader.getTestCaseNames(self.obj): + x = getattr(self.obj, name) + if not getattr(x, "__test__", True): + continue + funcobj = getimfunc(x) + yield TestCaseFunction.from_parent(self, name=name, callobj=funcobj) + foundsomething = True + + if not foundsomething: + runtest = getattr(self.obj, "runTest", None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + # Type ignored because `ut` is an opaque module. + if ut is None or runtest != ut.TestCase.runTest: # type: ignore + yield TestCaseFunction.from_parent(self, name="runTest") + + def _inject_setup_teardown_fixtures(self, cls: type) -> None: + """Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding + teardown functions (#517).""" + class_fixture = _make_xunit_fixture( + cls, + "setUpClass", + "tearDownClass", + "doClassCleanups", + scope="class", + pass_self=False, + ) + if class_fixture: + cls.__pytest_class_setup = class_fixture # type: ignore[attr-defined] + + method_fixture = _make_xunit_fixture( + cls, + "setup_method", + "teardown_method", + None, + scope="function", + pass_self=True, + ) + if method_fixture: + cls.__pytest_method_setup = method_fixture # type: ignore[attr-defined] + + +def _make_xunit_fixture( + obj: type, + setup_name: str, + teardown_name: str, + cleanup_name: Optional[str], + scope: "_Scope", + pass_self: bool, +): + setup = getattr(obj, setup_name, None) + teardown = getattr(obj, teardown_name, None) + if setup is None and teardown is None: + return None + + if cleanup_name: + cleanup = getattr(obj, cleanup_name, lambda *args: None) + else: + + def cleanup(*args): + pass + + @pytest.fixture( + scope=scope, + autouse=True, + # Use a unique name to speed up lookup. + name=f"unittest_{setup_name}_fixture_{obj.__qualname__}", + ) + def fixture(self, request: FixtureRequest) -> Generator[None, None, None]: + if _is_skipped(self): + reason = self.__unittest_skip_why__ + pytest.skip(reason) + if setup is not None: + try: + if pass_self: + setup(self, request.function) + else: + setup() + # unittest does not call the cleanup function for every BaseException, so we + # follow this here. + except Exception: + if pass_self: + cleanup(self) + else: + cleanup() + + raise + yield + try: + if teardown is not None: + if pass_self: + teardown(self, request.function) + else: + teardown() + finally: + if pass_self: + cleanup(self) + else: + cleanup() + + return fixture + + +class TestCaseFunction(Function): + nofuncargs = True + _excinfo: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] = None + _testcase: Optional["unittest.TestCase"] = None + + def setup(self) -> None: + # A bound method to be called during teardown() if set (see 'runtest()'). + self._explicit_tearDown: Optional[Callable[[], None]] = None + assert self.parent is not None + self._testcase = self.parent.obj(self.name) # type: ignore[attr-defined] + self._obj = getattr(self._testcase, self.name) + if hasattr(self, "_request"): + self._request._fillfixtures() + + def teardown(self) -> None: + if self._explicit_tearDown is not None: + self._explicit_tearDown() + self._explicit_tearDown = None + self._testcase = None + self._obj = None + + def startTest(self, testcase: "unittest.TestCase") -> None: + pass + + def _addexcinfo(self, rawexcinfo: "_SysExcInfoType") -> None: + # Unwrap potential exception info (see twisted trial support below). + rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) + try: + excinfo = _pytest._code.ExceptionInfo(rawexcinfo) # type: ignore[arg-type] + # Invoke the attributes to trigger storing the traceback + # trial causes some issue there. + excinfo.value + excinfo.traceback + except TypeError: + try: + try: + values = traceback.format_exception(*rawexcinfo) + values.insert( + 0, + "NOTE: Incompatible Exception Representation, " + "displaying natively:\n\n", + ) + fail("".join(values), pytrace=False) + except (fail.Exception, KeyboardInterrupt): + raise + except BaseException: + fail( + "ERROR: Unknown Incompatible Exception " + "representation:\n%r" % (rawexcinfo,), + pytrace=False, + ) + except KeyboardInterrupt: + raise + except fail.Exception: + excinfo = _pytest._code.ExceptionInfo.from_current() + self.__dict__.setdefault("_excinfo", []).append(excinfo) + + def addError( + self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + ) -> None: + try: + if isinstance(rawexcinfo[1], exit.Exception): + exit(rawexcinfo[1].msg) + except TypeError: + pass + self._addexcinfo(rawexcinfo) + + def addFailure( + self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + ) -> None: + self._addexcinfo(rawexcinfo) + + def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None: + try: + skip(reason) + except skip.Exception: + self._store[skipped_by_mark_key] = True + self._addexcinfo(sys.exc_info()) + + def addExpectedFailure( + self, + testcase: "unittest.TestCase", + rawexcinfo: "_SysExcInfoType", + reason: str = "", + ) -> None: + try: + xfail(str(reason)) + except xfail.Exception: + self._addexcinfo(sys.exc_info()) + + def addUnexpectedSuccess( + self, testcase: "unittest.TestCase", reason: str = "" + ) -> None: + self._store[unexpectedsuccess_key] = reason + + def addSuccess(self, testcase: "unittest.TestCase") -> None: + pass + + def stopTest(self, testcase: "unittest.TestCase") -> None: + pass + + def _expecting_failure(self, test_method) -> bool: + """Return True if the given unittest method (or the entire class) is marked + with @expectedFailure.""" + expecting_failure_method = getattr( + test_method, "__unittest_expecting_failure__", False + ) + expecting_failure_class = getattr(self, "__unittest_expecting_failure__", False) + return bool(expecting_failure_class or expecting_failure_method) + + def runtest(self) -> None: + from _pytest.debugging import maybe_wrap_pytest_function_for_tracing + + assert self._testcase is not None + + maybe_wrap_pytest_function_for_tracing(self) + + # Let the unittest framework handle async functions. + if is_async_function(self.obj): + # Type ignored because self acts as the TestResult, but is not actually one. + self._testcase(result=self) # type: ignore[arg-type] + else: + # When --pdb is given, we want to postpone calling tearDown() otherwise + # when entering the pdb prompt, tearDown() would have probably cleaned up + # instance variables, which makes it difficult to debug. + # Arguably we could always postpone tearDown(), but this changes the moment where the + # TestCase instance interacts with the results object, so better to only do it + # when absolutely needed. + if self.config.getoption("usepdb") and not _is_skipped(self.obj): + self._explicit_tearDown = self._testcase.tearDown + setattr(self._testcase, "tearDown", lambda *args: None) + + # We need to update the actual bound method with self.obj, because + # wrap_pytest_function_for_tracing replaces self.obj by a wrapper. + setattr(self._testcase, self.name, self.obj) + try: + self._testcase(result=self) # type: ignore[arg-type] + finally: + delattr(self._testcase, self.name) + + def _prunetraceback( + self, excinfo: _pytest._code.ExceptionInfo[BaseException] + ) -> None: + Function._prunetraceback(self, excinfo) + traceback = excinfo.traceback.filter( + lambda x: not x.frame.f_globals.get("__unittest") + ) + if traceback: + excinfo.traceback = traceback + + +@hookimpl(tryfirst=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: + if isinstance(item, TestCaseFunction): + if item._excinfo: + call.excinfo = item._excinfo.pop(0) + try: + del call.result + except AttributeError: + pass + + unittest = sys.modules.get("unittest") + if ( + unittest + and call.excinfo + and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] + ): + excinfo = call.excinfo + # Let's substitute the excinfo with a pytest.skip one. + call2 = CallInfo[None].from_call( + lambda: pytest.skip(str(excinfo.value)), call.when + ) + call.excinfo = call2.excinfo + + +# Twisted trial support. + + +@hookimpl(hookwrapper=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules: + ut: Any = sys.modules["twisted.python.failure"] + Failure__init__ = ut.Failure.__init__ + check_testcase_implements_trial_reporter() + + def excstore( + self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None + ): + if exc_value is None: + self._rawexcinfo = sys.exc_info() + else: + if exc_type is None: + exc_type = type(exc_value) + self._rawexcinfo = (exc_type, exc_value, exc_tb) + try: + Failure__init__( + self, exc_value, exc_type, exc_tb, captureVars=captureVars + ) + except TypeError: + Failure__init__(self, exc_value, exc_type, exc_tb) + + ut.Failure.__init__ = excstore + yield + ut.Failure.__init__ = Failure__init__ + else: + yield + + +def check_testcase_implements_trial_reporter(done: List[int] = []) -> None: + if done: + return + from zope.interface import classImplements + from twisted.trial.itrial import IReporter + + classImplements(TestCaseFunction, IReporter) + done.append(1) + + +def _is_skipped(obj) -> bool: + """Return True if the given object has been marked with @unittest.skip.""" + return bool(getattr(obj, "__unittest_skip__", False)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unraisableexception.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unraisableexception.py new file mode 100644 index 0000000..fcb5d82 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/unraisableexception.py @@ -0,0 +1,93 @@ +import sys +import traceback +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Optional +from typing import Type + +import pytest + + +# Copied from cpython/Lib/test/support/__init__.py, with modifications. +class catch_unraisable_exception: + """Context manager catching unraisable exception using sys.unraisablehook. + + Storing the exception value (cm.unraisable.exc_value) creates a reference + cycle. The reference cycle is broken explicitly when the context manager + exits. + + Storing the object (cm.unraisable.object) can resurrect it if it is set to + an object which is being finalized. Exiting the context manager clears the + stored object. + + Usage: + with catch_unraisable_exception() as cm: + # code creating an "unraisable exception" + ... + # check the unraisable exception: use cm.unraisable + ... + # cm.unraisable attribute no longer exists at this point + # (to break a reference cycle) + """ + + def __init__(self) -> None: + self.unraisable: Optional["sys.UnraisableHookArgs"] = None + self._old_hook: Optional[Callable[["sys.UnraisableHookArgs"], Any]] = None + + def _hook(self, unraisable: "sys.UnraisableHookArgs") -> None: + # Storing unraisable.object can resurrect an object which is being + # finalized. Storing unraisable.exc_value creates a reference cycle. + self.unraisable = unraisable + + def __enter__(self) -> "catch_unraisable_exception": + self._old_hook = sys.unraisablehook + sys.unraisablehook = self._hook + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + assert self._old_hook is not None + sys.unraisablehook = self._old_hook + self._old_hook = None + del self.unraisable + + +def unraisable_exception_runtest_hook() -> Generator[None, None, None]: + with catch_unraisable_exception() as cm: + yield + if cm.unraisable: + if cm.unraisable.err_msg is not None: + err_msg = cm.unraisable.err_msg + else: + err_msg = "Exception ignored in" + msg = f"{err_msg}: {cm.unraisable.object!r}\n\n" + msg += "".join( + traceback.format_exception( + cm.unraisable.exc_type, + cm.unraisable.exc_value, + cm.unraisable.exc_traceback, + ) + ) + warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_setup() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_call() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_teardown() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warning_types.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warning_types.py new file mode 100644 index 0000000..2eadd9f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warning_types.py @@ -0,0 +1,132 @@ +from typing import Any +from typing import Generic +from typing import Type +from typing import TypeVar + +import attr + +from _pytest.compat import final + + +class PytestWarning(UserWarning): + """Base class for all warnings emitted by pytest.""" + + __module__ = "pytest" + + +@final +class PytestAssertRewriteWarning(PytestWarning): + """Warning emitted by the pytest assert rewrite module.""" + + __module__ = "pytest" + + +@final +class PytestCacheWarning(PytestWarning): + """Warning emitted by the cache plugin in various situations.""" + + __module__ = "pytest" + + +@final +class PytestConfigWarning(PytestWarning): + """Warning emitted for configuration issues.""" + + __module__ = "pytest" + + +@final +class PytestCollectionWarning(PytestWarning): + """Warning emitted when pytest is not able to collect a file or symbol in a module.""" + + __module__ = "pytest" + + +@final +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """Warning class for features that will be removed in a future version.""" + + __module__ = "pytest" + + +@final +class PytestExperimentalApiWarning(PytestWarning, FutureWarning): + """Warning category used to denote experiments in pytest. + + Use sparingly as the API might change or even be removed completely in a + future version. + """ + + __module__ = "pytest" + + @classmethod + def simple(cls, apiname: str) -> "PytestExperimentalApiWarning": + return cls( + "{apiname} is an experimental api that may change over time".format( + apiname=apiname + ) + ) + + +@final +class PytestUnhandledCoroutineWarning(PytestWarning): + """Warning emitted for an unhandled coroutine. + + A coroutine was encountered when collecting test functions, but was not + handled by any async-aware plugin. + Coroutine test functions are not natively supported. + """ + + __module__ = "pytest" + + +@final +class PytestUnknownMarkWarning(PytestWarning): + """Warning emitted on use of unknown markers. + + See :ref:`mark` for details. + """ + + __module__ = "pytest" + + +@final +class PytestUnraisableExceptionWarning(PytestWarning): + """An unraisable exception was reported. + + Unraisable exceptions are exceptions raised in :meth:`__del__ ` + implementations and similar situations when the exception cannot be raised + as normal. + """ + + __module__ = "pytest" + + +@final +class PytestUnhandledThreadExceptionWarning(PytestWarning): + """An unhandled exception occurred in a :class:`~threading.Thread`. + + Such exceptions don't propagate normally. + """ + + __module__ = "pytest" + + +_W = TypeVar("_W", bound=PytestWarning) + + +@final +@attr.s +class UnformattedWarning(Generic[_W]): + """A warning meant to be formatted during runtime. + + This is used to hold warnings that need to format their message at runtime, + as opposed to a direct message. + """ + + category = attr.ib(type=Type["_W"]) + template = attr.ib(type=str) + + def format(self, **kwargs: Any) -> _W: + """Return an instance of the warning category, formatted with given kwargs.""" + return self.category(self.template.format(**kwargs)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warnings.py b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warnings.py new file mode 100644 index 0000000..35eed96 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/_pytest/warnings.py @@ -0,0 +1,139 @@ +import sys +import warnings +from contextlib import contextmanager +from typing import Generator +from typing import Optional +from typing import TYPE_CHECKING + +import pytest +from _pytest.config import apply_warning_filters +from _pytest.config import Config +from _pytest.config import parse_warning_filter +from _pytest.main import Session +from _pytest.nodes import Item +from _pytest.terminal import TerminalReporter + +if TYPE_CHECKING: + from typing_extensions import Literal + + +def pytest_configure(config: Config) -> None: + config.addinivalue_line( + "markers", + "filterwarnings(warning): add a warning filter to the given test. " + "see https://docs.pytest.org/en/stable/warnings.html#pytest-mark-filterwarnings ", + ) + + +@contextmanager +def catch_warnings_for_item( + config: Config, + ihook, + when: "Literal['config', 'collect', 'runtest']", + item: Optional[Item], +) -> Generator[None, None, None]: + """Context manager that catches warnings generated in the contained execution block. + + ``item`` can be None if we are not in the context of an item execution. + + Each warning captured triggers the ``pytest_warning_recorded`` hook. + """ + config_filters = config.getini("filterwarnings") + cmdline_filters = config.known_args_namespace.pythonwarnings or [] + with warnings.catch_warnings(record=True) as log: + # mypy can't infer that record=True means log is not None; help it. + assert log is not None + + if not sys.warnoptions: + # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). + warnings.filterwarnings("always", category=DeprecationWarning) + warnings.filterwarnings("always", category=PendingDeprecationWarning) + + apply_warning_filters(config_filters, cmdline_filters) + + # apply filters from "filterwarnings" marks + nodeid = "" if item is None else item.nodeid + if item is not None: + for mark in item.iter_markers(name="filterwarnings"): + for arg in mark.args: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + yield + + for warning_message in log: + ihook.pytest_warning_captured.call_historic( + kwargs=dict( + warning_message=warning_message, + when=when, + item=item, + location=None, + ) + ) + ihook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=warning_message, + nodeid=nodeid, + when=when, + location=None, + ) + ) + + +def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: + """Convert a warnings.WarningMessage to a string.""" + warn_msg = warning_message.message + msg = warnings.formatwarning( + str(warn_msg), + warning_message.category, + warning_message.filename, + warning_message.lineno, + warning_message.line, + ) + return msg + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + with catch_warnings_for_item( + config=item.config, ihook=item.ihook, when="runtest", item=item + ): + yield + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_collection(session: Session) -> Generator[None, None, None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="collect", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_terminal_summary( + terminalreporter: TerminalReporter, +) -> Generator[None, None, None]: + config = terminalreporter.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_load_initial_conftests( + early_config: "Config", +) -> Generator[None, None, None]: + with catch_warnings_for_item( + config=early_config, ihook=early_config.hook, when="config", item=None + ): + yield diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.py new file mode 100644 index 0000000..f95c96d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +import sys + +from functools import partial + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Factory, + attrib, + attrs, + fields, + fields_dict, + make_class, + validate, +) +from ._version_info import VersionInfo + + +__version__ = "21.4.0" +__version_info__ = VersionInfo._from_version_string(__version__) + +__title__ = "attrs" +__description__ = "Classes Without Boilerplate" +__url__ = "https://www.attrs.org/" +__uri__ = __url__ +__doc__ = __description__ + " <" + __uri__ + ">" + +__author__ = "Hynek Schlawack" +__email__ = "hs@ox.cx" + +__license__ = "MIT" +__copyright__ = "Copyright (c) 2015 Hynek Schlawack" + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + +__all__ = [ + "Attribute", + "Factory", + "NOTHING", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "evolve", + "exceptions", + "fields", + "fields_dict", + "filters", + "get_run_validators", + "has", + "ib", + "make_class", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + +if sys.version_info[:2] >= (3, 6): + from ._next_gen import define, field, frozen, mutable # noqa: F401 + + __all__.extend(("define", "field", "frozen", "mutable")) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.pyi new file mode 100644 index 0000000..c0a2126 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__init__.pyi @@ -0,0 +1,484 @@ +import sys + +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._version_info import VersionInfo + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = Union[bool, Callable[[Any], Any]] +_ValidatorType = Callable[[Any, Attribute[_T], _T], Any] +_ConverterType = Callable[[Any], Any] +_FilterType = Callable[[Attribute[_T], _T], bool] +_ReprType = Callable[[Any], str] +_ReprArgType = Union[bool, _ReprType] +_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any] +_OnSetAttrArgType = Union[ + _OnSetAttrType, List[_OnSetAttrType], setters._NoOpType +] +_FieldTransformer = Callable[ + [type, List[Attribute[Any]]], List[Attribute[Any]] +] +_CompareWithType = Callable[[Any, Any], bool] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]] + +# _make -- + +NOTHING: object + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. +if sys.version_info >= (3, 8): + from typing import Literal + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], + ) -> _T: ... + @overload + def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], + ) -> _T: ... + +else: + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Union[Callable[[Any], _T], Callable[[], _T]], + takes_self: bool = ..., + ) -> _T: ... + +# Static type inference support via __dataclass_transform__ implemented as per: +# https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md +# This annotation must be applied to all overloads of "define" and "attrs" +# +# NOTE: This is a typing construct and does not exist at runtime. Extensions +# wrapping attrs decorators should declare a separate __dataclass_transform__ +# signature in the extension module using the specification linked above to +# provide pyright support. +def __dataclass_transform__( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()), +) -> Callable[[_T], _T]: ... + +class Attribute(Generic[_T]): + name: str + default: Optional[_T] + validator: Optional[_ValidatorType[_T]] + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: Optional[bool] + init: bool + converter: Optional[_ConverterType] + metadata: Dict[Any, Any] + type: Optional[Type[_T]] + kw_only: bool + on_setattr: _OnSetAttrType + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: object = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: _C, + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define +frozen = define # they differ only in their defaults + +# TODO: add support for returning NamedTuple from the mypy plugin +class _Fields(Tuple[Attribute[Any], ...]): + def __getattr__(self, name: str) -> Attribute[Any]: ... + +def fields(cls: type) -> _Fields: ... +def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ... +def validate(inst: Any) -> None: ... +def resolve_types( + cls: _C, + globalns: Optional[Dict[str, Any]] = ..., + localns: Optional[Dict[str, Any]] = ..., + attribs: Optional[List[Attribute[Any]]] = ..., +) -> _C: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]], + bases: Tuple[type, ...] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + collect_by_mro: bool = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +# XXX: remember to fix attrs.asdict/astuple too! +def asdict( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[ + Callable[[type, Attribute[Any], Any], Any] + ] = ..., + tuple_keys: Optional[bool] = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... +def has(cls: type) -> bool: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b32ed6b834896cb27c97cb926255173100b729dc GIT binary patch literal 1750 zcmZ8hOLN;c5GJYj%a(1~@;kEQv|$oUdACX2Owx4P zmY9RN%&zcx(T28IfQ8Jj@r7iI399(19;5zf^ zeAi*uyAEx49k@XkV4p5RpDw|GcHk!4E;?`vZnG_1@4#JFX4`a`uKeM_y`e+9f4Oj< z6<>crJ-Yh4x8~5blhU8A{1H`H=U<8k@IX9-hvE@D5(jYLI_&V3vCMv2&QNg<*GI7`5shBWn^U2W>qXK zTBurBx3FQMW}#(a3Bh>Lk=(Ri$9k@kbJnrrF1IW+5Y9aW-#af@8|UBgi>6H3aTGIC z%7n-FAd?x3O+}1#aKxGa*whKOq8>{|h_nYtyItD93R6&1WolWYCJ9%vWP|T(&dSY- z%!;u0n_fMdzSDm& zP?2W)Db7+bWNKjhdqD6NM4FI(IyKb;kNUMhx9PUso_nLwEEL_k XS9J4l+gkQlsJd0R=~cHo?yC16nYQy! literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_cmp.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_cmp.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06d42aa2d854893f5aafc658edede1a81b608645 GIT binary patch literal 3906 zcmb_f%WoUU8Q)nh$t5jOvaQ%oQv{O&jw@Ft|r7o3$ra%ul6h#j?*hl{Z{bTmfOHMwwm!<{M-#1I@k<=;LbcuO>^Y|XW@B4<~ z@^aI`_b~oP`0lFXd`g3}Umb%VqiBUnID*Aa!n!PRyKYkHRv68>vDfuPMR;*Fsda0Z ztB7h`PyDWreofScFB+mLE__n$HpDg25-U$ww<%V|#V1bpg196uqg@hf;uW;Z;)=M6 zwk6z$&ibn_aIdpoDOSS1&f?t2AW9~gGKDXq{YXbyTC^rAN=-1#(}BTw`vs#dtZ1b2 zAQ;4<))@Ff55vFXg1UGvLXT{M5_9M!Sz!i?ELqaC&1$APWRde*XUN25vG&A2_Kq5}s`|^Uv0HmuH~uq5)|}Nn z2>aMke-p1fc0OimHT9U2UYq$JGhH(capjo(hMDHm3$w;Eh7%gF+|}?8p5rt*zI#fB zM)HtbV{kJL4IhLl?@K;`b)?|1U&ec(F{&N)dp9)i_2gc!w}CcOLMrNpsj#Q@dV^%r z>+w*H@0U8J+L-L$)Q#*!KCH$uof@&E5Vi3kLA?y*Cdv|zmG3@c2^a9D`ei-MZKDn2NabylHE2LI% zU=A{#^>^jK=(e4I`SG#&>f;Z_5LT+w(@92qG>c9&$1XO-^(K<105zZFozSc@w2f^d2dJM z5`MJ6kwxkkp?+e1>l;d%Tw&*SX5LFCu_Sksq6dFipw2=Zh_GOUi_xW=E`15ywNZ~s z?+a2ay~DR@1CVS(Lz~~1!w^1defq{}{Jk+h?xB?MkrUtU@gY=`WN<|k0-km%A?c43!?YAtksFF*`b+2dyfsQkyg8=yB6ox1p_ zPwC|{il%F3&Qqj|XY8T#oNXhW)$)lzP(aU3^>bEu{Y)G_cRtHIA7}{P8NgM;QI?9Z zb3YrDak4YWWM?nDh31z0)kLbuO4ykk8mUcyKt+LMKD;H<{Z1V9X*kZ(@3w!`(UFn2 zCgI?5IFh(AO*Qb7;SIH8RDAWLZ@8y0-3>? z-j{nkLENT1AmlJYz(J(3h_oRcW{xES-w8&CDognJh}&LYDw<_#C-bQ_99*=@i3}o! zFqfiat!hN6l@U#mUW-~dnaETWtHEF_2aiu3rf7ah0C`ubOcf3Lu7G3dqGd7@#=(N0 z^`=FDHMTXfHLf*k_u`kluxMI{TiACmr+HGIbMkD_-IZ~u2}_HIOi?9A3KS>yU#IE~ zV!LsoSb~rK2`cAJk9o{x?mufjdajRAi#5^uto3i|YZZ?zRldP$73}%k^O~;e;FJL! zwfG2vF3LR=O%N7%+dOfO07Yc?%JdEEOY_veaGrU_EVIIZwl6;FQz3wqh-Nb_99!$v(PN48$#pc4(G9KDNsVa_Xt15K~y%+* z;57%4Jp<8SU5wosy>qMQ^|^rV%k{aq>N0M!*HMI+v1d%T40QSt%(Uyy*e;%mN4UbU z+)>{q3SFmO!>;wp+*{5^_fo4B^%j;CRh#RJTKRIKI78qpwPV3pE1yVH;(|cpuqN== z0`5F_`ucf4JXQI86GG;7#NO$*-Y@e^$ggYOZ{xuT*T+*|@ctpEIHTM62Y?7oDFdN) zH7NCi@NTDcQ4a!<4T1p6JX)l_4_4FmKHJ9Kwwazl0x&qc zKpxdO`7oQlZ@HaopPmPl*>4h8H|^`-rsW)cBv^WFo=xx@`~hM*=IJ$f5NTw5^2>GC z0*F#;8SetjoC?Ij$QfDIG5jOS+(8wM*X!H#?Wh$RtRTE2BH|rF*^`7)1bsz@QQtuY z+uQ7H$_QVzm@7m6JG6!ZoAp*}V5=v4P00l(s;w-X{mmp3c`Uc+4o&lGuJ0}(Sgv{w QJM;T{^XGoczv?&t1&Hbw@Bjb+ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_compat.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e6d6e6faf80b4e96d497602a625f0ceb26766c3 GIT binary patch literal 6249 zcmbVQ&2!tv6~_V~2~s2_>%+1W+d)1WnbSy0tR|gi($tBar0Fz@VmVEXX$yncr9=?~ zm<6b>p)Qfz#xr#%J@#guTKz9Q_kdfcJ=E8pI?loUy#+~0l-kCjw2S?C`}XbI_ul?? zy-KB|;kO!m?fu2pw7*m3_|HV;I#RsMG>vPV1zMXqtgSnG+i(mu9G|?W@kueeZ#b9u6rV=>Wj@2t<9j81Q*nOo80XonhhOM( zy$Ej4WZXny<0AM!+vjnKUj`3b*!!CEyvRR%;Xrd<syYh%MT{F^guO3H1HxX{L z-HBw9TD-a0jGIxIRy(p8CT^n}`U&dGk8})4&6S;%otpm0K;==MnCP-VW!lUdOUFKz zTu8xhHvOcgr-hHaPNx~JrPiwWyemRqfM(*uL`bh5h)-i7iO|Om4}Vmqy}yAJPasRQ zo|dpg-!K~bKBPZn%QZ7CLCP>rJg`j7WTzw29Obc7yb*YDd>iFMmgZViIn?Ad7(L!z zq#5Hyf87hcwJ7A?;?2nKwndnrF%pZPd-M3ri|vk(&8)ezlZZHRJ2Ki3ezKTV=S8@= z7&PltU5~=0g&!=&%|y(1Jiq0wiFnaV61nL5QM==`|CpE0F{9z)r{{;}PalHLEkW4D`Ov+V_ToOJ7U)UcHv7DBP* zsyXds-An8(VTVF+yAesd?X^VKw=iT=W0I6z%cW-MwV}@gf1mg zJH{3=J~E%Y464_V28yJ*k96Wk26`PSzKu+14&$1mbLJQv4iC35xyf_*=6IeL@Xd3J z7x68?1;=>#9&;>S;Z?L0`LuFZ=9DsLb;fuJn=q4>ZwZfo90ogN#JjH#b@?tVW?nR) z>co~HnU6qYa}e2E5)L8ck=}h0bnv6{P%~vz62!fI_63B;g!=OC8*}Zb z!wRbJ3|iGP4t4n=W=WYlDtQ^JO&YqCqZ@mA4C-<>%j z)PSM}$bkQHiZOJ$xo%o=-FC#g0hP4z8GPk;DSL&o1I+3QGZ%@#9FE1&(9@Ga$4{|QBr{6j?%G)T04ZIAWX279RYv;#nZ z9vd8yh8i#50GSdHAnZAmQ$vbVr*pL}5|5Nf7-oBC9dub#WO-I*@`tGVNW&iqLKy60 zq?q6c$Fw#5@q^*<_L;ENfwtwz5G8v>fgpRyt_$Dm#)7bhCxJI`AbB7HdBgU?9ovsW zUy4KwIiVr$RyPbuI3QyoJG2(5 z4&QK&&fZ3&={IrQ;;HK7#J)K(;0s3q%m97IV1aHsH|h!-TS2tcX?fb6#d zu_=OO9Qzh&1Iz&HsFYT*oCG-cv#eSE2%|>Ss1&w|_E_E4awgLa;DE1B*e;pN&~}L~ zgX{#>Gf1b)abisXQ&JEZorLUtAF01fDb5;zms4}l9^N3bvp{2xLS-zkpnsG-0mLt9 zTxJBP_zpog;m<^Sh~4jL`|RMDznw!*I?=z)tdJuSo80|S> z^2CE&c8VBgu5@3CiIdnz+lk+hkeuYp!CH=lr5oCIR#jnl8n+%_xZ7?Lxpm=sO7uTt z>_GHSzJchCrbO(fMQ?f%oQ@WD7I!r}j=+yFi~NK#bq-TRmzhkYj!~WmB$K+UGLxCP z-KB3KmsE4mY=dVIQYSWc%#Q}*$EwnhLSQZN3A-Vip~n-o5jF}Gd%ncApJG7|9(9l- zfA0Y-Wp;!OO7mC@j-A1xRbVQ=-+!f{pj0y?q4Z20KSSw|Ip>cw(3b>{6L_dO0xZiQ z{40_&2)e$PpP1$eYamM?iIuc8kPrNxA{YFgBGUzYHE!S*3o84KtlNu{` zuBAmKC~2S2D18AnZp|Q0^0PUztY$w!JLRXlDh_sBdLI-hk7m}dX2s|Kz0!m7_qAK8t zEmpk*jx$q+86Q;|%|K91jZ=jxu_FHsnxP1zl=uXID_&hHTeWUr$z-QaInzWI;^@&~ z5bfNwLcR|<0JHEmmlogK_C-hCrr(8V71zQ_Z1FS-!Grx>{xSG;`+e z{Q>MY{8^qvAkZ^ jO>EYLUN$YmDq#c7i6tvnDop5B1)GTt&7;j~1jS5ccj~lI0+Y6lkDw7f#CU0-+&72=PEL>xXxzI`qw9dC%(?77oG78+kB&_KR>^Tj8hZM3i zhS7QKw=kB`dGsMVVvNI=Kz=*}oPp!kabuBRJ;{XwPF`f3KU;7KN21ZV z!|Cwia;x9bWEpMO4oZO__xAXM``K=GkG~>vrFBO;Au6cs9OMq4G{_Zlp}k!`&`|}i zx?>zIH*n0yV}a=Wc+8Ecz}KRLjKA@~FYxTj-z-E;DlaA)l+gA9+Fm)OrV(lLnIglIl7cY`AR{Uj%k zoX^XdFk)(r6!{BVHWe5o&_e!R3~3s|Nexb2!1V&bV^KT%7D~)7%psV0uGJ)%Gi!FU zN4ZxBL;ChRF$JH8VB|$<&5@dB^}?;t^VdVhS4pw7AEj{_R?8A9qpOM@Es#pmrQD@o&Cl5YG2qozBy literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_funcs.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_funcs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca3a02520ac339fd29ae1f750e0b44756ac09081 GIT binary patch literal 10451 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 V@vy$*yj|QqstaevIq!V&KLCj|UwZ%m literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_make.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_make.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..835a24411d5860b4e423147a38574603fdd3d225 GIT binary patch literal 75845 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

    W$=eF~P`|KMOW(e)Vjj7>C39uvdz^mOU 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 PZd0c8%0haym|yq5Lo9aA literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_next_gen.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/_next_gen.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6a4f453852db34997b779e15e76ffc5d835c791 GIT binary patch literal 4866 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/converters.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/converters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef893b23952a13d4b0e2143f7d5be2aee9414989 GIT binary patch literal 3840 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 UylD*tcUyB?%dJ}RtGD3yH*HoKi2wiq literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/filters.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/__pycache__/filters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26dee21501c0f3efe5f21fad4cfac9bc9bef7073 GIT binary patch literal 1779 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.py new file mode 100644 index 0000000..6cffa4d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.py @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +import functools + +from ._compat import new_class +from ._make import _make_ne + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and + ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if + at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + :param Optional[callable] eq: `callable` used to evaluate equality + of two objects. + :param Optional[callable] lt: `callable` used to evaluate whether + one object is less than another object. + :param Optional[callable] le: `callable` used to evaluate whether + one object is less than or equal to another object. + :param Optional[callable] gt: `callable` used to evaluate whether + one object is greater than another object. + :param Optional[callable] ge: `callable` used to evaluate whether + one object is greater than or equal to another object. + + :param bool require_same_type: When `True`, equality and ordering methods + will return `NotImplemented` if objects are not of the same type. + + :param Optional[str] class_name: Name of class. Defaults to 'Comparable'. + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = _make_ne() + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = new_class(class_name, (object,), {}, lambda ns: ns.update(body)) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + raise ValueError( + "eq must be define is order to complete ordering from " + "lt, le, gt, ge." + ) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = "__%s__" % (name,) + method.__doc__ = "Return a %s b. Computed by attrs." % ( + _operation_names[name], + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + for func in self._requirements: + if not func(self, other): + return False + return True + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.pyi new file mode 100644 index 0000000..e71aaff --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_cmp.pyi @@ -0,0 +1,13 @@ +from typing import Type + +from . import _CompareWithType + +def cmp_using( + eq: Optional[_CompareWithType], + lt: Optional[_CompareWithType], + le: Optional[_CompareWithType], + gt: Optional[_CompareWithType], + ge: Optional[_CompareWithType], + require_same_type: bool, + class_name: str, +) -> Type: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_compat.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_compat.py new file mode 100644 index 0000000..dc0cb02 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_compat.py @@ -0,0 +1,261 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +import platform +import sys +import threading +import types +import warnings + + +PY2 = sys.version_info[0] == 2 +PYPY = platform.python_implementation() == "PyPy" +PY36 = sys.version_info[:2] >= (3, 6) +HAS_F_STRINGS = PY36 +PY310 = sys.version_info[:2] >= (3, 10) + + +if PYPY or PY36: + ordered_dict = dict +else: + from collections import OrderedDict + + ordered_dict = OrderedDict + + +if PY2: + from collections import Mapping, Sequence + + from UserDict import IterableUserDict + + # We 'bundle' isclass instead of using inspect as importing inspect is + # fairly expensive (order of 10-15 ms for a modern machine in 2016) + def isclass(klass): + return isinstance(klass, (type, types.ClassType)) + + def new_class(name, bases, kwds, exec_body): + """ + A minimal stub of types.new_class that we need for make_class. + """ + ns = {} + exec_body(ns) + + return type(name, bases, ns) + + # TYPE is used in exceptions, repr(int) is different on Python 2 and 3. + TYPE = "type" + + def iteritems(d): + return d.iteritems() + + # Python 2 is bereft of a read-only dict proxy, so we make one! + class ReadOnlyDict(IterableUserDict): + """ + Best-effort read-only dict wrapper. + """ + + def __setitem__(self, key, val): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item assignment" + ) + + def update(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'update'" + ) + + def __delitem__(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item deletion" + ) + + def clear(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'clear'" + ) + + def pop(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'pop'" + ) + + def popitem(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'popitem'" + ) + + def setdefault(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'setdefault'" + ) + + def __repr__(self): + # Override to be identical to the Python 3 version. + return "mappingproxy(" + repr(self.data) + ")" + + def metadata_proxy(d): + res = ReadOnlyDict() + res.data.update(d) # We blocked update, so we have to do it like this. + return res + + def just_warn(*args, **kw): # pragma: no cover + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + +else: # Python 3 and later. + from collections.abc import Mapping, Sequence # noqa + + def just_warn(*args, **kw): + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + warnings.warn( + "Running interpreter doesn't sufficiently support code object " + "introspection. Some features like bare super() or accessing " + "__class__ will not work with slotted classes.", + RuntimeWarning, + stacklevel=2, + ) + + def isclass(klass): + return isinstance(klass, type) + + TYPE = "class" + + def iteritems(d): + return d.items() + + new_class = types.new_class + + def metadata_proxy(d): + return types.MappingProxyType(dict(d)) + + +def make_set_closure_cell(): + """Return a function of two arguments (cell, value) which sets + the value stored in the closure cell `cell` to `value`. + """ + # pypy makes this easy. (It also supports the logic below, but + # why not do the easy/fast thing?) + if PYPY: + + def set_closure_cell(cell, value): + cell.__setstate__((value,)) + + return set_closure_cell + + # Otherwise gotta do it the hard way. + + # Create a function that will set its first cellvar to `value`. + def set_first_cellvar_to(value): + x = value + return + + # This function will be eliminated as dead code, but + # not before its reference to `x` forces `x` to be + # represented as a closure cell rather than a local. + def force_x_to_be_a_cell(): # pragma: no cover + return x + + try: + # Extract the code object and make sure our assumptions about + # the closure behavior are correct. + if PY2: + co = set_first_cellvar_to.func_code + else: + co = set_first_cellvar_to.__code__ + if co.co_cellvars != ("x",) or co.co_freevars != (): + raise AssertionError # pragma: no cover + + # Convert this code object to a code object that sets the + # function's first _freevar_ (not cellvar) to the argument. + if sys.version_info >= (3, 8): + # CPython 3.8+ has an incompatible CodeType signature + # (added a posonlyargcount argument) but also added + # CodeType.replace() to do this without counting parameters. + set_first_freevar_code = co.replace( + co_cellvars=co.co_freevars, co_freevars=co.co_cellvars + ) + else: + args = [co.co_argcount] + if not PY2: + args.append(co.co_kwonlyargcount) + args.extend( + [ + co.co_nlocals, + co.co_stacksize, + co.co_flags, + co.co_code, + co.co_consts, + co.co_names, + co.co_varnames, + co.co_filename, + co.co_name, + co.co_firstlineno, + co.co_lnotab, + # These two arguments are reversed: + co.co_cellvars, + co.co_freevars, + ] + ) + set_first_freevar_code = types.CodeType(*args) + + def set_closure_cell(cell, value): + # Create a function using the set_first_freevar_code, + # whose first closure cell is `cell`. Calling it will + # change the value of that cell. + setter = types.FunctionType( + set_first_freevar_code, {}, "setter", (), (cell,) + ) + # And call it to set the cell. + setter(value) + + # Make sure it works on this interpreter: + def make_func_with_cell(): + x = None + + def func(): + return x # pragma: no cover + + return func + + if PY2: + cell = make_func_with_cell().func_closure[0] + else: + cell = make_func_with_cell().__closure__[0] + set_closure_cell(cell, 100) + if cell.cell_contents != 100: + raise AssertionError # pragma: no cover + + except Exception: + return just_warn + else: + return set_closure_cell + + +set_closure_cell = make_set_closure_cell() + +# Thread-local global to track attrs instances which are already being repr'd. +# This is needed because there is no other (thread-safe) way to pass info +# about the instances that are already being repr'd through the call stack +# in order to ensure we don't perform infinite recursion. +# +# For instance, if an instance contains a dict which contains that instance, +# we need to know that we're already repr'ing the outside instance from within +# the dict's repr() call. +# +# This lives here rather than in _make.py so that the functions in _make.py +# don't have a direct reference to the thread-local in their globals dict. +# If they have such a reference, it breaks cloudpickle. +repr_context = threading.local() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_config.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_config.py new file mode 100644 index 0000000..fc9be29 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_config.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + + +__all__ = ["set_run_validators", "get_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` + instead. + """ + if not isinstance(run, bool): + raise TypeError("'run' must be bool.") + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` + instead. + """ + return _run_validators diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_funcs.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_funcs.py new file mode 100644 index 0000000..4c90085 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_funcs.py @@ -0,0 +1,422 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +import copy + +from ._compat import iteritems +from ._make import NOTHING, _obj_setattr, fields +from .exceptions import AttrsAttributeNotFoundError + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the ``attrs`` attribute values of *inst* as a dict. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attrs.Attribute` as the first argument and the + value as the second argument. + :param callable dict_factory: A callable to produce dictionaries from. For + example, to produce ordered dictionaries instead of normal Python + dictionaries, pass in ``collections.OrderedDict``. + :param bool retain_collection_types: Do not convert to ``list`` when + encountering an attribute whose type is ``tuple`` or ``set``. Only + meaningful if ``recurse`` is ``True``. + :param Optional[callable] value_serializer: A hook that is called for every + attribute or dict key/value. It receives the current instance, field + and value and must return the (updated) value. The hook is run *after* + the optional *filter* has been applied. + + :rtype: return type of *dict_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + .. versionadded:: 21.3.0 If a dict has a collection for a key, it is + serialized as a tuple. + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + if has(v.__class__): + rv[a.name] = asdict( + v, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain_collection_types is True else list + rv[a.name] = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in v + ] + ) + elif isinstance(v, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in iteritems(v) + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + is_key, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + if getattr(val.__class__, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif isinstance(val, (tuple, list, set, frozenset)): + if retain_collection_types is True: + cf = val.__class__ + elif is_key: + cf = tuple + else: + cf = list + + rv = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in val + ] + ) + elif isinstance(val, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in iteritems(val) + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the ``attrs`` attribute values of *inst* as a tuple. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attrs.Attribute` as the first argument and the + value as the second argument. + :param callable tuple_factory: A callable to produce tuples from. For + example, to produce lists instead of tuples. + :param bool retain_collection_types: Do not convert to ``list`` + or ``dict`` when encountering an attribute which type is + ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is + ``True``. + + :rtype: return type of *tuple_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + if recurse is True: + if has(v.__class__): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + rv.append( + cf( + [ + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + for j in v + ] + ) + ) + elif isinstance(v, dict): + df = v.__class__ if retain is True else dict + rv.append( + df( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk, + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv, + ) + for kk, vv in iteritems(v) + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with ``attrs`` attributes. + + :param type cls: Class to introspect. + :raise TypeError: If *cls* is not a class. + + :rtype: bool + """ + return getattr(cls, "__attrs_attrs__", None) is not None + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't + be found on *cls*. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. deprecated:: 17.1.0 + Use `attrs.evolve` instead if you can. + This function will not be removed du to the slightly different approach + compared to `attrs.evolve`. + """ + import warnings + + warnings.warn( + "assoc is deprecated and will be removed after 2018/01.", + DeprecationWarning, + stacklevel=2, + ) + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in iteritems(changes): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + raise AttrsAttributeNotFoundError( + "{k} is not an attrs attribute on {cl}.".format( + k=k, cl=new.__class__ + ) + ) + _obj_setattr(new, k, v) + return new + + +def evolve(inst, **changes): + """ + Create a new instance, based on *inst* with *changes* applied. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise TypeError: If *attr_name* couldn't be found in the class + ``__init__``. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 17.1.0 + """ + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = attr_name if attr_name[0] != "_" else attr_name[1:] + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +def resolve_types(cls, globalns=None, localns=None, attribs=None): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in `Attribute`'s *type* + field. In other words, you don't need to resolve your types if you only + use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, e.g. if the name only exists + inside a method, you may pass *globalns* or *localns* to specify other + dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + :param type cls: Class to resolve. + :param Optional[dict] globalns: Dictionary containing global variables. + :param Optional[dict] localns: Dictionary containing local variables. + :param Optional[list] attribs: List of attribs for the given class. + This is necessary when calling from inside a ``field_transformer`` + since *cls* is not an ``attrs`` class yet. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class and you didn't pass any attribs. + :raise NameError: If types cannot be resolved because of missing variables. + + :returns: *cls* so you can use this function also as a class decorator. + Please note that you have to apply it **after** `attrs.define`. That + means the decorator has to come in the line **before** `attrs.define`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + + """ + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + if getattr(cls, "__attrs_types_resolved__", None) != cls: + import typing + + hints = typing.get_type_hints(cls, globalns=globalns, localns=localns) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _obj_setattr(field, "type", hints[field.name]) + # We store the class we resolved so that subclasses know they haven't + # been resolved. + cls.__attrs_types_resolved__ = cls + + # Return the class so you can use it as a decorator too. + return cls diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_make.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_make.py new file mode 100644 index 0000000..d46f8a3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_make.py @@ -0,0 +1,3173 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +import copy +import inspect +import linecache +import sys +import warnings + +from operator import itemgetter + +# We need to import _compat itself in addition to the _compat members to avoid +# having the thread-local in the globals here. +from . import _compat, _config, setters +from ._compat import ( + HAS_F_STRINGS, + PY2, + PY310, + PYPY, + isclass, + iteritems, + metadata_proxy, + new_class, + ordered_dict, + set_closure_cell, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + PythonTooOldError, + UnannotatedAttributeError, +) + + +if not PY2: + import typing + + +# This is used at least twice, so cache it here. +_obj_setattr = object.__setattr__ +_init_converter_pat = "__attr_converter_%s" +_init_factory_pat = "__attr_factory_{}" +_tuple_property_pat = ( + " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" +) +_classvar_prefixes = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_hash_cache_field = "_attrs_cached_hash" + +_empty_metadata_singleton = metadata_proxy({}) + +# Unique object for unequivocal getattr() defaults. +_sentinel = object() + +_ng_default_on_setattr = setters.pipe(setters.convert, setters.validate) + + +class _Nothing(object): + """ + Sentinel class to indicate the lack of a value when ``None`` is ambiguous. + + ``_Nothing`` is a singleton. There is only ever one of it. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + """ + + _singleton = None + + def __new__(cls): + if _Nothing._singleton is None: + _Nothing._singleton = super(_Nothing, cls).__new__(cls) + return _Nothing._singleton + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + def __len__(self): + return 0 # __bool__ for Python 2 + + +NOTHING = _Nothing() +""" +Sentinel to indicate the lack of a value when ``None`` is ambiguous. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since ``None`` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + if PY2: + # For some reason `type(None)` isn't callable in Python 2, but we don't + # actually need a constructor for None objects, we just need any + # available function that returns None. + def __reduce__(self, _none_constructor=getattr, _args=(0, "", None)): + return _none_constructor, _args + + else: + + def __reduce__(self, _none_constructor=type(None), _args=()): + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Create a new attribute on a class. + + .. warning:: + + Does *not* do anything unless the class is also decorated with + `attr.s`! + + :param default: A value that is used if an ``attrs``-generated ``__init__`` + is used and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `attrs.Factory`, its callable will be + used to construct a new value (useful for mutable data types like lists + or dicts). + + If a default is not set (or set manually to `attrs.NOTHING`), a value + *must* be supplied when instantiating; otherwise a `TypeError` + will be raised. + + The default can also be set using decorator notation as shown below. + + :type default: Any value + + :param callable factory: Syntactic sugar for + ``default=attr.Factory(factory)``. + + :param validator: `callable` that is called by ``attrs``-generated + ``__init__`` methods after the instance has been initialized. They + receive the initialized instance, the :func:`~attrs.Attribute`, and the + passed value. + + The return value is *not* inspected so the validator has to throw an + exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `get_run_validators`. + + The validator can also be set using decorator notation as shown below. + + :type validator: `callable` or a `list` of `callable`\\ s. + + :param repr: Include this attribute in the generated ``__repr__`` + method. If ``True``, include the attribute; if ``False``, omit it. By + default, the built-in ``repr()`` function is used. To override how the + attribute value is formatted, pass a ``callable`` that takes a single + value and returns a string. Note that the resulting string is used + as-is, i.e. it will be used directly *instead* of calling ``repr()`` + (the default). + :type repr: a `bool` or a `callable` to use a custom function. + + :param eq: If ``True`` (default), include this attribute in the + generated ``__eq__`` and ``__ne__`` methods that check two instances + for equality. To override how the attribute value is compared, + pass a ``callable`` that takes a single value and returns the value + to be compared. + :type eq: a `bool` or a `callable`. + + :param order: If ``True`` (default), include this attributes in the + generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. + To override how the attribute value is ordered, + pass a ``callable`` that takes a single value and returns the value + to be ordered. + :type order: a `bool` or a `callable`. + + :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the + same value. Must not be mixed with *eq* or *order*. + :type cmp: a `bool` or a `callable`. + + :param Optional[bool] hash: Include this attribute in the generated + ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This + is the correct behavior according the Python spec. Setting this value + to anything else than ``None`` is *discouraged*. + :param bool init: Include this attribute in the generated ``__init__`` + method. It is possible to set this to ``False`` and set a default + value. In that case this attributed is unconditionally initialized + with the specified default value or factory. + :param callable converter: `callable` that is called by + ``attrs``-generated ``__init__`` methods to convert attribute's value + to the desired format. It is given the passed-in value, and the + returned value will be used as the new value of the attribute. The + value is converted before being passed to the validator, if any. + :param metadata: An arbitrary mapping, to be used by third-party + components. See `extending_metadata`. + :param type: The type of the attribute. In Python 3.6 or greater, the + preferred method to specify the type is using a variable annotation + (see `PEP 526 `_). + This argument is provided for backward compatibility. + Regardless of the approach used, the type will be stored on + ``Attribute.type``. + + Please note that ``attrs`` doesn't do anything with this metadata by + itself. You can use it as part of your own code or for + `static type checking `. + :param kw_only: Make this attribute keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param on_setattr: Allows to overwrite the *on_setattr* setting from + `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. + Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `attr.s`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attrs.setters.NO_OP` + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is ``None`` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated + *convert* to achieve consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + + if factory is not None: + if default is not NOTHING: + raise ValueError( + "The `default` and `factory` arguments are mutually " + "exclusive." + ) + if not callable(factory): + raise ValueError("The `factory` argument must be a callable.") + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + ) + + +def _compile_and_eval(script, globs, locs=None, filename=""): + """ + "Exec" the script with the given global (globs) and local (locs) variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _make_method(name, script, filename, globs=None): + """ + Create the method with the script given and return the method object. + """ + locs = {} + if globs is None: + globs = {} + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + else: + filename = "{}-{}>".format(base_filename[:-1], count) + count += 1 + + _compile_and_eval(script, globs, locs, filename) + + return locs[name] + + +def _make_attr_tuple_class(cls_name, attr_names): + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = "{}Attributes".format(cls_name) + attr_class_template = [ + "class {}(tuple):".format(attr_class_name), + " __slots__ = ()", + ] + if attr_names: + for i, attr_name in enumerate(attr_names): + attr_class_template.append( + _tuple_property_pat.format(index=i, attr_name=attr_name) + ) + else: + attr_class_template.append(" pass") + globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} + _compile_and_eval("\n".join(attr_class_template), globs) + return globs[attr_class_name] + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +_Attributes = _make_attr_tuple_class( + "_Attributes", + [ + # all attributes to build dunder methods for + "attrs", + # attributes that have been inherited + "base_attrs", + # map inherited attributes to their originating classes + "base_attrs_map", + ], +) + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_classvar_prefixes) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + + Requires Python 3. + """ + attr = getattr(cls, attrib_name, _sentinel) + if attr is _sentinel: + return False + + for base_cls in cls.__mro__[1:]: + a = getattr(base_cls, attrib_name, None) + if attr is a: + return False + + return True + + +def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + if _has_own_attribute(cls, "__annotations__"): + return cls.__annotations__ + + return {} + + +def _counter_getter(e): + """ + Key function for sorting to avoid re-creating a lambda for every class. + """ + return e[1].counter + + +def _collect_base_attrs(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + *collect_by_mro* is True, collect them in the correct MRO order, otherwise + use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = [(name, ca) for name, ca in iteritems(these)] + + if not isinstance(these, ordered_dict): + ca_list.sort(key=_counter_getter) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if not isinstance(a, _CountingAttr): + if a is NOTHING: + a = attrib() + else: + a = attrib(default=a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if len(unannotated) > 0: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + ), + key=lambda e: e[1].counter, + ) + + own_attrs = [ + Attribute.from_counting_attr( + name=attr_name, ca=ca, type=anns.get(attr_name) + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + if kw_only: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = base_attrs + own_attrs + + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + raise ValueError( + "No mandatory attributes allowed after an attribute with a " + "default value or factory. Attribute in question: %r" % (a,) + ) + + if had_default is False and a.default is not NOTHING: + had_default = True + + if field_transformer is not None: + attrs = field_transformer(cls, attrs) + + # Create AttrsClass *after* applying the field_transformer since it may + # add or remove attributes! + attr_names = [a.name for a in attrs] + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map)) + + +if PYPY: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + +else: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + raise FrozenInstanceError() + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + raise FrozenInstanceError() + + +class _ClassBuilder(object): + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_pre_init", + "_has_post_init", + "_is_exc", + "_on_setattr", + "_slots", + "_weakref_slot", + "_wrote_own_setattr", + "_has_custom_setattr", + ) + + def __init__( + self, + cls, + these, + slots, + frozen, + weakref_slot, + getstate_setstate, + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_custom_setattr, + field_transformer, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if slots else {} + self._attrs = attrs + self._base_names = set(a.name for a in base_attrs) + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = slots + self._frozen = frozen + self._weakref_slot = weakref_slot + self._cache_hash = cache_hash + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = is_exc + self._on_setattr = on_setattr + + self._has_custom_setattr = has_custom_setattr + self._wrote_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + + if frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._wrote_own_setattr = True + elif on_setattr in ( + _ng_default_on_setattr, + setters.validate, + setters.convert, + ): + has_validator = has_converter = False + for a in attrs: + if a.validator is not None: + has_validator = True + if a.converter is not None: + has_converter = True + + if has_validator and has_converter: + break + if ( + ( + on_setattr == _ng_default_on_setattr + and not (has_validator or has_converter) + ) + or (on_setattr == setters.validate and not has_validator) + or (on_setattr == setters.convert and not has_converter) + ): + # If class-level on_setattr is set to convert + validate, but + # there's no field to convert or validate, pretend like there's + # no on_setattr. + self._on_setattr = None + + if getstate_setstate: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + def __repr__(self): + return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + if self._slots is True: + return self._create_slots_class() + else: + return self._patch_original_class() + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _sentinel) is not _sentinel + ): + try: + delattr(cls, name) + except AttributeError: + # This can happen if a base class defines a class + # variable and we want to set an attribute with the + # same name by using only a type annotation. + pass + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._wrote_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = object.__setattr__ + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in iteritems(self._cls_dict) + if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") + } + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._wrote_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = object.__setattr__ + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = dict() + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overriden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in iteritems(existing_slots) + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_hash_cache_field) + cd["__slots__"] = tuple(slot_names) + + qualname = getattr(self._cls, "__qualname__", None) + if qualname is not None: + cd["__qualname__"] = qualname + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # . On Python 3, + # if a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in cls.__dict__.values(): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # ValueError: Cell is empty + pass + else: + if match: + set_closure_cell(cell, cls) + + return cls + + def add_repr(self, ns): + self._cls_dict["__repr__"] = self._add_method_dunders( + _make_repr(self._attrs, ns, self._cls) + ) + return self + + def add_str(self): + repr = self._cls_dict.get("__repr__") + if repr is None: + raise ValueError( + "__str__ can only be generated if a __repr__ exists." + ) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return tuple(getattr(self, name) for name in state_attr_names) + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_hash_cache_field, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + self._cls_dict["__hash__"] = self._add_method_dunders( + _make_hash( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + ) + + return self + + def add_init(self): + self._cls_dict["__init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=False, + ) + ) + + return self + + def add_match_args(self): + self._cls_dict["__match_args__"] = tuple( + field.name + for field in self._attrs + if field.init and not field.kw_only + ) + + def add_attrs_init(self): + self._cls_dict["__attrs_init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=True, + ) + ) + + return self + + def add_eq(self): + cd = self._cls_dict + + cd["__eq__"] = self._add_method_dunders( + _make_eq(self._cls, self._attrs) + ) + cd["__ne__"] = self._add_method_dunders(_make_ne()) + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + if self._frozen: + return self + + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + raise ValueError( + "Can't combine custom __setattr__ with on_setattr hooks." + ) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _obj_setattr(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._wrote_own_setattr = True + + return self + + def _add_method_dunders(self, method): + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + try: + method.__module__ = self._cls.__module__ + except AttributeError: + pass + + try: + method.__qualname__ = ".".join( + (self._cls.__qualname__, method.__name__) + ) + except AttributeError: + pass + + try: + method.__doc__ = "Method generated by attrs for class %s." % ( + self._cls.__qualname__, + ) + except AttributeError: + pass + + return method + + +_CMP_DEPRECATION = ( + "The usage of `cmp` is deprecated and will be removed on or after " + "2021-06-01. Please use `eq` and `order` instead." +) + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + + auto_detect must be False on Python 2. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, +): + r""" + A class decorator that adds `dunder + `_\ -methods according to the + specified attributes using `attr.ib` or the *these* argument. + + :param these: A dictionary of name to `attr.ib` mappings. This is + useful to avoid the definition of your attributes within the class body + because you can't (e.g. if you want to add ``__repr__`` methods to + Django models) or don't want to. + + If *these* is not ``None``, ``attrs`` will *not* search the class body + for attributes and will *not* remove any attributes from it. + + If *these* is an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the attributes inside *these*. Otherwise the order + of the definition of the attributes is used. + + :type these: `dict` of `str` to `attr.ib` + + :param str repr_ns: When using nested classes, there's no way in Python 2 + to automatically detect that. Therefore it's possible to set the + namespace explicitly for a more meaningful ``repr`` output. + :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, + *order*, and *hash* arguments explicitly, assume they are set to + ``True`` **unless any** of the involved methods for one of the + arguments is implemented in the *current* class (i.e. it is *not* + inherited from some base class). + + So for example by implementing ``__eq__`` on a class yourself, + ``attrs`` will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible + ``__ne__`` by default, so it *should* be enough to only implement + ``__eq__`` in most cases). + + .. warning:: + + If you prevent ``attrs`` from creating the ordering methods for you + (``order=False``, e.g. by implementing ``__le__``), it becomes + *your* responsibility to make sure its ordering is sound. The best + way is to use the `functools.total_ordering` decorator. + + + Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, + *cmp*, or *hash* overrides whatever *auto_detect* would determine. + + *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises + an `attrs.exceptions.PythonTooOldError`. + + :param bool repr: Create a ``__repr__`` method with a human readable + representation of ``attrs`` attributes.. + :param bool str: Create a ``__str__`` method that is identical to + ``__repr__``. This is usually not necessary except for + `Exception`\ s. + :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__`` + and ``__ne__`` methods that check two instances for equality. + + They compare the instances as if they were tuples of their ``attrs`` + attributes if and only if the types of both classes are *identical*! + :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``, + ``__gt__``, and ``__ge__`` methods that behave like *eq* above and + allow instances to be ordered. If ``None`` (default) mirror value of + *eq*. + :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* + and *order* to the same value. Must not be mixed with *eq* or *order*. + :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method + is generated according how *eq* and *frozen* are set. + + 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to + None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning the + ``__hash__`` method of the base class will be used (if base class is + ``object``, this means it will fall back to id-based hashing.). + + Although not recommended, you can decide for yourself and force + ``attrs`` to create one (e.g. if the class is immutable even though you + didn't freeze it programmatically) by passing ``True`` or not. Both of + these cases are rather special and should be used carefully. + + See our documentation on `hashing`, Python's documentation on + `object.__hash__`, and the `GitHub issue that led to the default \ + behavior `_ for more + details. + :param bool init: Create a ``__init__`` method that initializes the + ``attrs`` attributes. Leading underscores are stripped for the argument + name. If a ``__attrs_pre_init__`` method exists on the class, it will + be called before the class is initialized. If a ``__attrs_post_init__`` + method exists on the class, it will be called after the class is fully + initialized. + + If ``init`` is ``False``, an ``__attrs_init__`` method will be + injected instead. This allows you to define a custom ``__init__`` + method that can do pre-init work such as ``super().__init__()``, + and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. + :param bool slots: Create a `slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the default + dict classes, but have some gotchas you should know about, so we + encourage you to read the `glossary entry `. + :param bool frozen: Make instances immutable after initialization. If + someone attempts to modify a frozen instance, + `attr.exceptions.FrozenInstanceError` is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` method + on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other words: + ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You can + circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + :param bool weakref_slot: Make instances weak-referenceable. This has no + effect unless ``slots`` is also enabled. + :param bool auto_attribs: If ``True``, collect `PEP 526`_-annotated + attributes (Python 3.6 and later only) from the class body. + + In this case, you **must** annotate every field. If ``attrs`` + encounters a field that is set to an `attr.ib` but lacks a type + annotation, an `attr.exceptions.UnannotatedAttributeError` is + raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't + want to set a type. + + If you assign a value to those attributes (e.g. ``x: int = 42``), that + value becomes the default value like if it were passed using + ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also + works as expected in most cases (see warning below). + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `attr.ib` are **ignored**. + + .. warning:: + For features that use the attribute name to create decorators (e.g. + `validators `), you still *must* assign `attr.ib` to + them. Otherwise Python will either not find the name or try to use + the default value to call e.g. ``validator`` on it. + + These errors can be quite confusing and probably the most common bug + report on our bug tracker. + + .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/ + :param bool kw_only: Make all attributes keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param bool cache_hash: Ensure that the object's hash code is computed + only once and stored on the object. If this is set to ``True``, + hashing must be either explicitly or implicitly enabled for this + class. If the hash code is cached, avoid any reassignments of + fields involved in hash code computation or mutations of the objects + those fields point to after object creation. If such changes occur, + the behavior of the object's hash code is undefined. + :param bool auto_exc: If the class subclasses `BaseException` + (which implicitly includes any subclass of any exception), the + following happens to behave like a well-behaved Python exceptions + class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids (N.B. ``attrs`` will + *not* remove existing implementations of ``__hash__`` or the equality + methods. It just won't add own ones.), + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the ``args`` + attribute, + - the value of *str* is ignored leaving ``__str__`` to base classes. + :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` + collects attributes from base classes. The default behavior is + incorrect in certain cases of multiple inheritance. It should be on by + default but is kept off for backward-compatibility. + + See issue `#428 `_ for + more details. + + :param Optional[bool] getstate_setstate: + .. note:: + This is usually only interesting for slotted classes and you should + probably just set *auto_detect* to `True`. + + If `True`, ``__getstate__`` and + ``__setstate__`` are generated and attached to the class. This is + necessary for slotted classes to be pickleable. If left `None`, it's + `True` by default for slotted classes and ``False`` for dict classes. + + If *auto_detect* is `True`, and *getstate_setstate* is left `None`, + and **either** ``__getstate__`` or ``__setstate__`` is detected directly + on the class (i.e. not inherited), it is set to `False` (this is usually + what you want). + + :param on_setattr: A callable that is run whenever the user attempts to set + an attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + as validators: the instance, the attribute that is being modified, and + the new value. + + If no exception is raised, the attribute is set to the return value of + the callable. + + If a list of callables is passed, they're automatically wrapped in an + `attrs.setters.pipe`. + + :param Optional[callable] field_transformer: + A function that is called with the original class object and all + fields right before ``attrs`` finalizes the class. You can use + this, e.g., to automatically add converters or validators to + fields based on their types. See `transform-fields` for more details. + + :param bool match_args: + If `True` (default), set ``__match_args__`` on the class to support + `PEP 634 `_ (Structural + Pattern Matching). It is a tuple of all positional-only ``__init__`` + parameter names on Python 3.10 and later. Ignored on older Python + versions. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports ``None`` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 21.3.0 *match_args* + """ + if auto_detect and PY2: + raise PythonTooOldError( + "auto_detect only works on Python 3 and later." + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + hash_ = hash # work around the lack of nonlocal + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + + if getattr(cls, "__class__", None) is None: + raise TypeError("attrs only works with new-style classes.") + + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + raise ValueError("Can't freeze a class with a custom __setattr__.") + + builder = _ClassBuilder( + cls, + these, + slots, + is_frozen, + weakref_slot, + _determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_own_setattr, + field_transformer, + ) + if _determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ): + builder.add_repr(repr_ns) + if str is True: + builder.add_str() + + eq = _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + if not is_exc and eq is True: + builder.add_eq() + if not is_exc and _determine_whether_to_implement( + cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") + ): + builder.add_order() + + builder.add_setattr() + + if ( + hash_ is None + and auto_detect is True + and _has_own_attribute(cls, "__hash__") + ): + hash = False + else: + hash = hash_ + if hash is not True and hash is not False and hash is not None: + # Can't use `hash in` because 1 == True for example. + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + elif hash is False or (hash is None and eq is False) or is_exc: + # Don't do anything. Should fall back to __object__'s __hash__ + # which is by id. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + elif hash is True or ( + hash is None and eq is True and is_frozen is True + ): + # Build a __hash__ if told so, or if it's safe. + builder.add_hash() + else: + # Raise TypeError on attempts to hash. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + builder.make_unhashable() + + if _determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ): + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " init must be True." + ) + + if ( + PY310 + and match_args + and not _has_own_attribute(cls, "__match_args__") + ): + builder.add_match_args() + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +if PY2: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return ( + getattr(cls.__setattr__, "__module__", None) + == _frozen_setattrs.__module__ + and cls.__setattr__.__name__ == _frozen_setattrs.__name__ + ) + +else: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ == _frozen_setattrs + + +def _generate_unique_filename(cls, func_name): + """ + Create a "filename" suitable for a function being generated. + """ + unique_filename = "".format( + func_name, + cls.__module__, + getattr(cls, "__qualname__", cls.__name__), + ) + return unique_filename + + +def _make_hash(cls, attrs, frozen, cache_hash): + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + unique_filename = _generate_unique_filename(cls, "hash") + type_hash = hash(unique_filename) + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + if not PY2: + hash_def += ", *" + + hash_def += ( + ", _cache_wrapper=" + + "__import__('attr._make')._make._CacheHashWrapper):" + ) + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + " %d," % (type_hash,), + ] + ) + + for a in attrs: + method_lines.append(indent + " self.%s," % a.name) + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + "if self.%s is None:" % _hash_cache_field) + if frozen: + append_hash_computation_lines( + "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + "self.%s = " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab + "return self.%s" % _hash_cache_field) + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return _make_method("__hash__", script, unique_filename) + + +def _add_hash(cls, attrs): + """ + Add a hash method to *cls*. + """ + cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False) + return cls + + +def _make_ne(): + """ + Create __ne__ method. + """ + + def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + return __ne__ + + +def _make_eq(cls, attrs): + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + unique_filename = _generate_unique_filename(cls, "eq") + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + # We can't just do a big self.x = other.x and... clause due to + # irregularities like nan == nan is false but (nan,) == (nan,) is true. + globs = {} + if attrs: + lines.append(" return (") + others = [" ) == ("] + for a in attrs: + if a.eq_key: + cmp_name = "_%s_key" % (a.name,) + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + " %s(self.%s)," + % ( + cmp_name, + a.name, + ) + ) + others.append( + " %s(other.%s)," + % ( + cmp_name, + a.name, + ) + ) + else: + lines.append(" self.%s," % (a.name,)) + others.append(" other.%s," % (a.name,)) + + lines += others + [" )"] + else: + lines.append(" return True") + + script = "\n".join(lines) + + return _make_method("__eq__", script, unique_filename, globs) + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__eq__ = _make_eq(cls, attrs) + cls.__ne__ = _make_ne() + + return cls + + +if HAS_F_STRINGS: + + def _make_repr(attrs, ns, cls): + unique_filename = _generate_unique_filename(cls, "repr") + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, (repr if a.repr is True else a.repr), a.init) + for a in attrs + if a.repr is not False + ) + globs = { + name + "_repr": r + for name, r, _ in attr_names_with_reprs + if r != repr + } + globs["_compat"] = _compat + globs["AttributeError"] = AttributeError + globs["NOTHING"] = NOTHING + attribute_fragments = [] + for name, r, i in attr_names_with_reprs: + accessor = ( + "self." + name + if i + else 'getattr(self, "' + name + '", NOTHING)' + ) + fragment = ( + "%s={%s!r}" % (name, accessor) + if r == repr + else "%s={%s_repr(%s)}" % (name, name, accessor) + ) + attribute_fragments.append(fragment) + repr_fragment = ", ".join(attribute_fragments) + + if ns is None: + cls_name_fragment = ( + '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' + ) + else: + cls_name_fragment = ns + ".{self.__class__.__name__}" + + lines = [ + "def __repr__(self):", + " try:", + " already_repring = _compat.repr_context.already_repring", + " except AttributeError:", + " already_repring = {id(self),}", + " _compat.repr_context.already_repring = already_repring", + " else:", + " if id(self) in already_repring:", + " return '...'", + " else:", + " already_repring.add(id(self))", + " try:", + " return f'%s(%s)'" % (cls_name_fragment, repr_fragment), + " finally:", + " already_repring.remove(id(self))", + ] + + return _make_method( + "__repr__", "\n".join(lines), unique_filename, globs=globs + ) + +else: + + def _make_repr(attrs, ns, _): + """ + Make a repr method that includes relevant *attrs*, adding *ns* to the + full name. + """ + + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, repr if a.repr is True else a.repr) + for a in attrs + if a.repr is not False + ) + + def __repr__(self): + """ + Automatically created by attrs. + """ + try: + already_repring = _compat.repr_context.already_repring + except AttributeError: + already_repring = set() + _compat.repr_context.already_repring = already_repring + + if id(self) in already_repring: + return "..." + real_cls = self.__class__ + if ns is None: + qualname = getattr(real_cls, "__qualname__", None) + if qualname is not None: # pragma: no cover + # This case only happens on Python 3.5 and 3.6. We exclude + # it from coverage, because we don't want to slow down our + # test suite by running them under coverage too for this + # one line. + class_name = qualname.rsplit(">.", 1)[-1] + else: + class_name = real_cls.__name__ + else: + class_name = ns + "." + real_cls.__name__ + + # Since 'self' remains on the stack (i.e.: strongly referenced) + # for the duration of this call, it's safe to depend on id(...) + # stability, and not need to track the instance and therefore + # worry about properties like weakref- or hash-ability. + already_repring.add(id(self)) + try: + result = [class_name, "("] + first = True + for name, attr_repr in attr_names_with_reprs: + if first: + first = False + else: + result.append(", ") + result.extend( + (name, "=", attr_repr(getattr(self, name, NOTHING))) + ) + return "".join(result) + ")" + finally: + already_repring.remove(id(self)) + + return __repr__ + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__repr__ = _make_repr(attrs, ns, cls) + return cls + + +def fields(cls): + """ + Return the tuple of ``attrs`` attributes for a class. + + The tuple also allows accessing the fields by their names (see below for + examples). + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: tuple (with name accessors) of `attrs.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of ``attrs`` attributes for a class, whose + keys are the attribute names. + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: an ordered dict where keys are attribute names and values are + `attrs.Attribute`\\ s. This will be a `dict` if it's + naturally ordered like on Python 3.6+ or an + :class:`~collections.OrderedDict` otherwise. + + .. versionadded:: 18.1.0 + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return ordered_dict(((a.name, a) for a in attrs)) + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + :param inst: Instance of a class with ``attrs`` attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_cls(cls): + return "__slots__" in cls.__dict__ + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) + + +def _make_init( + cls, + attrs, + pre_init, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + cls_on_setattr, + attrs_init, +): + has_cls_on_setattr = ( + cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP + ) + + if frozen and has_cls_on_setattr: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = True + elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: + needs_cached_setattr = True + + unique_filename = _generate_unique_filename(cls, "init") + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + attrs_init, + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr"] = _obj_setattr + + init = _make_method( + "__attrs_init__" if attrs_init else "__init__", + script, + unique_filename, + globs, + ) + init.__annotations__ = annotations + + return init + + +def _setattr(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return "_setattr('%s', %s)" % (attr_name, value_var) + + +def _setattr_with_converter(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return "_setattr('%s', %s(%s))" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _assign(attr_name, value, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return "self.%s = %s" % (attr_name, value) + + +def _assign_with_converter(attr_name, value_var, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True) + + return "self.%s = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +if PY2: + + def _unpack_kw_only_py2(attr_name, default=None): + """ + Unpack *attr_name* from _kw_only dict. + """ + if default is not None: + arg_default = ", %s" % default + else: + arg_default = "" + return "%s = _kw_only.pop('%s'%s)" % ( + attr_name, + attr_name, + arg_default, + ) + + def _unpack_kw_only_lines_py2(kw_only_args): + """ + Unpack all *kw_only_args* from _kw_only dict and handle errors. + + Given a list of strings "{attr_name}" and "{attr_name}={default}" + generates list of lines of code that pop attrs from _kw_only dict and + raise TypeError similar to builtin if required attr is missing or + extra key is passed. + + >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"]))) + try: + a = _kw_only.pop('a') + b = _kw_only.pop('b', 42) + except KeyError as _key_error: + raise TypeError( + ... + if _kw_only: + raise TypeError( + ... + """ + lines = ["try:"] + lines.extend( + " " + _unpack_kw_only_py2(*arg.split("=")) + for arg in kw_only_args + ) + lines += """\ +except KeyError as _key_error: + raise TypeError( + '__init__() missing required keyword-only argument: %s' % _key_error + ) +if _kw_only: + raise TypeError( + '__init__() got an unexpected keyword argument %r' + % next(iter(_kw_only)) + ) +""".split( + "\n" + ) + return lines + + +def _attrs_to_init_script( + attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + attrs_init, +): + """ + Return a script of an initializer for *attrs* and a dict of globals. + + The globals are expected by the generated script. + + If *frozen* is True, we cannot set the attributes directly so we use + a cached ``object.__setattr__``. + """ + lines = [] + if pre_init: + lines.append("self.__attrs_pre_init__()") + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. + # Note _setattr will be used again below if cache_hash is True + "_setattr = _cached_setattr.__get__(self, self.__class__)" + ) + + if frozen is True: + if slots is True: + fmt_setter = _setattr + fmt_setter_with_converter = _setattr_with_converter + else: + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + lines.append("_inst_dict = self.__dict__") + + def fmt_setter(attr_name, value_var, has_on_setattr): + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return "_inst_dict['%s'] = %s" % (attr_name, value_var) + + def fmt_setter_with_converter( + attr_name, value_var, has_on_setattr + ): + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr + ) + + return "_inst_dict['%s'] = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + else: + # Not frozen. + fmt_setter = _assign + fmt_setter_with_converter = _assign_with_converter + + args = [] + kw_only_args = [] + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_cls_on_setattr + ) + arg_name = a.name.lstrip("_") + + has_factory = isinstance(a.default, Factory) + if has_factory and a.default.takes_self: + maybe_self = "self" + else: + maybe_self = "" + + if a.init is False: + if has_factory: + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = "%s=NOTHING" % (arg_name,) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + lines.append("if %s is not NOTHING:" % (arg_name,)) + + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and a.converter is None: + annotations[arg_name] = a.type + elif a.converter is not None and not PY2: + # Try to get the type from the converter. + sig = None + try: + sig = inspect.signature(a.converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + sig_params = list(sig.parameters.values()) + if ( + sig_params + and sig_params[0].annotation + is not inspect.Parameter.empty + ): + annotations[arg_name] = sig_params[0].annotation + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append( + " %s(self, %s, self.%s)" % (val_name, attr_name, a.name) + ) + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if post_init: + lines.append("self.__attrs_post_init__()") + + # because this is set only after __attrs_post_init is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to + # field values during post-init combined with post-init accessing the + # hash code would result in silent bugs. + if cache_hash: + if frozen: + if slots: + # if frozen and slots, then _setattr defined above + init_hash_cache = "_setattr('%s', %s)" + else: + # if frozen and not slots, then _inst_dict defined above + init_hash_cache = "_inst_dict['%s'] = %s" + else: + init_hash_cache = "self.%s = %s" + lines.append(init_hash_cache % (_hash_cache_field, "None")) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join("self." + a.name for a in attrs if a.init) + + lines.append("BaseException.__init__(self, %s)" % (vals,)) + + args = ", ".join(args) + if kw_only_args: + if PY2: + lines = _unpack_kw_only_lines_py2(kw_only_args) + lines + + args += "%s**_kw_only" % (", " if args else "",) # leading comma + else: + args += "%s*, %s" % ( + ", " if args else "", # leading comma + ", ".join(kw_only_args), # kw_only args + ) + return ( + """\ +def {init_name}(self, {args}): + {lines} +""".format( + init_name=("__attrs_init__" if attrs_init else "__init__"), + args=args, + lines="\n ".join(lines) if lines else "pass", + ), + names_for_globals, + annotations, + ) + + +class Attribute(object): + """ + *Read-only* representation of an attribute. + + The class has *all* arguments of `attr.ib` (except for ``factory`` + which is only syntactic sugar for ``default=Factory(...)`` plus the + following: + + - ``name`` (`str`): The name of the attribute. + - ``inherited`` (`bool`): Whether or not that attribute has been inherited + from a base class. + - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables + that are used for comparing and ordering objects by this attribute, + respectively. These are set by passing a callable to `attr.ib`'s ``eq``, + ``order``, or ``cmp`` arguments. See also :ref:`comparison customization + `. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The :ref:`field transformer ` hook receives a list of + them. + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + + For the full version history of the fields, see `attr.ib`. + """ + + __slots__ = ( + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _obj_setattr.__get__(self, Attribute) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + metadata_proxy(metadata) + if metadata + else _empty_metadata_singleton + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + + def __setattr__(self, name, value): + raise FrozenInstanceError() + + @classmethod + def from_counting_attr(cls, name, ca, type=None): + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + raise ValueError( + "Type annotation and type argument cannot both be present" + ) + inst_dict = { + k: getattr(ca, k) + for k in Attribute.__slots__ + if k + not in ( + "name", + "validator", + "default", + "type", + "inherited", + ) # exclude methods and deprecated alias + } + return cls( + name=name, + validator=ca._validator, + default=ca._default, + type=type, + cmp=None, + inherited=False, + **inst_dict + ) + + @property + def cmp(self): + """ + Simulate the presence of a cmp attribute and warn. + """ + warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=2) + + return self.eq and self.order + + # Don't use attr.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attr.evolve` but that function does not work + with ``Attribute``. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + metadata_proxy(value) + if value + else _empty_metadata_singleton, + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr(object): + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "counter", + "_default", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "_validator", + "converter", + "type", + "kw_only", + "on_setattr", + ) + __attrs_attrs__ = tuple( + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + ) + ) + ( + Attribute( + name="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + :raises DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError() + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class Factory(object): + """ + Stores a factory callable. + + If passed as the default value to `attrs.field`, the factory is used to + generate a new value. + + :param callable factory: A callable that takes either none or exactly one + mandatory positional argument depending on *takes_self*. + :param bool takes_self: Pass the partially initialized instance that is + being initialized as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + """ + `Factory` is part of the default machinery so if we want a default + value here, we have to implement it ourselves. + """ + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +def make_class(name, attrs, bases=(object,), **attributes_arguments): + """ + A quick way to create a new class called *name* with *attrs*. + + :param str name: The name for the new class. + + :param attrs: A list of names or a dictionary of mappings of names to + attributes. + + If *attrs* is a list or an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the names or attributes inside *attrs*. Otherwise the + order of the definition of the attributes is used. + :type attrs: `list` or `dict` + + :param tuple bases: Classes that the new class will subclass. + + :param attributes_arguments: Passed unmodified to `attr.s`. + + :return: A new class with *attrs*. + :rtype: type + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + """ + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = dict((a, attrib()) for a in attrs) + else: + raise TypeError("attrs argument must be a dict or a list.") + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + try: + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + except (AttributeError, ValueError): + pass + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + return _attrs(these=cls_dict, **attributes_arguments)(type_) + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, hash=True) +class _AndValidator(object): + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + :param callables validators: Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if + they have any. + + :param callables converters: Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + def pipe_converter(val): + for converter in converters: + val = converter(val) + + return val + + if not PY2: + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = typing.TypeVar("A") + pipe_converter.__annotations__ = {"val": A, "return": A} + else: + # Get parameter type. + sig = None + try: + sig = inspect.signature(converters[0]) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if ( + params + and params[0].annotation is not inspect.Parameter.empty + ): + pipe_converter.__annotations__["val"] = params[ + 0 + ].annotation + # Get return type. + sig = None + try: + sig = inspect.signature(converters[-1]) + except (ValueError, TypeError): # inspect failed + pass + if sig and sig.return_annotation is not inspect.Signature().empty: + pipe_converter.__annotations__[ + "return" + ] = sig.return_annotation + + return pipe_converter diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_next_gen.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_next_gen.py new file mode 100644 index 0000000..0682536 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_next_gen.py @@ -0,0 +1,216 @@ +# SPDX-License-Identifier: MIT + +""" +These are Python 3.6+-only and keyword-only APIs that call `attr.s` and +`attr.ib` with different default values. +""" + + +from functools import partial + +from . import setters +from ._funcs import asdict as _asdict +from ._funcs import astuple as _astuple +from ._make import ( + NOTHING, + _frozen_setattrs, + _ng_default_on_setattr, + attrib, + attrs, +) +from .exceptions import UnannotatedAttributeError + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, +): + r""" + Define an ``attrs`` class. + + Differences to the classic `attr.s` that it uses underneath: + + - Automatically detect whether or not *auto_attribs* should be `True` + (c.f. *auto_attribs* parameter). + - If *frozen* is `False`, run converters and validators when setting an + attribute by default. + - *slots=True* (see :term:`slotted classes` for potentially surprising + behaviors) + - *auto_exc=True* + - *auto_detect=True* + - *order=False* + - *match_args=True* + - Some options that were only relevant on Python 2 or were kept around for + backwards-compatibility have been removed. + + Please note that these are all defaults and you can change them as you + wish. + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If any attributes are annotated and no unannotated `attrs.fields`\ s + are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attrs.fields`\ s. + + For now, please refer to `attr.s` for the rest of the parameters. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + match_args=match_args, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes convert & validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = _ng_default_on_setattr + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + raise ValueError( + "Frozen classes can't use on_setattr " + "(frozen-ness was inherited)." + ) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Identical to `attr.ib`, except keyword-only and with some arguments + removed. + + .. versionadded:: 20.1.0 + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + ) + + +def asdict(inst, *, recurse=True, filter=None, value_serializer=None): + """ + Same as `attr.asdict`, except that collections types are always retained + and dict is always used as *dict_factory*. + + .. versionadded:: 21.3.0 + """ + return _asdict( + inst=inst, + recurse=recurse, + filter=filter, + value_serializer=value_serializer, + retain_collection_types=True, + ) + + +def astuple(inst, *, recurse=True, filter=None): + """ + Same as `attr.astuple`, except that collections types are always retained + and `tuple` is always used as the *tuple_factory*. + + .. versionadded:: 21.3.0 + """ + return _astuple( + inst=inst, recurse=recurse, filter=filter, retain_collection_types=True + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.py new file mode 100644 index 0000000..cdaeec3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo(object): + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.pyi new file mode 100644 index 0000000..45ced08 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.py new file mode 100644 index 0000000..1fb6c05 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.py @@ -0,0 +1,155 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful converters. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import PY2 +from ._make import NOTHING, Factory, pipe + + +if not PY2: + import inspect + import typing + + +__all__ = [ + "default_if_none", + "optional", + "pipe", + "to_bool", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to ``None``. + + Type annotations will be inferred from the wrapped converter's, if it + has any. + + :param callable converter: the converter that is used for non-``None`` + values. + + .. versionadded:: 17.1.0 + """ + + def optional_converter(val): + if val is None: + return None + return converter(val) + + if not PY2: + sig = None + try: + sig = inspect.signature(converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + optional_converter.__annotations__["val"] = typing.Optional[ + params[0].annotation + ] + if sig.return_annotation is not inspect.Signature.empty: + optional_converter.__annotations__["return"] = typing.Optional[ + sig.return_annotation + ] + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace ``None`` values by *default* or the + result of *factory*. + + :param default: Value to be used if ``None`` is passed. Passing an instance + of `attrs.Factory` is supported, however the ``takes_self`` option + is *not*. + :param callable factory: A callable that takes no parameters whose result + is used if ``None`` is passed. + + :raises TypeError: If **neither** *default* or *factory* is passed. + :raises TypeError: If **both** *default* and *factory* are passed. + :raises ValueError: If an instance of `attrs.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + raise TypeError("Must pass either `default` or `factory`.") + + if default is not NOTHING and factory is not None: + raise TypeError( + "Must pass either `default` or `factory` but not both." + ) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + raise ValueError( + "`takes_self` is not supported by default_if_none." + ) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter + + +def to_bool(val): + """ + Convert "boolean" strings (e.g., from env. vars.) to real booleans. + + Values mapping to :code:`True`: + + - :code:`True` + - :code:`"true"` / :code:`"t"` + - :code:`"yes"` / :code:`"y"` + - :code:`"on"` + - :code:`"1"` + - :code:`1` + + Values mapping to :code:`False`: + + - :code:`False` + - :code:`"false"` / :code:`"f"` + - :code:`"no"` / :code:`"n"` + - :code:`"off"` + - :code:`"0"` + - :code:`0` + + :raises ValueError: for any other value. + + .. versionadded:: 21.3.0 + """ + if isinstance(val, str): + val = val.lower() + truthy = {True, "true", "t", "yes", "y", "on", "1", 1} + falsy = {False, "false", "f", "no", "n", "off", "0", 0} + try: + if val in truthy: + return True + if val in falsy: + return False + except TypeError: + # Raised when "val" is not hashable (e.g., lists) + pass + raise ValueError("Cannot convert value to bool: {}".format(val)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.pyi new file mode 100644 index 0000000..0f58088 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/converters.pyi @@ -0,0 +1,13 @@ +from typing import Callable, Optional, TypeVar, overload + +from . import _ConverterType + +_T = TypeVar("_T") + +def pipe(*validators: _ConverterType) -> _ConverterType: ... +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: _T) -> _ConverterType: ... +@overload +def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ... +def to_bool(val: str) -> bool: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.py new file mode 100644 index 0000000..b2f1edc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.py @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: MIT + +from __future__ import absolute_import, division, print_function + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + msg = "can't set attribute" + args = [msg] + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An ``attrs`` function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-``attrs`` class has been passed into an ``attrs`` function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set using ``attr.ib()`` and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type + annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an ``attrs`` feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A ``attr.ib()`` requiring a callable has been set with a value + that is not callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.pyi new file mode 100644 index 0000000..f268011 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/exceptions.pyi @@ -0,0 +1,17 @@ +from typing import Any + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.py new file mode 100644 index 0000000..a1978a8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.py @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attr.asdict`. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import isclass +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isclass(cls)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Include *what*. + + :param what: What to include. + :type what: `list` of `type` or `attrs.Attribute`\\ s + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def include_(attribute, value): + return value.__class__ in cls or attribute in attrs + + return include_ + + +def exclude(*what): + """ + Exclude *what*. + + :param what: What to exclude. + :type what: `list` of classes or `attrs.Attribute`\\ s. + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def exclude_(attribute, value): + return value.__class__ not in cls and attribute not in attrs + + return exclude_ diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.pyi new file mode 100644 index 0000000..9938668 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/filters.pyi @@ -0,0 +1,6 @@ +from typing import Any, Union + +from . import Attribute, _FilterType + +def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... +def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/py.typed b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.py new file mode 100644 index 0000000..b1cbb5d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly used hooks for on_setattr. +""" + +from __future__ import absolute_import, division, print_function + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError() + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + return c(new_value) + + return new_value + + +NO_OP = object() +""" +Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. + +Does not work in `pipe` or within lists. + +.. versionadded:: 20.1.0 +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.pyi new file mode 100644 index 0000000..3f5603c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/setters.pyi @@ -0,0 +1,19 @@ +from typing import Any, NewType, NoReturn, TypeVar, cast + +from . import Attribute, _OnSetAttrType + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.py new file mode 100644 index 0000000..0b0c834 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.py @@ -0,0 +1,561 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful validators. +""" + +from __future__ import absolute_import, division, print_function + +import operator +import re + +from contextlib import contextmanager + +from ._config import get_run_validators, set_run_validators +from ._make import _AndValidator, and_, attrib, attrs +from .exceptions import NotCallableError + + +try: + Pattern = re.Pattern +except AttributeError: # Python <3.7 lacks a Pattern type. + Pattern = type(re.compile("")) + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "disabled", + "ge", + "get_disabled", + "gt", + "in_", + "instance_of", + "is_callable", + "le", + "lt", + "matches_re", + "max_len", + "optional", + "provides", + "set_disabled", +] + + +def set_disabled(disabled): + """ + Globally disable or enable running validators. + + By default, they are run. + + :param disabled: If ``True``, disable running all validators. + :type disabled: bool + + .. warning:: + + This function is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(not disabled) + + +def get_disabled(): + """ + Return a bool indicating whether validators are currently disabled or not. + + :return: ``True`` if validators are currently disabled. + :rtype: bool + + .. versionadded:: 21.3.0 + """ + return not get_run_validators() + + +@contextmanager +def disabled(): + """ + Context manager that disables running validators within its context. + + .. warning:: + + This context manager is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(False) + try: + yield + finally: + set_run_validators(True) + + +@attrs(repr=False, slots=True, hash=True) +class _InstanceOfValidator(object): + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + raise TypeError( + "'{name}' must be {type!r} (got {value!r} that is a " + "{actual!r}).".format( + name=attr.name, + type=self.type, + actual=value.__class__, + value=value, + ), + attr, + self.type, + value, + ) + + def __repr__(self): + return "".format( + type=self.type + ) + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called + with a wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + :param type: The type to check for. + :type type: type or tuple of types + + :raises TypeError: With a human readable error message, the attribute + (of type `attrs.Attribute`), the expected type, and the value it + got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator(object): + pattern = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + raise ValueError( + "'{name}' must match regex {pattern!r}" + " ({value!r} doesn't)".format( + name=attr.name, pattern=self.pattern.pattern, value=value + ), + attr, + self.pattern, + value, + ) + + def __repr__(self): + return "".format( + pattern=self.pattern + ) + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called + with a string that doesn't match *regex*. + + :param regex: a regex string or precompiled pattern to match against + :param int flags: flags that will be passed to the underlying re function + (default 0) + :param callable func: which underlying `re` function to call (options + are `re.fullmatch`, `re.search`, `re.match`, default + is ``None`` which means either `re.fullmatch` or an emulation of + it on Python 2). For performance reasons, they won't be used directly + but on a pre-`re.compile`\ ed pattern. + + .. versionadded:: 19.2.0 + .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. + """ + fullmatch = getattr(re, "fullmatch", None) + valid_funcs = (fullmatch, None, re.search, re.match) + if func not in valid_funcs: + raise ValueError( + "'func' must be one of {}.".format( + ", ".join( + sorted( + e and e.__name__ or "None" for e in set(valid_funcs) + ) + ) + ) + ) + + if isinstance(regex, Pattern): + if flags: + raise TypeError( + "'flags' can only be used with a string pattern; " + "pass flags to re.compile() instead" + ) + pattern = regex + else: + pattern = re.compile(regex, flags) + + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + elif fullmatch: + match_func = pattern.fullmatch + else: # Python 2 fullmatch emulation (https://bugs.python.org/issue16203) + pattern = re.compile( + r"(?:{})\Z".format(pattern.pattern), pattern.flags + ) + match_func = pattern.match + + return _MatchesReValidator(pattern, match_func) + + +@attrs(repr=False, slots=True, hash=True) +class _ProvidesValidator(object): + interface = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.interface.providedBy(value): + raise TypeError( + "'{name}' must provide {interface!r} which {value!r} " + "doesn't.".format( + name=attr.name, interface=self.interface, value=value + ), + attr, + self.interface, + value, + ) + + def __repr__(self): + return "".format( + interface=self.interface + ) + + +def provides(interface): + """ + A validator that raises a `TypeError` if the initializer is called + with an object that does not provide the requested *interface* (checks are + performed using ``interface.providedBy(value)`` (see `zope.interface + `_). + + :param interface: The interface to check for. + :type interface: ``zope.interface.Interface`` + + :raises TypeError: With a human readable error message, the attribute + (of type `attrs.Attribute`), the expected interface, and the + value it got. + """ + return _ProvidesValidator(interface) + + +@attrs(repr=False, slots=True, hash=True) +class _OptionalValidator(object): + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return "".format( + what=repr(self.validator) + ) + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to ``None`` in addition to satisfying the requirements of + the sub-validator. + + :param validator: A validator (or a list of validators) that is used for + non-``None`` values. + :type validator: callable or `list` of callables. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + """ + if isinstance(validator, list): + return _OptionalValidator(_AndValidator(validator)) + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, hash=True) +class _InValidator(object): + options = attrib() + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + raise ValueError( + "'{name}' must be in {options!r} (got {value!r})".format( + name=attr.name, options=self.options, value=value + ) + ) + + def __repr__(self): + return "".format( + options=self.options + ) + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called + with a value that does not belong in the options provided. The check is + performed using ``value in options``. + + :param options: Allowed options. + :type options: list, tuple, `enum.Enum`, ... + + :raises ValueError: With a human readable error message, the attribute (of + type `attrs.Attribute`), the expected options, and the value it + got. + + .. versionadded:: 17.1.0 + """ + return _InValidator(options) + + +@attrs(repr=False, slots=False, hash=True) +class _IsCallableValidator(object): + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attr.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute + that is not callable. + + .. versionadded:: 19.1.0 + + :raises `attr.exceptions.NotCallableError`: With a human readable error + message containing the attribute (`attrs.Attribute`) name, + and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, hash=True) +class _DeepIterable(object): + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else " {iterable!r}".format(iterable=self.iterable_validator) + ) + return ( + "" + ).format( + iterable_identifier=iterable_identifier, + member=self.member_validator, + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + :param member_validator: Validator to apply to iterable members + :param iterable_validator: Validator to apply to iterable itself + (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, hash=True) +class _DeepMapping(object): + key_validator = attrib(validator=is_callable()) + value_validator = attrib(validator=is_callable()) + mapping_validator = attrib(default=None, validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + self.key_validator(inst, attr, key) + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return ( + "" + ).format(key=self.key_validator, value=self.value_validator) + + +def deep_mapping(key_validator, value_validator, mapping_validator=None): + """ + A validator that performs deep validation of a dictionary. + + :param key_validator: Validator to apply to dictionary keys + :param value_validator: Validator to apply to dictionary values + :param mapping_validator: Validator to apply to top-level mapping + attribute (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepMapping(key_validator, value_validator, mapping_validator) + + +@attrs(repr=False, frozen=True, slots=True) +class _NumberValidator(object): + bound = attrib() + compare_op = attrib() + compare_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.compare_func(value, self.bound): + raise ValueError( + "'{name}' must be {op} {bound}: {value}".format( + name=attr.name, + op=self.compare_op, + bound=self.bound, + value=value, + ) + ) + + def __repr__(self): + return "".format( + op=self.compare_op, bound=self.bound + ) + + +def lt(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number larger or equal to *val*. + + :param val: Exclusive upper bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<", operator.lt) + + +def le(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number greater than *val*. + + :param val: Inclusive upper bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<=", operator.le) + + +def ge(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number smaller than *val*. + + :param val: Inclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">=", operator.ge) + + +def gt(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number smaller or equal to *val*. + + :param val: Exclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">", operator.gt) + + +@attrs(repr=False, frozen=True, slots=True) +class _MaxLengthValidator(object): + max_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) > self.max_length: + raise ValueError( + "Length of '{name}' must be <= {max}: {len}".format( + name=attr.name, max=self.max_length, len=len(value) + ) + ) + + def __repr__(self): + return "".format(max=self.max_length) + + +def max_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is longer than *length*. + + :param int length: Maximum length of the string or iterable + + .. versionadded:: 21.3.0 + """ + return _MaxLengthValidator(length) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.pyi new file mode 100644 index 0000000..5e00b85 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attr/validators.pyi @@ -0,0 +1,78 @@ +from typing import ( + Any, + AnyStr, + Callable, + Container, + ContextManager, + Iterable, + List, + Mapping, + Match, + Optional, + Pattern, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +from . import _ValidatorType + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +def set_disabled(run: bool) -> None: ... +def get_disabled() -> bool: ... +def disabled() -> ContextManager[None]: ... + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2]] +) -> _ValidatorType[Union[_T1, _T2]]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2], Type[_T3]] +) -> _ValidatorType[Union[_T1, _T2, _T3]]: ... +@overload +def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ... +def provides(interface: Any) -> _ValidatorType[Any]: ... +def optional( + validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]] +) -> _ValidatorType[Optional[_T]]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: Union[Pattern[AnyStr], AnyStr], + flags: int = ..., + func: Optional[ + Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]] + ] = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorType[_T], + iterable_validator: Optional[_ValidatorType[_I]] = ..., +) -> _ValidatorType[_I]: ... +def deep_mapping( + key_validator: _ValidatorType[_K], + value_validator: _ValidatorType[_V], + mapping_validator: Optional[_ValidatorType[_M]] = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... +def lt(val: _T) -> _ValidatorType[_T]: ... +def le(val: _T) -> _ValidatorType[_T]: ... +def ge(val: _T) -> _ValidatorType[_T]: ... +def gt(val: _T) -> _ValidatorType[_T]: ... +def max_len(length: int) -> _ValidatorType[_T]: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/AUTHORS.rst b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/AUTHORS.rst new file mode 100644 index 0000000..f14ef6c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/AUTHORS.rst @@ -0,0 +1,11 @@ +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/INSTALLER b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/LICENSE b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/LICENSE new file mode 100644 index 0000000..7ae3df9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack + +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/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/METADATA b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/METADATA new file mode 100644 index 0000000..aa327d5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/METADATA @@ -0,0 +1,232 @@ +Metadata-Version: 2.1 +Name: attrs +Version: 21.4.0 +Summary: Classes Without Boilerplate +Home-page: https://www.attrs.org/ +Author: Hynek Schlawack +Author-email: hs@ox.cx +Maintainer: Hynek Schlawack +Maintainer-email: hs@ox.cx +License: MIT +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues +Project-URL: Source Code, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Project-URL: Ko-fi, https://ko-fi.com/the_hynek +Keywords: class,attribute,boilerplate +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS.rst +Provides-Extra: dev +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'dev' +Requires-Dist: hypothesis ; extra == 'dev' +Requires-Dist: pympler ; extra == 'dev' +Requires-Dist: pytest (>=4.3.0) ; extra == 'dev' +Requires-Dist: six ; extra == 'dev' +Requires-Dist: mypy ; extra == 'dev' +Requires-Dist: pytest-mypy-plugins ; extra == 'dev' +Requires-Dist: zope.interface ; extra == 'dev' +Requires-Dist: furo ; extra == 'dev' +Requires-Dist: sphinx ; extra == 'dev' +Requires-Dist: sphinx-notfound-page ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Requires-Dist: cloudpickle ; (platform_python_implementation == "CPython") and extra == 'dev' +Provides-Extra: docs +Requires-Dist: furo ; extra == 'docs' +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: zope.interface ; extra == 'docs' +Requires-Dist: sphinx-notfound-page ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests' +Requires-Dist: hypothesis ; extra == 'tests' +Requires-Dist: pympler ; extra == 'tests' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests' +Requires-Dist: six ; extra == 'tests' +Requires-Dist: mypy ; extra == 'tests' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests' +Requires-Dist: zope.interface ; extra == 'tests' +Requires-Dist: cloudpickle ; (platform_python_implementation == "CPython") and extra == 'tests' +Provides-Extra: tests_no_zope +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests_no_zope' +Requires-Dist: hypothesis ; extra == 'tests_no_zope' +Requires-Dist: pympler ; extra == 'tests_no_zope' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests_no_zope' +Requires-Dist: six ; extra == 'tests_no_zope' +Requires-Dist: mypy ; extra == 'tests_no_zope' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests_no_zope' +Requires-Dist: cloudpickle ; (platform_python_implementation == "CPython") and extra == 'tests_no_zope' + + +.. image:: https://www.attrs.org/en/stable/_static/attrs_logo.png + :alt: attrs logo + :align: center + + +``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder methods `_). +`Trusted by NASA `_ for Mars missions since 2020! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + +.. teaser-end + +For that, it gives you a class decorator and a way to declaratively define the attributes on that class: + +.. -code-begin- + +.. code-block:: pycon + + >>> from attrs import asdict, define, make_class, Factory + + >>> @define + ... class SomeClass: + ... a_number: int = 42 + ... list_of_numbers: list[int] = Factory(list) + ... + ... def hard_math(self, another_number): + ... return self.a_number + sum(self.list_of_numbers) * another_number + + + >>> sc = SomeClass(1, [1, 2, 3]) + >>> sc + SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + + >>> sc.hard_math(3) + 19 + >>> sc == SomeClass(1, [1, 2, 3]) + True + >>> sc != SomeClass(2, [3, 2, 1]) + True + + >>> asdict(sc) + {'a_number': 1, 'list_of_numbers': [1, 2, 3]} + + >>> SomeClass() + SomeClass(a_number=42, list_of_numbers=[]) + + >>> C = make_class("C", ["a", "b"]) + >>> C("foo", "bar") + C(a='foo', b='bar') + + +After *declaring* your attributes ``attrs`` gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable ``__repr__``, +- a equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +**Hate type annotations**!? +No problem! +Types are entirely **optional** with ``attrs``. +Simply assign ``attrs.field()`` to the attributes instead of annotating them with types. + +---- + +This example uses ``attrs``'s modern APIs that have been introduced in version 20.1.0, and the ``attrs`` package import name that has been added in version 21.3.0. +The classic APIs (``@attr.s``, ``attr.ib``, plus their serious business aliases) and the ``attr`` package import name will remain **indefinitely**. + +Please check out `On The Core API Names `_ for a more in-depth explanation. + + +Data Classes +============ + +On the tin, ``attrs`` might remind you of ``dataclasses`` (and indeed, ``dataclasses`` are a descendant of ``attrs``). +In practice it does a lot more and is more flexible. +For instance it allows you to define `special handling of NumPy arrays for equality checks `_, or allows more ways to `plug into the initialization process `_. + +For more details, please refer to our `comparison page `_. + + +.. -getting-help- + +Getting Help +============ + +Please use the ``python-attrs`` tag on `Stack Overflow `_ to get help. + +Answering questions of your fellow developers is also a great way to help the project! + + +.. -project-information- + +Project Information +=================== + +``attrs`` is released under the `MIT `_ license, +its documentation lives at `Read the Docs `_, +the code on `GitHub `_, +and the latest release on `PyPI `_. +It’s rigorously tested on Python 2.7, 3.5+, and PyPy. + +We collect information on **third-party extensions** in our `wiki `_. +Feel free to browse and add your own! + +If you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide `_ to get you started! + + +``attrs`` for Enterprise +------------------------ + +Available as part of the Tidelift Subscription. + +The maintainers of ``attrs`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. +`Learn more. `_ + + +Release Information +=================== + +21.4.0 (2021-12-29) +------------------- + +Changes +^^^^^^^ + +- Fixed the test suite on PyPy3.8 where ``cloudpickle`` does not work. + `#892 `_ +- Fixed ``coverage report`` for projects that use ``attrs`` and don't set a ``--source``. + `#895 `_, + `#896 `_ + +`Full changelog `_. + +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? + + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/RECORD b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/RECORD new file mode 100644 index 0000000..3a40298 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/RECORD @@ -0,0 +1,56 @@ +attr/__init__.py,sha256=_zhJ4O8Q5KR5gaIrjX73vkR5nA6NjfpMGXQChEdNljI,1667 +attr/__init__.pyi,sha256=ubRkstoRHPpQN17iA0OCh8waIwZ5NeJgbz0lwI8XUjY,15100 +attr/__pycache__/__init__.cpython-39.pyc,, +attr/__pycache__/_cmp.cpython-39.pyc,, +attr/__pycache__/_compat.cpython-39.pyc,, +attr/__pycache__/_config.cpython-39.pyc,, +attr/__pycache__/_funcs.cpython-39.pyc,, +attr/__pycache__/_make.cpython-39.pyc,, +attr/__pycache__/_next_gen.cpython-39.pyc,, +attr/__pycache__/_version_info.cpython-39.pyc,, +attr/__pycache__/converters.cpython-39.pyc,, +attr/__pycache__/exceptions.cpython-39.pyc,, +attr/__pycache__/filters.cpython-39.pyc,, +attr/__pycache__/setters.cpython-39.pyc,, +attr/__pycache__/validators.cpython-39.pyc,, +attr/_cmp.py,sha256=JP0N7OIyTqIR3prUDfMZOR4DV4tlV_xXf39-bQg7xOo,4165 +attr/_cmp.pyi,sha256=oyjJVytrwwkUJOoe332IiYzp6pCVZEKKcKveH-ev604,317 +attr/_compat.py,sha256=i8u27AAK_4SzQnmTf3aliGV27UdYbJxdZ-O0tOHbLU8,8396 +attr/_config.py,sha256=aj1Lh8t2CuVa5nSxgCrLQtg_ZSdO8ZKeNJQd6RvpIp8,892 +attr/_funcs.py,sha256=sm_D12y2IyRW_bCnR7M-O7U5qHaieXr0BzINwJ7_K38,14753 +attr/_make.py,sha256=D05j0_ckcVIRFn2xHch5SPUCwh3t7WpeFj-3Ku9SocQ,102736 +attr/_next_gen.py,sha256=s5jCsVEQ4IhOjAykP4N0ETaWpg0RsgQttMvEZErUrhQ,5752 +attr/_version_info.py,sha256=sxD9yNai0jGbur_-RGEQHbgV2YX5_5G9PhrhBA5pA54,2194 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=uiiWTz8GLJe8I1Ty7UICK1DegVUnqHTXbOSnar7g7Nk,4078 +attr/converters.pyi,sha256=MQo7iEzPNVoFpKqD30sVwgVpdNoIeSCF2nsXvoxLZ-Y,416 +attr/exceptions.py,sha256=BMg7AljkJnvG-irMwL2TBHYlaLBXhSKnzoEWo4e42Zw,1981 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=JGZgvPGkdOfttkoL6XhXS6ZCoaVV5nZ8GCYeZNUN_mE,1124 +attr/filters.pyi,sha256=_Sm80jGySETX_Clzdkon5NHVjQWRl3Y3liQKZX1czXc,215 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=rH_UtQuHgQEC7hfZyMO_SJW0R1Gus7-a83U8igZfqs8,1466 +attr/setters.pyi,sha256=7dM10rqpQVDW0y-iJUnq8rabdO5Wx2Sbo5LwNa0IXl0,573 +attr/validators.py,sha256=jVE9roaSOmTf0dJNSLHNaQNilkrlzc3pNNBKmv0g7pk,15966 +attr/validators.pyi,sha256=adn6rNbIXmRXlg_FKrTmWj0dOX0vKTsGG82Jd3YcJbQ,2268 +attrs-21.4.0.dist-info/AUTHORS.rst,sha256=wsqCNbGz_mklcJrt54APIZHZpoTIJLkXqEhhn4Nd8hc,752 +attrs-21.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-21.4.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082 +attrs-21.4.0.dist-info/METADATA,sha256=WwgR4MfxE55PpGGv21UOEOEtXZGCqwekfXYg-JgA5HY,9810 +attrs-21.4.0.dist-info/RECORD,, +attrs-21.4.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110 +attrs-21.4.0.dist-info/top_level.txt,sha256=AGbmKnOtYpdkLRsDRQVSBIwfL32pAQ6BSo1mt-BxI7M,11 +attrs/__init__.py,sha256=CeyxLGVViAEKKsLOLaif8vF3vs1a28vsrRVLv7eMEgM,1109 +attrs/__init__.pyi,sha256=57aCxUJukK9lZlrUgk9RuWiBiPY5DzDKJAJkhbrStYw,1982 +attrs/__pycache__/__init__.cpython-39.pyc,, +attrs/__pycache__/converters.cpython-39.pyc,, +attrs/__pycache__/exceptions.cpython-39.pyc,, +attrs/__pycache__/filters.cpython-39.pyc,, +attrs/__pycache__/setters.cpython-39.pyc,, +attrs/__pycache__/validators.cpython-39.pyc,, +attrs/converters.py,sha256=fCBEdlYWcmI3sCnpUk2pz22GYtXzqTkp6NeOpdI64PY,70 +attrs/exceptions.py,sha256=SlDli6AY77f6ny-H7oy98OkQjsrw-D_supEuErIVYkE,70 +attrs/filters.py,sha256=dc_dNey29kH6KLU1mT2Dakq7tZ3kBfzEGwzOmDzw1F8,67 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=oKw51C72Hh45wTwYvDHJP9kbicxiMhMR4Y5GvdpKdHQ,67 +attrs/validators.py,sha256=4ag1SyVD2Hm3PYKiNG_NOtR_e7f81Hr6GiNl4YvXo4Q,70 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/WHEEL b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/WHEEL new file mode 100644 index 0000000..0b18a28 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/top_level.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..eca8ba9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs-21.4.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +attr +attrs diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.py new file mode 100644 index 0000000..a704b8b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + Factory, + __author__, + __copyright__, + __description__, + __doc__, + __email__, + __license__, + __title__, + __url__, + __version__, + __version_info__, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._next_gen import asdict, astuple + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "Attribute", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "Factory", + "field", + "fields_dict", + "fields", + "filters", + "frozen", + "has", + "make_class", + "mutable", + "NOTHING", + "resolve_types", + "setters", + "validate", + "validators", +] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.pyi new file mode 100644 index 0000000..7426fa5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__init__.pyi @@ -0,0 +1,63 @@ +from typing import ( + Any, + Callable, + Dict, + Mapping, + Optional, + Sequence, + Tuple, + Type, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import _FilterType +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import define as define +from attr import evolve as evolve +from attr import Factory as Factory +from attr import exceptions as exceptions +from attr import field as field +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import frozen as frozen +from attr import has as has +from attr import make_class as make_class +from attr import mutable as mutable +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators + +# TODO: see definition of attr.asdict/astuple +def asdict( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[ + Callable[[type, Attribute[Any], Any], Any] + ] = ..., + tuple_keys: bool = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4a4b486e24e62066c41ffaefd82cef6cdf8c6a9 GIT binary patch literal 1110 zcmb7@J8$GR5XZGo?Za!Y-;Z0zuR8==*eX&52!aF%@@Smma?LaZEsb}1#}O5hyYSw} zNtJvhwyEwb{wUvcljKh}{yo~!_$EH?_lP1X zS~{T?w5Sbj>OhCO(4`*qs1JQgA*BHfXb3|Z!HC8%rY+c_2~21kwrK};XbMx>gMeny3ye#^!lX?0fs!ir+A-(_kmmhz+{~-*&_1>z7mB)~NFLQ6~CX9rT zE59^Wh%gZ%H`T_f#nMM}M~DJlZdK)#DWfBe4wJ{lPzWTcbX<;w&??8$VdMA8YhK3K zm3M%$aTyZQd)WWpfOA=I!+_}ra~76YpGQsJ)~7p5#^_MORBgr$#8gW0e0Hcq$rRH0Rk zyV`hkUR2h+#oT9&d5h&b)9QiEr76F-`!aLN<83ALcXEL)W63TPLX|51B6rm$489^$ StTcYjwRC*_)oj|Hbv^<9-6W#` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/converters.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/converters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99d3e014ed5e31869584fdf1d54746c7ff4075fd GIT binary patch literal 247 zcmYk0F$=;l5QS5_h$#M!3T*}_LBv5EUDU-z&g7`oHi=0T1^iSN{~B)UeONfIsE;JwX+RtK~m zt-CNA)BPjQGoL%^xLAXNTq_BDq{Y5M<*C%jcaV~$cr<8>Z*5KHg=5($1D=qgW4g%04P`?2vI!BWL literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/exceptions.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c9cf44d46e825e812aa5a904a6c4294b4ddd5a1 GIT binary patch literal 247 zcmYk0vCct35QX>Bh!D?lCHGFDl87iosqt$xGIl3iuHCh}gyerAucTF}bu?xvILVxo zbEf&Q*{loJqWvqXCYPi=PLo*+LvT4ACD~9I z?a#th`Tw@ZKTJJ24|0VXmqsgup^^J7>A<5Q*kQ(z(b1ACKC?Xq@?mbBS(6O#sTt`5 pl*J;}mC>EN3%&@HS(F=Gl7~p);fs;m)1}ti8nrK}AA#&%Xd0PmWSK7*>( mv2Km-=L7Inpv+>i$C5lm1P>pE+}79D@L=Rg(J0dVN8Tq!e?;a0 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/setters.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/setters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46b0825bcf275ee79c017aca0a2acc47c8f34aa5 GIT binary patch literal 241 zcmYk0v2MaZ5Jb<=L+iHiD$Q^`{-@9IeTk_&w?t#neWAEi5mO1o!SRGJ)<&+jx@^pV-;ftbmM zgYsQ@KJ41UJje#+1`V#Q(FikJ?iw;Iqb2ymOd_MbBVRpd*D?iOI&ZgBGCXTW<{#>6 n73;>DVLpOTF=Yb8h)ijATm&khuQw2o` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/validators.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/__pycache__/validators.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a73f6ff06cb1a847ef34ce72098bcc6378a8abe3 GIT binary patch literal 247 zcmYk0F^d8*5QVdDBZqRoV};!*tOOB@!_r1=u90yw=xR2JNfZVDkblYcD(maCaiWDj znD=;Znh)FUFTt9qJB~R2V);~q%aG5v*rbq&5>p`?6OpA?UPP8$lFm3we!npUSL{($ z5`%I6EL_U}r`=94^W;2~E7Z6&S|a?Ia#xcMJQ{*6<{UX4EV=46+hZUf=GK`tl>uHg rC%p$%En;06-7R|Ht3bKMa)V3q5Gg#oF$#OUH2bxYJ4J&C^dG4o8q`N` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/converters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/converters.py new file mode 100644 index 0000000..edfa8d3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/exceptions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/exceptions.py new file mode 100644 index 0000000..bd9efed --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/filters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/filters.py new file mode 100644 index 0000000..5295900 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/py.typed b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/setters.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/setters.py new file mode 100644 index 0000000..9b50770 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/validators.py b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/validators.py new file mode 100644 index 0000000..ab2c9b3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/easy_install.py b/pytest_project/pytest-env/lib/python3.9/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/INSTALLER b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/LICENSE b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/LICENSE new file mode 100644 index 0000000..31ecdfb --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/LICENSE @@ -0,0 +1,19 @@ + + 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/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/METADATA b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/METADATA new file mode 100644 index 0000000..c078a75 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/METADATA @@ -0,0 +1,78 @@ +Metadata-Version: 2.1 +Name: iniconfig +Version: 1.1.1 +Summary: iniconfig: brain-dead simple config-ini parsing +Home-page: http://github.com/RonnyPfannschmidt/iniconfig +Author: Ronny Pfannschmidt, Holger Krekel +Author-email: opensource@ronnypfannschmidt.de, holger.krekel@gmail.com +License: MIT License +Platform: unix +Platform: linux +Platform: osx +Platform: cygwin +Platform: win32 +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 + +iniconfig: brain-dead simple parsing of ini files +======================================================= + +iniconfig is a small and simple INI-file parser module +having a unique set of features: + +* tested against Python2.4 across to Python3.2, Jython, PyPy +* maintains order of sections and entries +* supports multi-line values with or without line-continuations +* supports "#" comments everywhere +* raises errors with proper line-numbers +* no bells and whistles like automatic substitutions +* iniconfig raises an Error if two sections have the same name. + +If you encounter issues or have feature wishes please report them to: + + http://github.com/RonnyPfannschmidt/iniconfig/issues + +Basic Example +=================================== + +If you have an ini file like this:: + + # content of example.ini + [section1] # comment + name1=value1 # comment + name1b=value1,value2 # comment + + [section2] + name2= + line1 + line2 + +then you can do:: + + >>> import iniconfig + >>> ini = iniconfig.IniConfig("example.ini") + >>> ini['section1']['name1'] # raises KeyError if not exists + 'value1' + >>> ini.get('section1', 'name1b', [], lambda x: x.split(",")) + ['value1', 'value2'] + >>> ini.get('section1', 'notexist', [], lambda x: x.split(",")) + [] + >>> [x.name for x in list(ini)] + ['section1', 'section2'] + >>> list(list(ini)[0].items()) + [('name1', 'value1'), ('name1b', 'value1,value2')] + >>> 'section1' in ini + True + >>> 'inexistendsection' in ini + False + + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/RECORD b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/RECORD new file mode 100644 index 0000000..1f9239e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/RECORD @@ -0,0 +1,10 @@ +iniconfig-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +iniconfig-1.1.1.dist-info/LICENSE,sha256=KvaAw570k_uCgwNW0dPfGstaBgM8ui3sehniHKp3qGY,1061 +iniconfig-1.1.1.dist-info/METADATA,sha256=_4-oFKpRXuZv5rzepScpXRwhq6DzqsgbnA5ZpgMUMcs,2405 +iniconfig-1.1.1.dist-info/RECORD,, +iniconfig-1.1.1.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110 +iniconfig-1.1.1.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10 +iniconfig/__init__.py,sha256=-pBe5AF_6aAwo1CxJQ8i_zJq6ejc6IxHta7qk2tNJhY,5208 +iniconfig/__init__.pyi,sha256=-4KOctzq28ohRmTZsqlH6aylyFqsNKxYqtk1dteypi4,1205 +iniconfig/__pycache__/__init__.cpython-39.pyc,, +iniconfig/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/WHEEL b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/WHEEL new file mode 100644 index 0000000..6d38aa0 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/top_level.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..9dda536 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig-1.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +iniconfig diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.py new file mode 100644 index 0000000..6ad9eaf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.py @@ -0,0 +1,165 @@ +""" brain-dead simple parser for ini-style files. +(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed +""" +__all__ = ['IniConfig', 'ParseError'] + +COMMENTCHARS = "#;" + + +class ParseError(Exception): + def __init__(self, path, lineno, msg): + Exception.__init__(self, path, lineno, msg) + self.path = path + self.lineno = lineno + self.msg = msg + + def __str__(self): + return "%s:%s: %s" % (self.path, self.lineno+1, self.msg) + + +class SectionWrapper(object): + def __init__(self, config, name): + self.config = config + self.name = name + + def lineof(self, name): + return self.config.lineof(self.name, name) + + def get(self, key, default=None, convert=str): + return self.config.get(self.name, key, + convert=convert, default=default) + + def __getitem__(self, key): + return self.config.sections[self.name][key] + + def __iter__(self): + section = self.config.sections.get(self.name, []) + + def lineof(key): + return self.config.lineof(self.name, key) + for name in sorted(section, key=lineof): + yield name + + def items(self): + for name in self: + yield name, self[name] + + +class IniConfig(object): + def __init__(self, path, data=None): + self.path = str(path) # convenience + if data is None: + f = open(self.path) + try: + tokens = self._parse(iter(f)) + finally: + f.close() + else: + tokens = self._parse(data.splitlines(True)) + + self._sources = {} + self.sections = {} + + for lineno, section, name, value in tokens: + if section is None: + self._raise(lineno, 'no section header defined') + self._sources[section, name] = lineno + if name is None: + if section in self.sections: + self._raise(lineno, 'duplicate section %r' % (section, )) + self.sections[section] = {} + else: + if name in self.sections[section]: + self._raise(lineno, 'duplicate name %r' % (name, )) + self.sections[section][name] = value + + def _raise(self, lineno, msg): + raise ParseError(self.path, lineno, msg) + + def _parse(self, line_iter): + result = [] + section = None + for lineno, line in enumerate(line_iter): + name, data = self._parseline(line, lineno) + # new value + if name is not None and data is not None: + result.append((lineno, section, name, data)) + # new section + elif name is not None and data is None: + if not name: + self._raise(lineno, 'empty section name') + section = name + result.append((lineno, section, None, None)) + # continuation + elif name is None and data is not None: + if not result: + self._raise(lineno, 'unexpected value continuation') + last = result.pop() + last_name, last_data = last[-2:] + if last_name is None: + self._raise(lineno, 'unexpected value continuation') + + if last_data: + data = '%s\n%s' % (last_data, data) + result.append(last[:-1] + (data,)) + return result + + def _parseline(self, line, lineno): + # blank lines + if iscommentline(line): + line = "" + else: + line = line.rstrip() + if not line: + return None, None + # section + if line[0] == '[': + realline = line + for c in COMMENTCHARS: + line = line.split(c)[0].rstrip() + if line[-1] == "]": + return line[1:-1], None + return None, realline.strip() + # value + elif not line[0].isspace(): + try: + name, value = line.split('=', 1) + if ":" in name: + raise ValueError() + except ValueError: + try: + name, value = line.split(":", 1) + except ValueError: + self._raise(lineno, 'unexpected line: %r' % line) + return name.strip(), value.strip() + # continuation + else: + return None, line.strip() + + def lineof(self, section, name=None): + lineno = self._sources.get((section, name)) + if lineno is not None: + return lineno + 1 + + def get(self, section, name, default=None, convert=str): + try: + return convert(self.sections[section][name]) + except KeyError: + return default + + def __getitem__(self, name): + if name not in self.sections: + raise KeyError(name) + return SectionWrapper(self, name) + + def __iter__(self): + for name in sorted(self.sections, key=self.lineof): + yield SectionWrapper(self, name) + + def __contains__(self, arg): + return arg in self.sections + + +def iscommentline(line): + c = line.lstrip()[:1] + return c in COMMENTCHARS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.pyi b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.pyi new file mode 100644 index 0000000..b6284be --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__init__.pyi @@ -0,0 +1,31 @@ +from typing import Callable, Iterator, Mapping, Optional, Tuple, TypeVar, Union +from typing_extensions import Final + +_D = TypeVar('_D') +_T = TypeVar('_T') + +class ParseError(Exception): + # Private __init__. + path: Final[str] + lineno: Final[int] + msg: Final[str] + +class SectionWrapper: + # Private __init__. + config: Final[IniConfig] + name: Final[str] + def __getitem__(self, key: str) -> str: ... + def __iter__(self) -> Iterator[str]: ... + def get(self, key: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def items(self) -> Iterator[Tuple[str, str]]: ... + def lineof(self, name: str) -> Optional[int]: ... + +class IniConfig: + path: Final[str] + sections: Final[Mapping[str, Mapping[str, str]]] + def __init__(self, path: str, data: Optional[str] = None): ... + def __contains__(self, arg: str) -> bool: ... + def __getitem__(self, name: str) -> SectionWrapper: ... + def __iter__(self) -> Iterator[SectionWrapper]: ... + def get(self, section: str, name: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def lineof(self, section: str, name: Optional[str] = ...) -> Optional[int]: ... diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22abb9ed1328e6dd8c095e74cb7ce040edec1bc5 GIT binary patch literal 5271 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 We^_~w#Q1FB*q-IswVB%Kn)x5gPB9e# literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/py.typed b/pytest_project/pytest-env/lib/python3.9/site-packages/iniconfig/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/INSTALLER b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE new file mode 100644 index 0000000..6f62d44 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.APACHE b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.BSD b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.BSD new file mode 100644 index 0000000..42ce7b7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +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. + +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/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/METADATA b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/METADATA new file mode 100644 index 0000000..358ace5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/METADATA @@ -0,0 +1,453 @@ +Metadata-Version: 2.1 +Name: packaging +Version: 21.3 +Summary: Core utilities for Python packages +Home-page: https://github.com/pypa/packaging +Author: Donald Stufft and individual contributors +Author-email: donald@stufft.io +License: BSD-2-Clause or Apache-2.0 +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: OSI Approved :: BSD License +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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Requires-Dist: pyparsing (!=3.0.5,>=2.0.2) + +packaging +========= + +.. start-intro + +Reusable core utilities for various Python Packaging +`interoperability specifications `_. + +This library provides utilities that implement the interoperability +specifications which have clearly one correct behaviour (eg: :pep:`440`) +or benefit greatly from having a single shared implementation (eg: :pep:`425`). + +.. end-intro + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Requirements +- Tags +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join ``#pypa`` on Freenode to ask questions or get involved. + + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + +Changelog +--------- + +21.3 - 2021-11-17 +~~~~~~~~~~~~~~~~~ + +* Add a ``pp3-none-any`` tag (`#311 `__) +* Replace the blank pyparsing 3 exclusion with a 3.0.5 exclusion (`#481 `__, `#486 `__) +* Fix a spelling mistake (`#479 `__) + +21.2 - 2021-10-29 +~~~~~~~~~~~~~~~~~ + +* Update documentation entry for 21.1. + +21.1 - 2021-10-29 +~~~~~~~~~~~~~~~~~ + +* Update pin to pyparsing to exclude 3.0.0. + +21.0 - 2021-07-03 +~~~~~~~~~~~~~~~~~ + +* PEP 656: musllinux support (`#411 `__) +* Drop support for Python 2.7, Python 3.4 and Python 3.5. +* Replace distutils usage with sysconfig (`#396 `__) +* Add support for zip files in ``parse_sdist_filename`` (`#429 `__) +* Use cached ``_hash`` attribute to short-circuit tag equality comparisons (`#417 `__) +* Specify the default value for the ``specifier`` argument to ``SpecifierSet`` (`#437 `__) +* Proper keyword-only "warn" argument in packaging.tags (`#403 `__) +* Correctly remove prerelease suffixes from ~= check (`#366 `__) +* Fix type hints for ``Version.post`` and ``Version.dev`` (`#393 `__) +* Use typing alias ``UnparsedVersion`` (`#398 `__) +* Improve type inference for ``packaging.specifiers.filter()`` (`#430 `__) +* Tighten the return type of ``canonicalize_version()`` (`#402 `__) + +20.9 - 2021-01-29 +~~~~~~~~~~~~~~~~~ + +* Run `isort `_ over the code base (`#377 `__) +* Add support for the ``macosx_10_*_universal2`` platform tags (`#379 `__) +* Introduce ``packaging.utils.parse_wheel_filename()`` and ``parse_sdist_filename()`` + (`#387 `__ and `#389 `__) + +20.8 - 2020-12-11 +~~~~~~~~~~~~~~~~~ + +* Revert back to setuptools for compatibility purposes for some Linux distros (`#363 `__) +* Do not insert an underscore in wheel tags when the interpreter version number + is more than 2 digits (`#372 `__) + +20.7 - 2020-11-28 +~~~~~~~~~~~~~~~~~ + +No unreleased changes. + +20.6 - 2020-11-28 +~~~~~~~~~~~~~~~~~ + +.. note:: This release was subsequently yanked, and these changes were included in 20.7. + +* Fix flit configuration, to include LICENSE files (`#357 `__) +* Make `intel` a recognized CPU architecture for the `universal` macOS platform tag (`#361 `__) +* Add some missing type hints to `packaging.requirements` (issue:`350`) + +20.5 - 2020-11-27 +~~~~~~~~~~~~~~~~~ + +* Officially support Python 3.9 (`#343 `__) +* Deprecate the ``LegacyVersion`` and ``LegacySpecifier`` classes (`#321 `__) +* Handle ``OSError`` on non-dynamic executables when attempting to resolve + the glibc version string. + +20.4 - 2020-05-19 +~~~~~~~~~~~~~~~~~ + +* Canonicalize version before comparing specifiers. (`#282 `__) +* Change type hint for ``canonicalize_name`` to return + ``packaging.utils.NormalizedName``. + This enables the use of static typing tools (like mypy) to detect mixing of + normalized and un-normalized names. + +20.3 - 2020-03-05 +~~~~~~~~~~~~~~~~~ + +* Fix changelog for 20.2. + +20.2 - 2020-03-05 +~~~~~~~~~~~~~~~~~ + +* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, + aarch64), to report the wrong bitness. + +20.1 - 2020-01-24 +~~~~~~~~~~~~~~~~~~~ + +* Fix a bug caused by reuse of an exhausted iterator. (`#257 `__) + +20.0 - 2020-01-06 +~~~~~~~~~~~~~~~~~ + +* Add type hints (`#191 `__) + +* Add proper trove classifiers for PyPy support (`#198 `__) + +* Scale back depending on ``ctypes`` for manylinux support detection (`#171 `__) + +* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 `__) + +* Expand upon the API provided by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 `__) + +* Officially support Python 3.8 (`#232 `__) + +* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 `__) + +* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 `__) + +19.2 - 2019-09-18 +~~~~~~~~~~~~~~~~~ + +* Remove dependency on ``attrs`` (`#178 `__, `#179 `__) + +* Use appropriate fallbacks for CPython ABI tag (`#181 `__, `#185 `__) + +* Add manylinux2014 support (`#186 `__) + +* Improve ABI detection (`#181 `__) + +* Properly handle debug wheels for Python 3.8 (`#172 `__) + +* Improve detection of debug builds on Windows (`#194 `__) + +19.1 - 2019-07-30 +~~~~~~~~~~~~~~~~~ + +* Add the ``packaging.tags`` module. (`#156 `__) + +* Correctly handle two-digit versions in ``python_version`` (`#119 `__) + + +19.0 - 2019-01-20 +~~~~~~~~~~~~~~~~~ + +* Fix string representation of PEP 508 direct URL requirements with markers. + +* Better handling of file URLs + + This allows for using ``file:///absolute/path``, which was previously + prevented due to the missing ``netloc``. + + This allows for all file URLs that ``urlunparse`` turns back into the + original URL to be valid. + + +18.0 - 2018-09-26 +~~~~~~~~~~~~~~~~~ + +* Improve error messages when invalid requirements are given. (`#129 `__) + + +17.1 - 2017-02-28 +~~~~~~~~~~~~~~~~~ + +* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions. + + +17.0 - 2017-02-28 +~~~~~~~~~~~~~~~~~ + +* Drop support for python 2.6, 3.2, and 3.3. + +* Define minimal pyparsing version to 2.0.2 (`#91 `__). + +* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to + ``Version`` and ``LegacyVersion`` (`#34 `__). + +* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to + make it easy to determine if a release is a development release. + +* Add ``utils.canonicalize_version`` to canonicalize version strings or + ``Version`` instances (`#121 `__). + + +16.8 - 2016-10-29 +~~~~~~~~~~~~~~~~~ + +* Fix markers that utilize ``in`` so that they render correctly. + +* Fix an erroneous test on Python RC releases. + + +16.7 - 2016-04-23 +~~~~~~~~~~~~~~~~~ + +* Add support for the deprecated ``python_implementation`` marker which was + an undocumented setuptools marker in addition to the newer markers. + + +16.6 - 2016-03-29 +~~~~~~~~~~~~~~~~~ + +* Add support for the deprecated, PEP 345 environment markers in addition to + the newer markers. + + +16.5 - 2016-02-26 +~~~~~~~~~~~~~~~~~ + +* Fix a regression in parsing requirements with whitespaces between the comma + separators. + + +16.4 - 2016-02-22 +~~~~~~~~~~~~~~~~~ + +* Fix a regression in parsing requirements like ``foo (==4)``. + + +16.3 - 2016-02-21 +~~~~~~~~~~~~~~~~~ + +* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when + matching legacy requirements. + + +16.2 - 2016-02-09 +~~~~~~~~~~~~~~~~~ + +* Add a function that implements the name canonicalization from PEP 503. + + +16.1 - 2016-02-07 +~~~~~~~~~~~~~~~~~ + +* Implement requirement specifiers from PEP 508. + + +16.0 - 2016-01-19 +~~~~~~~~~~~~~~~~~ + +* Relicense so that packaging is available under *either* the Apache License, + Version 2.0 or a 2 Clause BSD license. + +* Support installation of packaging when only distutils is available. + +* Fix ``==`` comparison when there is a prefix and a local version in play. + (`#41 `__). + +* Implement environment markers from PEP 508. + + +15.3 - 2015-08-01 +~~~~~~~~~~~~~~~~~ + +* Normalize post-release spellings for rev/r prefixes. `#35 `__ + + +15.2 - 2015-05-13 +~~~~~~~~~~~~~~~~~ + +* Fix an error where the arbitrary specifier (``===``) was not correctly + allowing pre-releases when it was being used. + +* Expose the specifier and version parts through properties on the + ``Specifier`` classes. + +* Allow iterating over the ``SpecifierSet`` to get access to all of the + ``Specifier`` instances. + +* Allow testing if a version is contained within a specifier via the ``in`` + operator. + + +15.1 - 2015-04-13 +~~~~~~~~~~~~~~~~~ + +* Fix a logic error that was causing inconsistent answers about whether or not + a pre-release was contained within a ``SpecifierSet`` or not. + + +15.0 - 2015-01-02 +~~~~~~~~~~~~~~~~~ + +* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to + make it easy to determine if a release is a post release. + +* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make + it easy to get the public version without any pre or post release markers. + +* Support the update to PEP 440 which removed the implied ``!=V.*`` when using + either ``>V`` or ``V`` or ````) operator. + + +14.3 - 2014-11-19 +~~~~~~~~~~~~~~~~~ + +* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely + handle legacy specifiers as well as PEP 440 specifiers. + +* **BACKWARDS INCOMPATIBLE** Move the specifier support out of + ``packaging.version`` into ``packaging.specifiers``. + + +14.2 - 2014-09-10 +~~~~~~~~~~~~~~~~~ + +* Add prerelease support to ``Specifier``. +* Remove the ability to do ``item in Specifier()`` and replace it with + ``Specifier().contains(item)`` in order to allow flags that signal if a + prerelease should be accepted or not. +* Add a method ``Specifier().filter()`` which will take an iterable and returns + an iterable with items that do not match the specifier filtered out. + + +14.1 - 2014-09-08 +~~~~~~~~~~~~~~~~~ + +* Allow ``LegacyVersion`` and ``Version`` to be sorted together. +* Add ``packaging.version.parse()`` to enable easily parsing a version string + as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440 + validity. + + +14.0 - 2014-09-05 +~~~~~~~~~~~~~~~~~ + +* Initial release. + + +.. _`master`: https://github.com/pypa/packaging/ + + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/RECORD b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/RECORD new file mode 100644 index 0000000..df898c0 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/RECORD @@ -0,0 +1,31 @@ +packaging-21.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +packaging-21.3.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-21.3.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-21.3.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging-21.3.dist-info/METADATA,sha256=KuKIy6qDLP3svIt6ejCbxBDhvq11ebkgUN55MeyKFyc,15147 +packaging-21.3.dist-info/RECORD,, +packaging-21.3.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +packaging-21.3.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10 +packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661 +packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497 +packaging/__pycache__/__about__.cpython-39.pyc,, +packaging/__pycache__/__init__.cpython-39.pyc,, +packaging/__pycache__/_manylinux.cpython-39.pyc,, +packaging/__pycache__/_musllinux.cpython-39.pyc,, +packaging/__pycache__/_structures.cpython-39.pyc,, +packaging/__pycache__/markers.cpython-39.pyc,, +packaging/__pycache__/requirements.cpython-39.pyc,, +packaging/__pycache__/specifiers.cpython-39.pyc,, +packaging/__pycache__/tags.cpython-39.pyc,, +packaging/__pycache__/utils.cpython-39.pyc,, +packaging/__pycache__/version.cpython-39.pyc,, +packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488 +packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378 +packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +packaging/markers.py,sha256=Fygi3_eZnjQ-3VJizW5AhI5wvo0Hb6RMk4DidsKpOC0,8475 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/requirements.py,sha256=rjaGRCMepZS1mlYMjJ5Qh6rfq3gtsCRQUQmftGZ_bu8,4664 +packaging/specifiers.py,sha256=LRQ0kFsHrl5qfcFNEEJrIFYsnIHQUJXY9fIsakTrrqE,30110 +packaging/tags.py,sha256=lmsnGNiJ8C4D_Pf9PbM0qgbZvD9kmB9lpZBQUZa3R_Y,15699 +packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 +packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/WHEEL b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/top_level.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/top_level.txt new file mode 100644 index 0000000..748809f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging-21.3.dist-info/top_level.txt @@ -0,0 +1 @@ +packaging diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__about__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__about__.py new file mode 100644 index 0000000..3551bc2 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__about__.py @@ -0,0 +1,26 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "21.3" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__init__.py new file mode 100644 index 0000000..3c50c5d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__init__.py @@ -0,0 +1,25 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from .__about__ import ( + __author__, + __copyright__, + __email__, + __license__, + __summary__, + __title__, + __uri__, + __version__, +) + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/__about__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/__about__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54382028a0da9fde73cd0787949e716b9532bc3f GIT binary patch literal 614 zcmYk2&5qMB6osAsrY)6b#e$VVV$nt7PKV7Pga)Q-gjCpMW4W=L7#utJry|+RQ_Ksn z4LNz4S^@dRkEGLentYftXSntUOnb z!a8X*h&iP!_}W-F!%fjJsm>lHOQBtCv!t{%ZD(aw`)F%f`IS{tSN)4yV+9UgD(R(g z_+Tvl*g-pu&nDse=e75(dre3seI53BX&TaYEhCR@Mw`q0s$Xr5W{TsT4~K)tOmi$X zmq*D1Q@AwRTe%P3Sl3_h2kVV{u;$YA&))C0*(SSHEI5H-jCat!7TG3W_fIzK%U2oT z8ozWORYw=ZN2T$x`D-$CtEcl3o zdXk^-ou1yi6E29W0{__xn8h^ z&RMPtRwSe)&Fn2<%O<@eW%e0;Nm(3L0DK6YbpVGAKyCZc$+ioTE&+t*QpI)+pkytK z7ZdS=*4&MgD9E62o&yX{PJ+E~URncC7A}uTZh=rBED`Vmmm$m%(g?TZ3M2W>4l#XF z;QUDTXYyzF?}Vvb#U0;?J3%$V_bYwyaRXX+-0;?#hSM8c4_cTIhgQ&MUd68xFTHSb z8uw!mK0xp6qo@O&XeG>^D!D`2S#!3%pk4-1^}K%Kt?+beq-iODG%{|!?Z-oLsckbT MafR1;Joa?`3!6@WmjD0& literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_manylinux.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_manylinux.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e105c00ce49c57e23b0fc72290234c37fc857c3d GIT binary patch literal 7318 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 DxVN?R literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_structures.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/_structures.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4efde04b71dc1574c7c63ff68c07a1267ecd1bef GIT binary patch literal 2827 zcmc&$OK;Oa5MDp>Y?7vJT0x<_A1Wd>5)u*;LP#JGmt0ydy{ugCHgQSqbk?b8)e9;I z{sEW%f&3+3`4@0uX5E;yi9-Yjw(@jmAM?%3&U~#~t(F;%t?p0vTaK~sq>K&=l~?$w z$0*)0!G!Szzf*IYNp{M%4O}yYg=-eBnbO2HOW2!iJ?G(#3nR7l6_+L66%21!!Z1M%YrP5{Fx~VqKJAa)k~s`dO6kSLXlTV7j@LDvLF`3 zBHCLL4b*F58U0*|EAN9{Kk%cYt)reC7;zqC)Q7+`M!csveXsFTHVR?jU9%MzhnEKo zui8p>cL5X0wD|Ga`Jf~y$LqL(+YSTaI&VU+zb}Ib7enbBxJ~@kl%IMM{A9Uz6iF5F z9)x?+i=0Gl%HYuH`a7g{!redPx#asyAs zB{a}=mBPo$@IfAMzBI)L+8^^_Qf9Z3L}tLWF=Z=LU}*o(FmM%3Ovk~L(V?uAwsAjh zv}D_j{6jhVuEUzpr9=S^2hA@89I^9cuN!gwvZ;wRUEn;BZO*Twsi9|$QE==geW0~I zPYn>b`{ZYt^ZiitsZ8Pu=Ldbad%j~~yFv-A#g-pLP@`Es+@ao&?VT{}0=+3TNL(SY zO5z%cno<1Wwbkq_jF40(Qa$q)~uEUrYYkFnVi6laq&e$#4j~L LlBlibt=j4@dhHY< literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/markers.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/markers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97a85bd50986821c961e11f0e3e7a9c6c1dc10ac GIT binary patch literal 9465 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 z zZ56Z|>T7J7g)lWj2vK<)Vv}w{e*|*_*eATR>DDO~n%Ly>JA{y>II7XL{0PuWCnJoauY-4P?1Ga=ly4VENBZlggUhB$3I>+Ai51w2Z{1HnkWy{0SgymQ)bnKnlPYT zLEVtUc{PT_{hzgsTgI}voLRO|XNDH)!Z3@-fVpU|u;pyqTC|sKub;}xS=E9XwAh`Q zbM0(9)3#TPc4n95;w1 zJ_)ccPR>C9SBO3lriB=eKev#X{0d|K6)7MZ)WFC#M1Ec$g6Pj9FX%S2KzI8XzL2Z_ zt=;5Bq^=C&I`clvACvO!CLiIw!*XX)fXD>?A^Iq=Lc|Ew{;45HKsXS7uunjqiw3<8 zysDuOU9vgdI`sXqk%qhI%jFUJs(GESs`Qu1UP8P(_6p3%iVcIPkoZ=0aZl7^WogCE zJ=pILu$L&CI!@-NHIHvWPBf4h7~P)RFEC7nz{qs7fRrI!{s?ukCyS5HkHk|ENiiQ8 z5#-M-GCJFG{AJSlU;t+p!9J_mH4V^KFsGoQU|zwVHNsp0J$ms4OYSS!a%&J>ixxK? z_9!gLdsyWgS{LN@Ixi&_^K93aK1V|OnwvegO6*A4Bl>(@7$kgJ! zc$=|>Aq;xup?4;pf5_BZWu}~0Dy%5USw#dj=E7qrY!br60X;~H4sPS4HJEs|IvETI zq>{<-E=&gV{w-ZwX1dlI3p0r6kP?DX&}NIUzv-U$YIN&gvqvT=rVn9SaP=^)nr4H-10to#HS-cu&8y+3s=>QSBM;>R94 zm1(koKI%OHmYrV(13gO>f=23M3+Jeq;UHaGAKJ-E7=%p;)us18F`u+{T~+TYoyhNR zhyvv?1$h~p;=al(m4A+=9m7GV&~S)0K-_?EP_eV*&M{xQBy~Y_%p{z~R5;C;Zso?~ z(I-#H(`b$lyHgN~Vxvyisv8H5qr0FH65Bf)CY>!DN0(4r!OgbeyDjN*ACw@dfs|w{ z9TQ`axyP~CRBVF?$4#z!B(L5d@Ra3TQ?Yz{l5lSOK6uk}=B3~0*e%|IYFnt?6JGk- zzG7Ynq? z%xqLhK5tH5xiJ-47iMOzpf=yBh15&FFJGU!8rk&thbD$?9vhHH%;0yZt z0fDawDAR%{S6%&1G=Z>Y{J=axml^ycKpx~s|Ksd*p^w>ipV4RGpDh;qGWc82oN)Lp zIQ-VHm}&hV%UHi;mbK2Z)-RZC{hZ~j|7CgWXROEiDJxk2!+Ncsu%h+fY?t+4tk3#S zw%hs-_Jr*rO`QDKExTw;JYgmTJ?o>yY?T*aHChn=gREdUw)TYShjsSr!eP?h=Enc$ ULxsUzMbk14^c)`UvxoZsAG+{4hyVZp literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/requirements.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/requirements.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e71398c4791de6931b072f8d9692b39f60df905a GIT binary patch literal 3986 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 RQ`0b^hxz)Ho;5PYe*wI_{ObS! literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/specifiers.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/specifiers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d923e4f0a7923ca5df8bc78c8a37307f16068a3 GIT binary patch literal 21545 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/tags.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/tags.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d20577c6c992d7d433587c3bc4cc0e0bf44a136 GIT binary patch literal 12276 zcmbtaO>i8?b)LWd#bN=1G(?IrH6$fbD-$41&@?5}v?vmkU{gy(AZ2MIdpOu001NES zEP4hY0q#1LfR1Ci=u#Y4;?paDvB!$Oiy>uo7dg1-+zxgI9Sy1yVm@fJM(Ex`zba0e|a=s#1+10Xxep6XhLsl zHFei(I=79Q!FRJ};%+ppR;HHG3C3(@Te(`Um9OPng<7FitQA`WwSiWtR%#8_23zG? zxiwT9Y7N(h0dI*+b5CoeHqzQ#+pBAyc7LDukxjie>J5629nfm~y|VY1_t>0)`T>y@ zIgu9yQ4|CBGPTFOoOjSWNYA_{MCqL78H*nkgG+kt(94=Ai=jK37}_vuPXa3MJ$XPA z!}koaM~vLDYPR?+dJlU;z^hB|b+z-hg)y?|l^S9THFCy{E+$u~*plbg$&?$E-?TfgrZk4tqtO%i((&aYP)w zldC<09>Vj~JzYF4o&o-2;?*7=Jo#Vo90Q(bMFn`C#hetT=P=WciQ~Xj5!K#YwqsJW z7`tbQad853f6hBJr!!3sij!E;$Hgi1e#|?7KDEvX^qv-<0M_H;DzF~lnGses>KQ!$ zq&SPW#uyi^l-Bh;X7hsh6#B>eR(ei+8kkP>^_&-D}HPM4ot0Kr(#1tS;3w=hbOg|w0u2%GTWb%z`lhv82t5s*_ z`sK@0Z%ocqim`R6QIF!nWf`n{{)`vJ`Kid0?rhUbYEd9#>q;ZUldJ8h5%_L1&d+$u zE1qBX;>@*`Hb67ieLRV+x*J9hbPQNA;-b@X{k3MpU%5r~m9VMmMtdzb>h0KQ<7(hq zT&rZ_YLE0>YEF*H*5D4BK zYeTQ)dp!_C?kV(oN@9?EieQ9wU7dVb^*?d@RBjut#Sa_8+xHb8oNE|9is-(2v#=6I1`n)1WQ z#pZ;zD?Pg&_+dkM(i3*0)mn*YYi-vTz_X$t7Tj=wddKa_&Q^`16df z0kCEm*x^dM9mvQN4)nvB1eWhX zW%2fk^y?OOCjC*Ju8X+BvnV?HeQi_gXpY`7lA6&mlbRV>9c@v^W#BUB%nj6?jL>GZ zclBx|8)urqO;5@vfJlCnibE)3i)JUEL=`i%La#X|ZF+c^iX$lA)#S6dy}y3qdg#gU zM18^a-TA;5?uko5eWit6L1*BdSa!#78}n|pJ=sv*;8`z>oVE-Wy?S&)HOD-E^+dBV zOHDBQ#Q5_k!baqcwcYxXJMV=jR0V`TfgKLVApm&?G;TzWa~d-V$v$a%k^ZvlCGg33 z`uJU49>udd319l7I$cb~EGnQ5Xd<6NwW2dQ@Y*r7c%7=KP}4^78C)UEpr#q?Lo`(1 z9{wuBFu{)l&CWrH&OEhbkQ@yH-uP9J(?-fiA*cO&8CSXjCRWOG&`y~EawHYacznRp zkLo5ap4fR*BJf_MZx~@YGG>T^W=CH%myF;X>K5u&QqQ2CN$OeDvrB+iw9Y|~g^ffk z5NHv^q#RNvRfD&`IWedH{1juS@Gao+5k#DH2U_kZvlM)3xXj5MngPuaFBrXvE#heH+mw5>Op0M=;XUC&v9V&oK_%Knp7`24k_2< z305J3I=Ty3PBVx?R87oZJ_p3mjWU2HiPm=Lqc|JPk~WF0*&t}jlLTu*D&@yflM_^Y zk_u8bkakZh3s!hCh!pGNaBxg?#+k4UEV1bdq0~U3H^)j|zC^$l zmKDaOK4lxm7PMj*7gF^VZt=xdr?X?n@q}pR03GdASVp1cvih)I=D+>Ad=)*HD`spF zSH{*&SNgjZq6wg$DSifx2)o_UHubL2K_IjOQ`ynwk;u4jl5}kvy4Hc>gG{fVzZm#) zjd^?3l?`?~jy)Y7BOKF`m1mjkNisz0^MTK28xx7tOixbR6K7A`H{H;lYse6C zvt`Jzed)eVjGs!RV36nkfLQ4P&5smJ)AQp@M9~9CQbXrXk_?hNIpMf%h>RC@0ino| zl@6gW<6?WQM+(~a-MmY?+tXM7frg?-SDV+m2GN58`7a}g-q5?)I!o(XxH8>rWC)n~ z4WpZj;9Lp|wYo*$3-T?Ih1SzVZb_3r#JhQZx7aC$>zyJ) zen}Ld2{lm!#Cw1kV2FXvfG8~);SX>pNqR4`7PAQB4f&oZceDP!jwRoNHqD8ljtN@{ zol4SmKQD&Gp2b4P>{z5b#mHT&CwF_RlB`nsd#@yA-i5;AQ+e;2)4~j|V?Lb?p^r`ps7_T)A@fVyw54rwkGg0pa!P)WxfpCY_n7 z+T;TggvP^%!(j9kxd#nN;<0|D-oBkMQ1>>AXZLn{4cy%f>fPHb_3(5TvYkuWc!oyK zrvR}<0F|QrB6XM39fo{}AY$>1b8t7@D3Z{rYoR2Dii<7x){5V#2f~Z9U>VOBaTe+x zRy591Fycm)lDq*>RuS^ER9m8gMwd-0S}5Y;%|^7~@TB7s#(}|CB;!#X?^YBKGDvT} zrDR+ugz)ZW;v}9WW)6QFBZTKrXgW+VR52Vjy=dk2eQ4zqHAMHko_%QO`G!200Y$ zm~|L>V%E=V%Wqg(lw0fQn24^uiS|icc*khJ9pyJj^o$O<3Qs^l4BST`eudxBh_N@! z*YVEsx0k;W6={6=&tK8zbjpR@$rJ2`K8YE>0vPVk@9tOcy`#$)qXDeWLQcloPr0|k zhKaGx!Udr*k9V`8xL9He3>?rpxj6%?E#1|>s?l7;U^(MtG zUQ;OlgzR?`m0wJ>X`eAqbm%n|#D^+8hYAhmUSLFHcD;RWyPiKkb`H#gGPVEbKNyl! z=Kl$hy`@KCY{HF!gX8-4&ROihZQDoft4(AUQhlGub7+g;rcb^T8vvf`)Z7kicHlSH z?6B?C8*|X^wrjWCMPxEky8}nhw^ty(V?B2dZUW$Z!3fC_JVt9cM(zVhZr^M)o8&&= zB_t@)QIFB2kJ-SSKvPO29Tpi8-zErnB|IRCwH~51+EO@lZj_GLkhZeYb~`}aPWc#0 zr1(JuAa)98z4?ak!+I;jr&O`hjLHVGD>ng;3sc_~s*o0=aEZKl_R%{e33{Y~`yhph zBpA$hhU@^U3LQXR{kukNjGvTOsH!W^kOKg6`{Tm7dCx~)LZ1IOH2({iw*F+Ql895s z&p|08y>ouN2l@Y)7g_15{1Qs}8&v!z6}{QW-$L`>scQqpgNOL75A@jzwjNjm>t$ME zWl)lr7MBvXKq>^f+$RiQpn^1EY&9u4i7ZkpSoPxK>*V`QN{O`PU@9==1cyv4_8sDk zbba`a5nhMe&CbVm4H}m~vu2}?#0%I%WrT>p@gRJqK&D1XEsYn+)GqZ?_6L9u&!Eso zExl}v8hNPuk^wc2ngJiYgjyM@KCh49+OO|7iYUo2lpkhI`E|T0KZc@CIMN?!a}yVJ zhEJd%cbtN+E_AxwPa14PC#^}@FF4_GX67m+1pe_ffaR}I!S1B4R>hms+aZLh#4oZ@ zr*;37#-O|rXBTw&4r;JCHg3v*eu%}PS9zYq5U@B$I9#;gcp+4{t9LWPxNndPGY`iG z^Vfw5TXQlpDX4^%G1@Og8D$|_(Fc zNjVOBG1YmA`+S=S?xLtl60k}>9_|~Ovkmdy{+>i!VrwyI_`GfMD}>{1DkK%8IKcWg z)pxLdlB-o{%L;9e;SbXbBndkmol?p-Bx(-;z)DOTvh*zc8DjG+m&&`z>+<&j1Ml(% z;W~=qky?kjXBAJk0vY)f(l_L~BUP_lck)!2)?96*9cwfo+Y%C~{_*J1s$0-gnX z9PoY^7vAdVWb@N^m#;C`+hs>1+So^7VHFXWQLpj={u zNN>nHD4+>gcd{1!8g-~mh%>C(cJAv(97I|Dyv3?B2XrcS&Xu6E$dN@p;ah~wQu@?J zU9x`Q%{9?Xe`Mx*s^JzIyPZ*Nm(hNL6al%1Km%Qx7NX4e0+e|yQP2$Pd7CN6n$obXWxDXlgRVd(gT0sz}h z{fK(HBlM19kq=iM?X~Ar#%h-jwq32TpN6a!d0XS-65M!? z*O%}BDG%hH{IyD!?W&}Bq)#)@{wd9ngcUY*@Fp%%?NM?+M5!-D7PhjZ#2J(q(FS41 zl$C>hqk2h~-$p+roLpI7i1UrmnK(7uh;|3=}7*~f<{N5=XeGw7ehV=ePkfue|(;1j{Fb~8_n;*R_mJY zgHdsEAgc+?`97FT0`pnG;1ofw0w%X)g>wn)VgieUNydc)oG1Y9TXj@&}xnEhjxg==l-%408`rb5AwS-a326Idi&VJRqXT1`6z1hPbX7XXf1K z%xS(&oRWXY&*UEy+@GR|O+1b>*baJM{t>z!khse~q2jwJ(q2*k{EGZD)Xvj}#~D|) z&YYItqk0ihH7Po`dFni4;4$ct{5};lG9!!&JxI#iAwY*ZW+RvEpWGyTIh6zmce24= zW`o&=@5(hNfq9-7;yB*YV6@0TL#Pg)Ex8*Mj+c^l8TTSz@`r#=hh(lhijF!hEd3EV z^SJ7`!f#>_^4&JIUfLYi3I5LuNDm7Kj;LJOI#?*g-I@=D$o>++Ps8KE z;WS0#9ge~~+LA864_?TE_r4!NZbS~2FE8?J^-jU>Ds(b)2F2O%lsL`R$-y3uBFR-; zEYj0XUJTq-(f8MvBgU1H|IN5cz*XqsDs&2>^uf63X&HXc z4ey~quFlxVK}BAxPvhT64D<~f8F*yY$rGTH0z0}HjN!PqWpmmyF&}H>I*{d2*iwHW z3U~(WRl$R>ca1-8lW%#06xfb|2-(Hy>p1Jg z0$X(of~*>jrI5+TY&gNr2q7{&`}zuU_I}i(hK*k}F23s4Ur(~t7s2(MnoLvxM|)5* z1JnD$h(y#di+J|GFxt2UZCbA75Az58WkkrDrhXK7?s349!R!~^7iuc z1hPrRU3ykl)-iRT0!t7K~cv|m$QW3rhLWS4xmNq~>^-~7t4Gqf} zHus@TnL~Ko4@*{lN4uCW!cFTzK(QIW7|Bu(Y#v(FmOlbs><*ht)?TzN>5uFjU97$4 zP>Hk%$9J3LqQM`BwS%656*?p+Ggv|kr{wZEVd1Hc!y$ZBvuV6#&=Fx|-G`&rF*gn5 z?=;HaMOpMf)1~dsRt%e~#wGr8$WeAFE~-}J%-J*XpsKsX>o7|RAneHMC!1LNI7FOsxJcwaO4W8l(H)(U&_kpiN z8$;HE(!3+M(Qez%M2iVZhxl6rO#^uK`GC1Kd-?Z(>thj~66YR9dXal?K7T>; zVFRSg_mY8d+xpS7i@3sZ6v`O!7LG*523(e;$G|o|MZP$CEGjuPverMv3Se3t$0ld9 z30b$aWV8U)$LFq1JVVM%#V2w0k}GdEa0GOPh+IWlfyFVav?+5UQ;aA6s0H(W{S~c{ zT{o))YU47ve;b_vv*k>V@=6!=MVQgZybnXVskir$Ygy6P&+v-WsjylQos6nD%d_PQ zr}nld2}sXj@@=4v%e`|jUa2~fe4L(`;JJ#fI<|9>{9Qc%F)f2+OT$?J_dp7_j5~tUteNnW@!DJ}>*g zmBzzcXKu-f%x%m;#TOt0ou0~1Q^DaSQH6tybU7SG>>$850Q8Uj`)I+V8qp>7<2`=m zDC*l11VjpQ3z-bVs0jT&)Dfq@;P7IZMwL@0@W_4-kLBM|!87|Z)ppE*X8SKRheTSa z-Q%H@>}Jv#zK%-r<%3drHYX8`yQIEMQfEGn5;nB71yx9{{^f|3;5H~oRSK0P9DclO zb9RKJ$%lW+2_akm$lYQr$ZzYy^@%{8P}0QD+=|!RB%L zqM~KD0^~FBQq#lNo^tpz>d7Ys!Go@}yEn4069m4Eay!spyyK->g=veT9M6C*mA^BLM%3b&Cpl zr}0W{ubMQz#2bfoUBL0TTvO+W*=VgzFVJ2~k~7J{2S+P8YUZgZP{F~`0M*!aaH&Rf zlw?EV0wn$lf1x1HQs-+_TtHD9R4IbgE&5A(KuUtSGahmr*GD>#H3)qc1PA37oh-(C z5iC0|O=Y9_Q={$H?#Qz< z>z-c88fG6rhCdNfrG4~&$gkjunpd9m3OpGq;+)>q%{C8YrS9qObNck@KHs^7b8}S! zzqR<^VZTYp-*IyC=fLC^wCo$`IN>xVK7LtD2h3*!!#4(|Zw@Tq8rZ%~2{*VII|J8u z2Nl1f=T=-D)co3@?$;?1mN2=!M1(2kL}P<35p4dvFt{TcyUcHXO1R4_F9@%QGrNX= z7Uor6gLzHQ&k3@(!0Qw0pVy-~7+nwz(d3Q4Fh0+lFHHX;oKl}k7Y%*}*3a^Du)YX< zrf33fUNGUp+2#`A3qU{5F93b%kd8X(^^1HF*5Bdu0ckDm!yAxJ%PK2(WtxlRfl#H> z5!-7!Ls435qhSo6he?zsrP&QLwNHV+#Y$r>?3I;aC^HeLum>xRd&yQ9NBohH8L~UG z!Anv$I;k80Ef>5K4n!BvJo!VJx1eQUE+U9eIq?~%zQGyjXp{>x(Vz8&i0?+RNI**d z>#7I8pC?{4J=ohuvbQm#d)r~=ZKR`wU-d>ADCK4SbQE(>iVYz>m3sH@-1k2E(Z_8q zr}NS*-5@~8K~PqMV36`rjN^I`JR61ajN$|VPrE@N>!9m#_v-hn5Al%IZa+-IUYhW5 z^>*4F4Md_~F%_%N!WHy<;VO=>vH>p7C_HL z+myBxY9x~`qUrF5?_omw1X}hEbc*azP75+&du&3+R2j-F=q}=nbzadG@PuT1LWQuM_0(#S^Zr4^^!LP{It$T{d*OkRLbX#wy@;>0CT|MJW!F{q*k%NeReSd+@{ z!WWcxwx$jvFT(tk@&+tmDAUSguu`NT3Y3-C^15OL*`;Ip8F}`RNtCg3l@k0bh)sS+ z9*}D!F$=h0kBnJCH%(5T8gi{*dlt^VgmCv2n^53~6=m-^805IM=W?4nF9`5dZiD;@ zofw*nZdSQFqH+)Uxe0ug8DDkAhqCBqO>=#%xvDt-lFZJbn{`e5o2J!(RzE?T)3kqS zS{-O}hcrB^08PO_bNL@M7}xqy=D|Na+>Vz;gD4KQKhFYAI|wo*gW#&SK2lyEqClDm z5A3I6;qhn#yk8_;p+#*+s_!8}x5FJTLhgjeBGXG!#3IaKr|6-(PVp)cU9af(a_92n z_O;fPOC>33`;^%bb7;B@8{(rNPa1$Uc%EJQ2u$>haWK$J+IdLGRhS zRJqX;2?Pvz^9G3dCp3~TpF-6PJM9~B3UJMCwvTpnzKu@u`*4K(0d_yc?l{J2kGu?P zw{X1$U2e69X{K_s&BfL$dZpDUt5FsunF`@`3Ng>f4 zOLH@gAXm)Zx-^DTlqSk84G@bS2lr|j@;zj>b-v-H1z(lDrd_%4wwHMy_GQ=+t4bNI z(>kj&lbJ@9o;BZx{#_g$HYPs?PH;;1FpIqDm3SSD0NN`X0EdF?K@l;b*`lHxyb_VS zOaBN#hC}J_CrG(_nfwQ=I3Ued74Uu=v~UVX z%lIqEXcRU?gL#z1n_pR|xvHv~su#}Q92O?S5ZGj>%2q@LC1R0^}O z3b&|inBb-7Isl37ew=?e%i6E!<;&u^5((A_-e3f&WLx1iMUmS53j4ZVF(^`JFhx&Y`51Bp(%rPYO+ zLzPxGj3Xu6Ag{Crp#tzb4?Fih`OWP+I`V7ZD4T&k0!yfAqS48vbbo;dV~J%%D61#a z(NI=ER;wz10w3SzVb&L!{4vU}=-LGDuJe(07QZ@`jH`ZaI-aUw=}BqAnQ!_KutP9!M%>C4aK-|>;?rzN1D*b#+Z$bu~`^d z3>?-m2jqL$Kem)rnDDg$y7mU zVY!5icd)~XNk?c6DXrFtd+KDRD=+uPw0OUXTImbA^j#>qnrx9)ji%kSs|>%3R~bF~ IP5sh;0oO;3xc~qF literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/version.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/__pycache__/version.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54c84c4e873c8720de312ab23d10ce36e5f17c68 GIT binary patch literal 13176 zcmb_iO>i4WcAlOY3_uVBA(EmfiIPS?wg}0jB-`taRa#2cpI5P$)>^W?3rU8Dm?i}Z zf9@Gl5(4fYy~!ro{MDvvt72`1+~lHLs#3`*ha7UtDc3nBC!do;B3G<@@68MdfS{<7 z0@U<$zkbv2_3Q4}@4aqkXeh1WceC=fbM0+S`wuE~{svIEfXBP8YnrRMqM{YTwjjaub9T*Eahnd(qssG2QgtHXui zYOatI+M?zrek%&QBS>Mt)Yd26 zBsfp~R&3~n1K@Pf9RSVrBi$WzGhdj6gYRnYkemHNbF=c$no+P(9(Hpm=hpPXVU%{c zBPfl?!>BzX_4T9fsMME^p}kk!G5mMq8F%+QN)%p2`<5I<`x9t?ue%TJ9|vqN)7$S( zqCDjuDCE)VLH7_^J;be^AinUNlmjF+nL=M9nW8QGk>MV8kKo(anEKHTQJ6-3O721J zF;IHNeHFDcU9#(Hqr1gErSZKQ=uWuDA0^!>H;>j%x#EVFKk*gKwYhvUNL_L&6=%L8 zgVZ%&D#x#@zbf8%UeN0HtMCjK~l+zbkM5d%9X%qC>fZI zI=Ysq)Jsk!ud5u`SHnmi>p^lQB5f(@FRNOK`WF8vpTctqk9Pt|Gwo|FZB2Zl-Ox(oQrzboxUNTg;*^lZ;p<=Ud{)Lc8HY|ApR zlYc4fjui52=KD5gox8>L@UHkf# z>-L*(o^G?oG%7q2F>HoEn9Cb1H?<40U@_)}+K>EWO&!4N$?B<(JjnA@>8?|A7V9STCU2P4`5xM`rMhuYg#^NNL3EOjfcMU{9;4ZsgtL|;J9 zQ(oDZ(+#I|&smh-sW5|1o{9uI(|D+|;DLt8qe0UwU8F@uFtkK~&L0i!1w0;22C0#D zg=Q7dsyYVG;2t)wa+W)e@|j}s^JS+J)g+5Ww_Yk1S(Y8iL9oaSy+sUTXV6lD%UgP7 zpzZ=8{SrystB*8kNK+A)=BlMf#YH3>$ytU2o9 zCF%z4mbBuQ#2VJrV=^H>C?Co2m*q9R!nPO*5N+MvcX(cIlhekR61 zdx<(NyO0saC4BrSPx53&_8|jCgfG@~FY9BDfGMLTmW+E?^6FJoKQ@DO*(=vP->H?P zItoybovZuTs?eM?)ukJ9t*boh)JaNc)r1KRa$psU6~9=#g1l!T(J+Hey*b*$JK>aG zgjWcWc~!5WZFP#0J{(|XzK5!3asZqc;sBD{nFINGvH1NRI3S!C;vn%n94w-LpX|T^ z;k*zB=5uj?F}|}C2QNAgjOXE?M*Z7?1Hzfh?^1_=r))|NQ$qSTur5^_?@1U3;WibO zhKog~R;&9k5b8A#I?yQBd_`J2Fz4&_iaJdVoZ$w-eNVkkkT)oKlag;yvN>%z$10MR z+&Mg~)6*iSXRTcO&kK^e)Ach^x`4;~21%?irQr%FNIg=JCdvt@%TPTgqEe_FEt!nU ztQ^-wA=QSkdIV$ukTkcz3KEb(C}6=5R*`^Y+K>@<^a~S^p*93YDMPYt2nW=%6f@1#ujo0K4Egi zwTmJ85)R{Uu=MlBTr+pBtC6YWPmJUF*^qiaJ|G7L!4-=SIvCl~`w(RdC);&g5qo{R zZ8etXE9KJfwTbD?u`Y&ZLWKVZQki*}CGi~kaR) zZ0DX#;F~|g6D|V1InqM{9^OmS7)SfR>Y&ln4fvBUO=2&a|8)n6o>6BPbq6A33F~uL zw;l9gsO-U=lF{IP*+Ff8x6r!qcBZO+7fo$XHcQQ67$UlV-9dM9E8R`RmnKa8#n?o2 z|E7cP*8Xnd<#+d5Eo3{Wi(9@#i4kxOTdtf7nCb3VLdH^--LyC?0Ieao3{ z-l5FxnU+1B4@9YHAwc6)rk{ul`2;f>Br!8q%JTjbF&*ft#5}_b65du08v97!NhESij3>R*ty4=#~aRtO^La@lM8yh6T6R7z#s!5;*`KX3) z%$BhsfHekpHPO=7!~%shtT}ZJ)YT6t`5}^KGL-0akUAax@)q+?k+SdMmh#Ekfw`ZcZPUN>Xb2K(JZBH(e2jA5e*3s9Dg>SRcS3cW~Iq)&&NH;myt|KG^Lf?K=!!~G=pCn{9EoNzt;MD_o$VD5H zKap=QsG~Nc#{thYZ2?_B+7~Y1@ftAXHXUc^c_9;fp#AqC>bPX#NnX@&x{^W+5%ESi z)d|-^R4|Ql5>9m-V+>1SgfSzBWR`1V7kU_w9AH_BVR8+f>X7TY3pFFC8<1QV)zT>s z)sAv)HiC}2W1+j-1>NnAGjumY={$+}+T%_zbR5u-H$DMEn|s<2J_7<|0+7A@)dBY) zf3>d-Ipo?5+24ln=?}h|1SIU`QTG_*9B4!M3R_frfV*bw|qZy-n-!3HB(Wsjwb`h4m|+84 zpF>`_yZ*#NNFnVHg6H&7hSMQVvz!idn&WiW8sUzx5iv5?9ElE%;<QHXB}ku;m}B*IUR^{&psCnoLiI#Q#91< zFzEazP_^dKDBj(U49tY<(3_oUV~1AcM?l9eN=iR`)dGu@8;VJO*UrS5*MkHrittrq zixXQnIh5O4qzJ%UFpn1HgTV5nqe^$fL!412yQu2RjnJ-LpjK`Xba!O=RHTnH!q@-` zVuOekL=5EsMv#mnFVAF!<1#p6X~$fGMAh-ZP>%ILz{{6A^tdS%`}8bia*OQTs-(xJpxrUIHzb;JrRc$P2m?_T{sIO|Ld`?$R7&CgWl;-0eBjuR27-#kmI3Zo_BHr$J^u-HOC&xXe zZgRw=XRrz3y)MdwakrtUyQq?=X>qf-DHsrnTaATpa^h~VpNr59JGrs+IdITVu~5Zz zBF>6MZ)V!0G38#2wxUpAOqhce{RmUV%?`rtK4X$^q1$$CsimmTwkyb$bq!Wnm~^2s z3j5F7v^n>3%HrAU^A?y7wS*nZj5n)3Is)4euK^C){0v>_7mel(_6H;pQr$HDC-lt) zz)f#27g{>x>aUR^$Fb4|Vh5zg^te~Ia7#@#%*%^TEGnDElU)y2y%#hEqAC`US=F3?6=k^ zL`WV~Jfyp0;?EAvwyBeSA5)Ljx(I`j{m4-;((fQQ-boH>rJp_p`le>j#FpQIXM>+o$(rX5##?)xl4D z7vq}k2l<(NkV9t9eMMukxtP^2sMw#2a;>g@(w7UWc{wf&B1=axERtt(QBw6U`g4Jr zyxkKWN3a+d^(fR@u1GZk3b?j+<->~~+_(~?uf03>!G~8ay>sJ=S|-}8$1YNiR8JUA zpQ9W(P~i$qQjQlVg%bJ((MjKh5%+%K^gp87UsCd?lx(hU&T-_C?)bq@*1QYIr1VKW zcL4GANyOJpT}$1pn7fp)}_X&{sZe zxG<{Q(mc#(rF!AK$)-b99V9q%7zhU!ED${m#5{Jr55QL_^zT#d1`?bC7>J|=Xpd|U zY)A7{MMH-CuPFbQlx#|xh5P`pKO`X^K}Iu8BL#~!l}N!Q+k8yK$WsPVT@ilMx^;V| zIDH50^3F@^Ys?0x4k$iM}uZuBS z<+R2;dR}G5?i?henugRHCs%1ht68`g0k(Pr2u6@Bdv1BL?01Ql9G|d15(!f0Do%CYb!Pt+n4wj(Hwu+>j9rA&MV6);4L(md+D#Cjvcvck zT}8xx2)(kPhZAl=k0ul+d4I{?b?Z)4^b=Ua8=& zFwqb`SfI`$2`v9%1Gj|~2}03AS0sMLdlXG-#rqabfMAer<}8<>N2I5ykBVk` z*ySNYz}+2Ovo974L(xTeN!Q-JaCf8uDP*rJc^!7wWw}CCh0KlR`S)pQb7`QRRc}$t z-=>7N!or@&Zo7o-*imJ#Ui+TJJ!v;c(^X=ZkLT1Vq0^P;i5yXrCX_5X$f2t?+uaV+!4(ek3scD3PJ~}c?Jv9?!nVglK$<5Lc mdG!C<8W`J^J7C(mA8YBX$bOSKmNv$Q=;}90_@{;bKmP~BB7oHZ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_manylinux.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_manylinux.py new file mode 100644 index 0000000..4c379aa --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_manylinux.py @@ -0,0 +1,301 @@ +import collections +import functools +import os +import re +import struct +import sys +import warnings +from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple + + +# Python does not provide platform information at sufficient granularity to +# identify the architecture of the running executable in some cases, so we +# determine it dynamically by reading the information from the running +# process. This only applies on Linux, which uses the ELF format. +class _ELFFileHeader: + # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + class _InvalidELFFileHeader(ValueError): + """ + An invalid ELF file header was found. + """ + + ELF_MAGIC_NUMBER = 0x7F454C46 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + EM_386 = 3 + EM_S390 = 22 + EM_ARM = 40 + EM_X86_64 = 62 + EF_ARM_ABIMASK = 0xFF000000 + EF_ARM_ABI_VER5 = 0x05000000 + EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + def __init__(self, file: IO[bytes]) -> None: + def unpack(fmt: str) -> int: + try: + data = file.read(struct.calcsize(fmt)) + result: Tuple[int, ...] = struct.unpack(fmt, data) + except struct.error: + raise _ELFFileHeader._InvalidELFFileHeader() + return result[0] + + self.e_ident_magic = unpack(">I") + if self.e_ident_magic != self.ELF_MAGIC_NUMBER: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_class = unpack("B") + if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_data = unpack("B") + if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_version = unpack("B") + self.e_ident_osabi = unpack("B") + self.e_ident_abiversion = unpack("B") + self.e_ident_pad = file.read(7) + format_h = "H" + format_i = "I" + format_q = "Q" + format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q + self.e_type = unpack(format_h) + self.e_machine = unpack(format_h) + self.e_version = unpack(format_i) + self.e_entry = unpack(format_p) + self.e_phoff = unpack(format_p) + self.e_shoff = unpack(format_p) + self.e_flags = unpack(format_i) + self.e_ehsize = unpack(format_h) + self.e_phentsize = unpack(format_h) + self.e_phnum = unpack(format_h) + self.e_shentsize = unpack(format_h) + self.e_shnum = unpack(format_h) + self.e_shstrndx = unpack(format_h) + + +def _get_elf_header() -> Optional[_ELFFileHeader]: + try: + with open(sys.executable, "rb") as f: + elf_header = _ELFFileHeader(f) + except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): + return None + return elf_header + + +def _is_linux_armhf() -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_ARM + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABIMASK + ) == elf_header.EF_ARM_ABI_VER5 + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD + ) == elf_header.EF_ARM_ABI_FLOAT_HARD + return result + + +def _is_linux_i686() -> bool: + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_386 + return result + + +def _have_compatible_abi(arch: str) -> bool: + if arch == "armv7l": + return _is_linux_armhf() + if arch == "i686": + return _is_linux_i686() + return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> Optional[str]: + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". + version_string = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.split() + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes() -> Optional[str]: + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return None + + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str: str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> Optional[str]: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: + """Parse glibc version. + + We use a regexp instead of str.split because we want to discard any + random junk that might come after the minor version -- this might happen + in patched/forked versions of glibc (e.g. Linaro's version of glibc + uses version strings like "2.20-2014.11"). See gh-3588. + """ + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + "Expected glibc version with 2 components major.minor," + " got: %s" % version_str, + RuntimeWarning, + ) + return -1, -1 + return int(m.group("major")), int(m.group("minor")) + + +@functools.lru_cache() +def _get_glibc_version() -> Tuple[int, int]: + version_str = _glibc_version_string() + if version_str is None: + return (-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + try: + import _manylinux # noqa + except ImportError: + return True + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + + +def platform_tags(linux: str, arch: str) -> Iterator[str]: + if not _have_compatible_abi(arch): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if arch in {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + if _is_compatible(tag, arch, glibc_version): + yield linux.replace("linux", tag) + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + if _is_compatible(legacy_tag, arch, glibc_version): + yield linux.replace("linux", legacy_tag) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_musllinux.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_musllinux.py new file mode 100644 index 0000000..8ac3059 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_musllinux.py @@ -0,0 +1,136 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +import contextlib +import functools +import operator +import os +import re +import struct +import subprocess +import sys +from typing import IO, Iterator, NamedTuple, Optional, Tuple + + +def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, f.read(struct.calcsize(fmt))) + + +def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]: + """Detect musl libc location by parsing the Python executable. + + Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca + ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html + """ + f.seek(0) + try: + ident = _read_unpacked(f, "16B") + except struct.error: + return None + if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. + return None + f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, p_fmt, p_idx = { + 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. + 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. + }[ident[4]] + except KeyError: + return None + else: + p_get = operator.itemgetter(*p_idx) + + # Find the interpreter section and return its content. + try: + _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) + except struct.error: + return None + for i in range(e_phnum + 1): + f.seek(e_phoff + e_phentsize * i) + try: + p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) + except struct.error: + return None + if p_type != 3: # Not PT_INTERP. + continue + f.seek(p_offset) + interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") + if "musl" not in interpreter: + return None + return interpreter + return None + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache() +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + with contextlib.ExitStack() as stack: + try: + f = stack.enter_context(open(executable, "rb")) + except OSError: + return None + ld = _parse_ld_musl_from_elf(f) + if not ld: + return None + proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(arch: str) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param arch: Should be the part of platform tag after the ``linux_`` + prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a + prerequisite for the current platform to be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): + print(t, end="\n ") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_structures.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_structures.py new file mode 100644 index 0000000..90a6465 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/_structures.py @@ -0,0 +1,61 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +class InfinityType: + def __repr__(self) -> str: + return "Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return False + + def __le__(self, other: object) -> bool: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return True + + def __ge__(self, other: object) -> bool: + return True + + def __neg__(self: object) -> "NegativeInfinityType": + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType: + def __repr__(self) -> str: + return "-Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return True + + def __le__(self, other: object) -> bool: + return True + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return False + + def __ge__(self, other: object) -> bool: + return False + + def __neg__(self: object) -> InfinityType: + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/markers.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/markers.py new file mode 100644 index 0000000..cb640e8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/markers.py @@ -0,0 +1,304 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import operator +import os +import platform +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from pyparsing import ( # noqa: N817 + Forward, + Group, + Literal as L, + ParseException, + ParseResults, + QuotedString, + ZeroOrMore, + stringEnd, + stringStart, +) + +from .specifiers import InvalidSpecifier, Specifier + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + +Operator = Callable[[str, str], bool] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node: + def __init__(self, value: Any) -> None: + self.value = value + + def __str__(self) -> str: + return str(self.value) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +VARIABLE = ( + L("implementation_version") + | L("platform_python_implementation") + | L("implementation_name") + | L("python_full_version") + | L("platform_release") + | L("platform_version") + | L("platform_machine") + | L("platform_system") + | L("python_version") + | L("sys_platform") + | L("os_name") + | L("os.name") # PEP-345 + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 +) +ALIASES = { + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker( + marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators: Dict[str, Operator] = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper: Optional[Operator] = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +class Undefined: + pass + + +_undefined = Undefined() + + +def _get_env(environment: Dict[str, str], name: str) -> str: + value: Union[str, Undefined] = environment.get(name, _undefined) + + if isinstance(value, Undefined): + raise UndefinedEnvironmentName( + f"{name!r} does not exist in evaluation environment." + ) + + return value + + +def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, str)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info: "sys._version_info") -> str: + version = "{0.major}.{0.minor}.{0.micro}".format(info) + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker: + def __init__(self, marker: str) -> None: + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + raise InvalidMarker( + f"Invalid marker: {marker!r}, parse error at " + f"{marker[e.loc : e.loc + 8]!r}" + ) + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"" + + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/py.typed b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/requirements.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/requirements.py new file mode 100644 index 0000000..53f9a3a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/requirements.py @@ -0,0 +1,146 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import re +import string +import urllib.parse +from typing import List, Optional as TOptional, Set + +from pyparsing import ( # noqa + Combine, + Literal as L, + Optional, + ParseException, + Regex, + Word, + ZeroOrMore, + originalTextFor, + stringEnd, + stringStart, +) + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r"[^ ]+")("url") +URL = AT + URI + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine( + VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False +)("_raw_spec") +_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start : t._original_end]) +) +MARKER_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd +# pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") + + +class Requirement: + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string: str) -> None: + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' + ) + + self.name: str = req.name + if req.url: + parsed_url = urllib.parse.urlparse(req.url) + if parsed_url.scheme == "file": + if urllib.parse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement(f"Invalid URL: {req.url}") + self.url: TOptional[str] = req.url + else: + self.url = None + self.extras: Set[str] = set(req.extras.asList() if req.extras else []) + self.specifier: SpecifierSet = SpecifierSet(req.specifier) + self.marker: TOptional[Marker] = req.marker if req.marker else None + + def __str__(self) -> str: + parts: List[str] = [self.name] + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append(f"@ {self.url}") + if self.marker: + parts.append(" ") + + if self.marker: + parts.append(f"; {self.marker}") + + return "".join(parts) + + def __repr__(self) -> str: + return f"" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/specifiers.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/specifiers.py new file mode 100644 index 0000000..0e218a6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/specifiers.py @@ -0,0 +1,802 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import abc +import functools +import itertools +import re +import warnings +from typing import ( + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Pattern, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import LegacyVersion, Version, parse + +ParsedVersion = Union[Version, LegacyVersion] +UnparsedVersion = Union[Version, LegacyVersion, str] +VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) +CallableOperator = Callable[[ParsedVersion, str], bool] + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractproperty + def prereleases(self) -> Optional[bool]: + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value: bool) -> None: + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators: Dict[str, str] = {} + _regex: Pattern[str] + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self) -> str: + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + return self._spec[0], canonicalize_version(self._spec[1]) + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self) -> str: + return self._spec[0] + + @property + def version(self) -> str: + return self._spec[1] + + @property + def prereleases(self) -> Optional[bool]: + return self._prereleases + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + def __contains__(self, item: str) -> bool: + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + normalized_item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later in case nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + super().__init__(spec, prereleases) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal( + self, prospective: LegacyVersion, spec: str + ) -> bool: + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: + return prospective > self._coerce_version(spec) + + +def _require_version_compare( + fn: Callable[["Specifier", ParsedVersion, str], bool] +) -> Callable[["Specifier", ParsedVersion, str], bool]: + @functools.wraps(fn) + def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: + + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore suffix segments. + prefix = ".".join( + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) + + @_require_version_compare + def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: + + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + split_spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + split_prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + shortened_prospective = split_prospective[: len(split_spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec + else: + # Convert our spec string into a Version + spec_version = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec_version.local: + prospective = Version(prospective.public) + + return prospective == spec_version + + @_require_version_compare + def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal( + self, prospective: ParsedVersion, spec: str + ) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self) -> bool: + + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version: str) -> List[str]: + result: List[str] = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) + + # Insert our padding + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) + + +class SpecifierSet(BaseSpecifier): + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + + # Split on , to break each individual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed: Set[_IndividualSpecifier] = set() + for specifier in split_specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self) -> str: + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + if isinstance(other, str): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + return len(self._specs) + + def __iter__(self) -> Iterator[_IndividualSpecifier]: + return iter(self._specs) + + @property + def prereleases(self) -> Optional[bool]: + + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + def __contains__(self, item: UnparsedVersion) -> bool: + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered: List[VersionTypeVar] = [] + found_prereleases: List[VersionTypeVar] = [] + + item: UnparsedVersion + parsed_version: Union[Version, LegacyVersion] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/tags.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/tags.py new file mode 100644 index 0000000..9a3d25a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/tags.py @@ -0,0 +1,487 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import logging +import platform +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ + + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + return self._interpreter + + @property + def abi(self) -> str: + return self._abi + + @property + def platform(self) -> str: + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: + return string.replace(".", "_").replace("-", "_") + + +def _abi3_applies(python_version: PythonVersion) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append(f"cp{version}") + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_version_nodot(python_version[:2])}" + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + if _abi3_applies(python_version): + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) + + +def _generic_abi() -> Iterator[str]: + abi = sysconfig.get_config_var("SOABI") + if abi: + yield _normalize_string(abi) + + +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a generic interpreter. + + The tags consist of: + - -- + + The "none" ABI will be added if it was not explicitly provided. + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> Iterator[Tag]: + """ + Yields the sequence of tags that are compatible with a specific version of Python. + + The tags consist of: + - py*-none- + - -none-any # ... if `interpreter` is provided. + - py*-none-any + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> Iterator[str]: + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version + if arch is None: + arch = _mac_arch(cpu_arch) + else: + arch = arch + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + _, arch = linux.split("_", 1) + yield from _manylinux.platform_tags(linux, arch) + yield from _musllinux.platform_tags(arch) + yield linux + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> Iterator[str]: + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags() + + if interp_name == "pp": + yield from compatible_tags(interpreter="pp3") + else: + yield from compatible_tags() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/utils.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/utils.py new file mode 100644 index 0000000..bab11b8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/utils.py @@ -0,0 +1,136 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import re +from typing import FrozenSet, NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +_canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str) -> NormalizedName: + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) + + +def canonicalize_version(version: Union[Version, str]) -> str: + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version + + parts = [] + + # Epoch + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) + + # Pre-release + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) + + # Post-release + if parsed.post is not None: + parts.append(f".post{parsed.post}") + + # Development release + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") + + # Local version segment + if parsed.local is not None: + parts.append(f"+{parsed.local}") + + return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/version.py b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/version.py new file mode 100644 index 0000000..de9a09a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/packaging/version.py @@ -0,0 +1,504 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import collections +import itertools +import re +import warnings +from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] + +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +LegacyCmpKey = Tuple[int, Tuple[str, ...]] +VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool +] + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version: str) -> Union["LegacyVersion", "Version"]: + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion: + _key: Union[CmpKey, LegacyCmpKey] + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +class LegacyVersion(_BaseVersion): + def __init__(self, version: str) -> None: + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def __str__(self) -> str: + return self._version + + def __repr__(self) -> str: + return f"" + + @property + def public(self) -> str: + return self._version + + @property + def base_version(self) -> str: + return self._version + + @property + def epoch(self) -> int: + return -1 + + @property + def release(self) -> None: + return None + + @property + def pre(self) -> None: + return None + + @property + def post(self) -> None: + return None + + @property + def dev(self) -> None: + return None + + @property + def local(self) -> None: + return None + + @property + def is_prerelease(self) -> bool: + return False + + @property + def is_postrelease(self) -> bool: + return False + + @property + def is_devrelease(self) -> bool: + return False + + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) + +_legacy_version_replacement_map = { + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", +} + + +def _parse_version_parts(s: str) -> Iterator[str]: + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version: str) -> LegacyCmpKey: + + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts: List[str] = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + + return epoch, tuple(parts) + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P

                                              # pre-release
    +            [-_\.]?
    +            (?P(a|b|c|rc|alpha|beta|pre|preview))
    +            [-_\.]?
    +            (?P[0-9]+)?
    +        )?
    +        (?P                                         # post release
    +            (?:-(?P[0-9]+))
    +            |
    +            (?:
    +                [-_\.]?
    +                (?Ppost|rev|r)
    +                [-_\.]?
    +                (?P[0-9]+)?
    +            )
    +        )?
    +        (?P                                          # dev release
    +            [-_\.]?
    +            (?Pdev)
    +            [-_\.]?
    +            (?P[0-9]+)?
    +        )?
    +    )
    +    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
    +"""
    +
    +
    +class Version(_BaseVersion):
    +
    +    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
    +
    +    def __init__(self, version: str) -> None:
    +
    +        # Validate the version and parse it into pieces
    +        match = self._regex.search(version)
    +        if not match:
    +            raise InvalidVersion(f"Invalid version: '{version}'")
    +
    +        # Store the parsed out pieces of the version
    +        self._version = _Version(
    +            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
    +            release=tuple(int(i) for i in match.group("release").split(".")),
    +            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
    +            post=_parse_letter_version(
    +                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
    +            ),
    +            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
    +            local=_parse_local_version(match.group("local")),
    +        )
    +
    +        # Generate a key which will be used for sorting
    +        self._key = _cmpkey(
    +            self._version.epoch,
    +            self._version.release,
    +            self._version.pre,
    +            self._version.post,
    +            self._version.dev,
    +            self._version.local,
    +        )
    +
    +    def __repr__(self) -> str:
    +        return f""
    +
    +    def __str__(self) -> str:
    +        parts = []
    +
    +        # Epoch
    +        if self.epoch != 0:
    +            parts.append(f"{self.epoch}!")
    +
    +        # Release segment
    +        parts.append(".".join(str(x) for x in self.release))
    +
    +        # Pre-release
    +        if self.pre is not None:
    +            parts.append("".join(str(x) for x in self.pre))
    +
    +        # Post-release
    +        if self.post is not None:
    +            parts.append(f".post{self.post}")
    +
    +        # Development release
    +        if self.dev is not None:
    +            parts.append(f".dev{self.dev}")
    +
    +        # Local version segment
    +        if self.local is not None:
    +            parts.append(f"+{self.local}")
    +
    +        return "".join(parts)
    +
    +    @property
    +    def epoch(self) -> int:
    +        _epoch: int = self._version.epoch
    +        return _epoch
    +
    +    @property
    +    def release(self) -> Tuple[int, ...]:
    +        _release: Tuple[int, ...] = self._version.release
    +        return _release
    +
    +    @property
    +    def pre(self) -> Optional[Tuple[str, int]]:
    +        _pre: Optional[Tuple[str, int]] = self._version.pre
    +        return _pre
    +
    +    @property
    +    def post(self) -> Optional[int]:
    +        return self._version.post[1] if self._version.post else None
    +
    +    @property
    +    def dev(self) -> Optional[int]:
    +        return self._version.dev[1] if self._version.dev else None
    +
    +    @property
    +    def local(self) -> Optional[str]:
    +        if self._version.local:
    +            return ".".join(str(x) for x in self._version.local)
    +        else:
    +            return None
    +
    +    @property
    +    def public(self) -> str:
    +        return str(self).split("+", 1)[0]
    +
    +    @property
    +    def base_version(self) -> str:
    +        parts = []
    +
    +        # Epoch
    +        if self.epoch != 0:
    +            parts.append(f"{self.epoch}!")
    +
    +        # Release segment
    +        parts.append(".".join(str(x) for x in self.release))
    +
    +        return "".join(parts)
    +
    +    @property
    +    def is_prerelease(self) -> bool:
    +        return self.dev is not None or self.pre is not None
    +
    +    @property
    +    def is_postrelease(self) -> bool:
    +        return self.post is not None
    +
    +    @property
    +    def is_devrelease(self) -> bool:
    +        return self.dev is not None
    +
    +    @property
    +    def major(self) -> int:
    +        return self.release[0] if len(self.release) >= 1 else 0
    +
    +    @property
    +    def minor(self) -> int:
    +        return self.release[1] if len(self.release) >= 2 else 0
    +
    +    @property
    +    def micro(self) -> int:
    +        return self.release[2] if len(self.release) >= 3 else 0
    +
    +
    +def _parse_letter_version(
    +    letter: str, number: Union[str, bytes, SupportsInt]
    +) -> Optional[Tuple[str, int]]:
    +
    +    if letter:
    +        # We consider there to be an implicit 0 in a pre-release if there is
    +        # not a numeral associated with it.
    +        if number is None:
    +            number = 0
    +
    +        # We normalize any letters to their lower case form
    +        letter = letter.lower()
    +
    +        # We consider some words to be alternate spellings of other words and
    +        # in those cases we want to normalize the spellings to our preferred
    +        # spelling.
    +        if letter == "alpha":
    +            letter = "a"
    +        elif letter == "beta":
    +            letter = "b"
    +        elif letter in ["c", "pre", "preview"]:
    +            letter = "rc"
    +        elif letter in ["rev", "r"]:
    +            letter = "post"
    +
    +        return letter, int(number)
    +    if not letter and number:
    +        # We assume if we are given a number, but we are not given a letter
    +        # then this is using the implicit post release syntax (e.g. 1.0-1)
    +        letter = "post"
    +
    +        return letter, int(number)
    +
    +    return None
    +
    +
    +_local_version_separators = re.compile(r"[\._-]")
    +
    +
    +def _parse_local_version(local: str) -> Optional[LocalType]:
    +    """
    +    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
    +    """
    +    if local is not None:
    +        return tuple(
    +            part.lower() if not part.isdigit() else int(part)
    +            for part in _local_version_separators.split(local)
    +        )
    +    return None
    +
    +
    +def _cmpkey(
    +    epoch: int,
    +    release: Tuple[int, ...],
    +    pre: Optional[Tuple[str, int]],
    +    post: Optional[Tuple[str, int]],
    +    dev: Optional[Tuple[str, int]],
    +    local: Optional[Tuple[SubLocalType]],
    +) -> CmpKey:
    +
    +    # When we compare a release version, we want to compare it with all of the
    +    # trailing zeros removed. So we'll use a reverse the list, drop all the now
    +    # leading zeros until we come to something non zero, then take the rest
    +    # re-reverse it back into the correct order and make it a tuple and use
    +    # that for our sorting key.
    +    _release = tuple(
    +        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
    +    )
    +
    +    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
    +    # We'll do this by abusing the pre segment, but we _only_ want to do this
    +    # if there is not a pre or a post segment. If we have one of those then
    +    # the normal sorting rules will handle this case correctly.
    +    if pre is None and post is None and dev is not None:
    +        _pre: PrePostDevType = NegativeInfinity
    +    # Versions without a pre-release (except as noted above) should sort after
    +    # those with one.
    +    elif pre is None:
    +        _pre = Infinity
    +    else:
    +        _pre = pre
    +
    +    # Versions without a post segment should sort before those with one.
    +    if post is None:
    +        _post: PrePostDevType = NegativeInfinity
    +
    +    else:
    +        _post = post
    +
    +    # Versions without a development segment should sort after those with one.
    +    if dev is None:
    +        _dev: PrePostDevType = Infinity
    +
    +    else:
    +        _dev = dev
    +
    +    if local is None:
    +        # Versions without a local segment should sort before those with one.
    +        _local: LocalType = NegativeInfinity
    +    else:
    +        # Versions with a local segment need that segment parsed to implement
    +        # the sorting rules in PEP440.
    +        # - Alpha numeric segments sort before numeric segments
    +        # - Alpha numeric segments sort lexicographically
    +        # - Numeric segments sort numerically
    +        # - Shorter versions sort before longer versions when the prefixes
    +        #   match exactly
    +        _local = tuple(
    +            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
    +        )
    +
    +    return epoch, _release, _pre, _post, _dev, _local
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/INSTALLER b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/INSTALLER
    new file mode 100644
    index 0000000..a1b589e
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/LICENSE.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/LICENSE.txt
    new file mode 100644
    index 0000000..737fec5
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/LICENSE.txt
    @@ -0,0 +1,20 @@
    +Copyright (c) 2008-2019 The pip developers (see AUTHORS.txt file)
    +
    +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/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/METADATA b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/METADATA
    new file mode 100644
    index 0000000..7fdd705
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/METADATA
    @@ -0,0 +1,88 @@
    +Metadata-Version: 2.1
    +Name: pip
    +Version: 20.2.3
    +Summary: The PyPA recommended tool for installing Python packages.
    +Home-page: https://pip.pypa.io/
    +Author: The pip developers
    +Author-email: distutils-sig@python.org
    +License: MIT
    +Project-URL: Documentation, https://pip.pypa.io
    +Project-URL: Source, https://github.com/pypa/pip
    +Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
    +Keywords: distutils easy_install egg setuptools wheel virtualenv
    +Platform: UNKNOWN
    +Classifier: Development Status :: 5 - Production/Stable
    +Classifier: Intended Audience :: Developers
    +Classifier: License :: OSI Approved :: MIT License
    +Classifier: Topic :: Software Development :: Build Tools
    +Classifier: Programming Language :: Python
    +Classifier: Programming Language :: Python :: 2
    +Classifier: Programming Language :: Python :: 2.7
    +Classifier: Programming Language :: Python :: 3
    +Classifier: Programming Language :: Python :: 3.5
    +Classifier: Programming Language :: Python :: 3.6
    +Classifier: Programming Language :: Python :: 3.7
    +Classifier: Programming Language :: Python :: 3.8
    +Classifier: Programming Language :: Python :: Implementation :: CPython
    +Classifier: Programming Language :: Python :: Implementation :: PyPy
    +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*
    +
    +pip - The Python Package Installer
    +==================================
    +
    +.. image:: https://img.shields.io/pypi/v/pip.svg
    +   :target: https://pypi.org/project/pip/
    +
    +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
    +   :target: https://pip.pypa.io/en/latest
    +
    +pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
    +
    +Please take a look at our documentation for how to install and use pip:
    +
    +* `Installation`_
    +* `Usage`_
    +
    +We release updates regularly, with a new version every 3 months. Find more details in our documentation:
    +
    +* `Release notes`_
    +* `Release process`_
    +
    +In 2020, we're working on improvements to the heart of pip. Please `learn more and take our survey`_ to help us do it right.
    +
    +If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
    +
    +* `Issue tracking`_
    +* `Discourse channel`_
    +* `User IRC`_
    +
    +If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
    +
    +* `GitHub page`_
    +* `Development documentation`_
    +* `Development mailing list`_
    +* `Development IRC`_
    +
    +Code of Conduct
    +---------------
    +
    +Everyone interacting in the pip project's codebases, issue trackers, chat
    +rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
    +
    +.. _package installer: https://packaging.python.org/guides/tool-recommendations/
    +.. _Python Package Index: https://pypi.org
    +.. _Installation: https://pip.pypa.io/en/stable/installing.html
    +.. _Usage: https://pip.pypa.io/en/stable/
    +.. _Release notes: https://pip.pypa.io/en/stable/news.html
    +.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
    +.. _GitHub page: https://github.com/pypa/pip
    +.. _Development documentation: https://pip.pypa.io/en/latest/development
    +.. _learn more and take our survey: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html
    +.. _Issue tracking: https://github.com/pypa/pip/issues
    +.. _Discourse channel: https://discuss.python.org/c/packaging
    +.. _Development mailing list: https://mail.python.org/mailman3/lists/distutils-sig.python.org/
    +.. _User IRC: https://webchat.freenode.net/?channels=%23pypa
    +.. _Development IRC: https://webchat.freenode.net/?channels=%23pypa-dev
    +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
    +
    +
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/RECORD b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/RECORD
    new file mode 100644
    index 0000000..78cde50
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/RECORD
    @@ -0,0 +1,752 @@
    +../../../bin/pip,sha256=_e4BRm8H3KD2DJHqkPXh1JBB7ibN82BzkXhfzns2gMU,293
    +../../../bin/pip3,sha256=_e4BRm8H3KD2DJHqkPXh1JBB7ibN82BzkXhfzns2gMU,293
    +../../../bin/pip3.9,sha256=_e4BRm8H3KD2DJHqkPXh1JBB7ibN82BzkXhfzns2gMU,293
    +pip-20.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +pip-20.2.3.dist-info/LICENSE.txt,sha256=W6Ifuwlk-TatfRU2LR7W1JMcyMj5_y1NkRkOEJvnRDE,1090
    +pip-20.2.3.dist-info/METADATA,sha256=9mmHP3BezeQwiPj12NdLFspqcxqrf7xqW6OX9PwZSr4,3708
    +pip-20.2.3.dist-info/RECORD,,
    +pip-20.2.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip-20.2.3.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110
    +pip-20.2.3.dist-info/entry_points.txt,sha256=HtfDOwpUlr9s73jqLQ6wF9V0_0qvUXJwCBz7Vwx0Ue0,125
    +pip-20.2.3.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +pip/__init__.py,sha256=NkPibWV383InU5x7DgeQLdL2zhlXTKjJRBMQTSKNYjI,455
    +pip/__main__.py,sha256=bqCAM1cj1HwHCDx3WJa-LJxOBXimGxE8OjBqAvnhVg0,911
    +pip/__pycache__/__init__.cpython-39.pyc,,
    +pip/__pycache__/__main__.cpython-39.pyc,,
    +pip/_internal/__init__.py,sha256=2si23JBW1erg19xIJ8CD6tfGknz0ijtXmzuXjGfGMGE,495
    +pip/_internal/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/__pycache__/build_env.cpython-39.pyc,,
    +pip/_internal/__pycache__/cache.cpython-39.pyc,,
    +pip/_internal/__pycache__/configuration.cpython-39.pyc,,
    +pip/_internal/__pycache__/exceptions.cpython-39.pyc,,
    +pip/_internal/__pycache__/locations.cpython-39.pyc,,
    +pip/_internal/__pycache__/main.cpython-39.pyc,,
    +pip/_internal/__pycache__/pyproject.cpython-39.pyc,,
    +pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc,,
    +pip/_internal/__pycache__/wheel_builder.cpython-39.pyc,,
    +pip/_internal/build_env.py,sha256=9_UaQ2fpsBvpKAji27f7bPAi2v3mb0cBvDYcejwFKNM,8088
    +pip/_internal/cache.py,sha256=pT17VVxgzZK32aqY5FRS8GyAI73LKzNMF8ZelQ7Ojm0,12249
    +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132
    +pip/_internal/cli/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/base_command.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/command_context.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/main.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/parser.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/req_command.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/spinners.cpython-39.pyc,,
    +pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc,,
    +pip/_internal/cli/autocompletion.py,sha256=ekGNtcDI0p7rFVc-7s4T9Tbss4Jgb7vsB649XJIblRg,6547
    +pip/_internal/cli/base_command.py,sha256=bf058xM1HE9QJCNEHExbuTjL0peKg9kSxCNxDtwAh88,9302
    +pip/_internal/cli/cmdoptions.py,sha256=M_BtuqeyRpZAUUYytts3pguBCF2RaGukVpDPE0niroI,28782
    +pip/_internal/cli/command_context.py,sha256=ygMVoTy2jpNilKT-6416gFSQpaBtrKRBbVbi2fy__EU,975
    +pip/_internal/cli/main.py,sha256=Hxc9dZyW3xiDsYZX-_J2cGXT5DWNLNn_Y7o9oUme-Ec,2616
    +pip/_internal/cli/main_parser.py,sha256=voAtjo4WVPIYeu7Fqabva9SXaB3BjG0gH93GBfe6jHQ,2843
    +pip/_internal/cli/parser.py,sha256=4FfwW8xB84CrkLs35ud90ZkhCcWyVkx17XD6j3XCW7c,9480
    +pip/_internal/cli/progress_bars.py,sha256=J1zykt2LI4gbBeXorfYRmYV5FgXhcW4x3r6xE_a7Z7c,9121
    +pip/_internal/cli/req_command.py,sha256=Eiz8TVzeqzG-40t7qLC1vO-vzjCRvX9C-qXMyfw9D1I,15132
    +pip/_internal/cli/spinners.py,sha256=PS9s53LB5aDPelIn8FhKerK3bOdgeefFH5wSWJ2PCzI,5509
    +pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156
    +pip/_internal/commands/__init__.py,sha256=yoLAnmEXjoQgYfDuwsuWG3RzzD19oeHobGEhmpIYsB4,4100
    +pip/_internal/commands/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/cache.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/check.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/completion.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/configuration.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/debug.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/download.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/freeze.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/hash.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/help.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/install.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/list.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/search.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/show.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc,,
    +pip/_internal/commands/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/commands/cache.py,sha256=U3rLjls0AMMO8PxnhXVwIp7Biyvns8-gBThKTH3tX7Y,5676
    +pip/_internal/commands/check.py,sha256=fqRrz2uKPC8Qsx2rgLygAD2Rbr-qxp1Q55zUoyZzB9Q,1677
    +pip/_internal/commands/completion.py,sha256=ObssM77quf61qvbuSE6XLwUBdm_WcWIvXFI-Hy1RBsI,3081
    +pip/_internal/commands/configuration.py,sha256=IN2QBF653sRiRU7-pHTpnZ6_gyiXNKUQkLiLaNRLKNw,9344
    +pip/_internal/commands/debug.py,sha256=otBZnpnostX2kmYyOl6g6CeCLmk6H00Tsj2CDsCtFXw,7314
    +pip/_internal/commands/download.py,sha256=EKFlj_ceGUEJj6yCDw7P6w7yUoB16IcNHhT2qnCFDNQ,4918
    +pip/_internal/commands/freeze.py,sha256=vLBBP1d8wgEXrmlh06hbz_x_Q1mWHUdiWDa9NP2eKLE,3452
    +pip/_internal/commands/hash.py,sha256=v2nYCiEsEI9nEam1p6GwdG8xyj5gFv-4WrqvNexKmeY,1843
    +pip/_internal/commands/help.py,sha256=ryuMDt2tc7ic3NJYMjjoNRH5r6LrB2yQVZvehAm8bLs,1270
    +pip/_internal/commands/install.py,sha256=OXjZCNSioJRfP7YMkJyAWLl7X7-8f6DkhWlhPhG6fXk,27995
    +pip/_internal/commands/list.py,sha256=2o3rYw37ECrhe4-Bu5s_2C0bwhYgghh4833MxcWAEug,11312
    +pip/_internal/commands/search.py,sha256=1HPAFU-tmgKrHhr4xNuk3xMoPeSzD_oDvDDiUFZZ15E,5756
    +pip/_internal/commands/show.py,sha256=r69-G8HIepDKm4SeyeHj0Ez1P9xoihrpVUyXm6NmXYY,6996
    +pip/_internal/commands/uninstall.py,sha256=Ys8hwFsg0kvvGtLGYG3ibL5BKvURhlSlCX50ZQ-hsHk,3311
    +pip/_internal/commands/wheel.py,sha256=-HSISE5AV29I752Aqw4DdmulrGd8rB_ZTOdpbJ6T8iM,6419
    +pip/_internal/configuration.py,sha256=-Gxz2J-KuvxiqWIJ9F-XnYVZ5lKhNk7VO6ondEbH4EM,14115
    +pip/_internal/distributions/__init__.py,sha256=ECBUW5Gtu9TjJwyFLvim-i6kUMYVuikNh9I5asL6tbA,959
    +pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/distributions/__pycache__/base.cpython-39.pyc,,
    +pip/_internal/distributions/__pycache__/installed.cpython-39.pyc,,
    +pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc,,
    +pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/distributions/base.py,sha256=ruprpM_L2T2HNi3KLUHlbHimZ1sWVw-3Q0Lb8O7TDAI,1425
    +pip/_internal/distributions/installed.py,sha256=YqlkBKr6TVP1MAYS6SG8ojud21wVOYLMZ8jMLJe9MSU,760
    +pip/_internal/distributions/sdist.py,sha256=D4XTMlCwgPlK69l62GLYkNSVTVe99fR5iAcVt2EbGok,4086
    +pip/_internal/distributions/wheel.py,sha256=95uD-TfaYoq3KiKBdzk9YMN4RRqJ28LNoSTS2K46gek,1294
    +pip/_internal/exceptions.py,sha256=ZVpArxQrSlm4qAMtHaY3nHvG_t5eSi3WCnMowdm_m8I,12637
    +pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30
    +pip/_internal/index/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/index/__pycache__/collector.cpython-39.pyc,,
    +pip/_internal/index/__pycache__/package_finder.cpython-39.pyc,,
    +pip/_internal/index/collector.py,sha256=rMdGdAABOrvIl0DYlCMWXr7mIoqrU2VGeQpCuWiPu1Q,22838
    +pip/_internal/index/package_finder.py,sha256=ISieDd20dOSndMNybafCu3pO2JR3BKOfHv92Bes0j0Q,37364
    +pip/_internal/locations.py,sha256=7YjzJy2CroQD8GBMemnHWRl9448BSIt0lfH98B-Dkd8,6732
    +pip/_internal/main.py,sha256=IVBnUQ-FG7DK6617uEXRB5_QJqspAsBFmTmTesYkbdQ,437
    +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63
    +pip/_internal/models/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/candidate.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/direct_url.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/format_control.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/index.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/link.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/scheme.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/search_scope.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/target_python.cpython-39.pyc,,
    +pip/_internal/models/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/models/candidate.py,sha256=gACeCSHTIaWuB6RAeLmGJnbFFbKfp_47UERDoC_ldOU,1195
    +pip/_internal/models/direct_url.py,sha256=MnBLPci1hE9Ndh6d3m0LAqB7hX3ci80CCJTE5eerFaQ,6900
    +pip/_internal/models/format_control.py,sha256=RdnnmXxVJppCZWzWEmFTr-zD_m3G0izPLqJi6Iop75M,2823
    +pip/_internal/models/index.py,sha256=carvxxaT7mJyoEkptaECHUZiNaA6R5NrsGF55zawNn8,1161
    +pip/_internal/models/link.py,sha256=FMlxvqKmLoj7xTQSgKqfO2ehE1WcgD4C5DmEBuC_Qos,7470
    +pip/_internal/models/scheme.py,sha256=EhPkT_6G0Md84JTLSVopYsp5H_K6BREYmFvU8H6wMK8,778
    +pip/_internal/models/search_scope.py,sha256=Lum0mY4_pdR9DDBy6HV5xHGIMPp_kU8vMsqYKFHZip4,4751
    +pip/_internal/models/selection_prefs.py,sha256=pgNjTfgePPiX1R5S2S8Yc6odOfU9NzG7YP_m_gnS0kw,2044
    +pip/_internal/models/target_python.py,sha256=R7tAXI15B_cgw7Fgnq5cI9F-44goUZncH9JMtE8pXRw,4034
    +pip/_internal/models/wheel.py,sha256=FTfzVb4WIbfIehxhdlAVvCil_MQ0-W44oyN56cE6NHc,2772
    +pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50
    +pip/_internal/network/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/auth.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/cache.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/download.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/session.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/utils.cpython-39.pyc,,
    +pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc,,
    +pip/_internal/network/auth.py,sha256=dt3NvTRJ8182S3VpdYFEZMPT0JhOKHyFtR-O-JMlJII,11652
    +pip/_internal/network/cache.py,sha256=6cCD7XNrqh1d1lOSY5U-0ZXOG1YwEgMYs-VhRZVyzMA,2329
    +pip/_internal/network/download.py,sha256=VTGDO01_nX-5MCdatd4Icv0F88_M8N3WnW6BevA6a0o,5151
    +pip/_internal/network/lazy_wheel.py,sha256=RXcQILT5v5UO6kxgv76CSncLTqRL29o-OXbaW2aK7t4,8138
    +pip/_internal/network/session.py,sha256=Zs0uiyPxTpfpgSv-ZI9hK9TjasmTplBuBivOTcUiJME,15208
    +pip/_internal/network/utils.py,sha256=ZPHg7u6DEcg2EvILIdPECnvPLp21OPHxNVmeXfMy-n0,4172
    +pip/_internal/network/xmlrpc.py,sha256=PFCiX_nnwYxC8SFIf7J3trP40ECGjA6fl2-IVNhbkPM,1882
    +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/operations/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/operations/__pycache__/check.cpython-39.pyc,,
    +pip/_internal/operations/__pycache__/freeze.cpython-39.pyc,,
    +pip/_internal/operations/__pycache__/prepare.cpython-39.pyc,,
    +pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc,,
    +pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc,,
    +pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc,,
    +pip/_internal/operations/build/metadata.py,sha256=2aILgWCQTF1aIhWuCH8TTSjv_kYmA3x1262fT2FQ6pQ,1254
    +pip/_internal/operations/build/metadata_legacy.py,sha256=VgzBTk8naIO8-8N_ifEYF7ZAxWUDhphWVIaVlZ2FqYM,2011
    +pip/_internal/operations/build/wheel.py,sha256=33vdkxTO-gNqrtWH1eNL_uZo4Irax85moDx2o9zae3M,1465
    +pip/_internal/operations/build/wheel_legacy.py,sha256=N1aqNZyGURBX0Bj6wPmB0t4866oMbxoHUpC9pz6FyT0,3356
    +pip/_internal/operations/check.py,sha256=JYDsVLvpFyJuJq0ttStgg8TRKbc0myYFAMnfnnQOREM,5215
    +pip/_internal/operations/freeze.py,sha256=_vJSZwHBNzBV0GpRUSXhUJz3BrGFdcT2aTcWxH1L4P0,10373
    +pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51
    +pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc,,
    +pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc,,
    +pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/operations/install/editable_legacy.py,sha256=rJ_xs2qtDUjpY2-n6eYlVyZiNoKbOtZXZrYrcnIELt4,1488
    +pip/_internal/operations/install/legacy.py,sha256=zu3Gw54dgHtluyW5n8j5qKcAScidQXJvqB8fb0oLB-4,4281
    +pip/_internal/operations/install/wheel.py,sha256=nJmOSOYY3keksXd_3GFuhAWeeoKvGOyoSGbjXABjZ40,31310
    +pip/_internal/operations/prepare.py,sha256=Rt7Yh7w10_Q-vI3b7R1wkt2R6XPX8YVUdODk-TaGI9c,19903
    +pip/_internal/pyproject.py,sha256=VJKsrXORGiGoDPVKCQhuu4tWlQSTOhoiRlVLRNu4rx4,7400
    +pip/_internal/req/__init__.py,sha256=s-E5Vxxqqpcs7xfY5gY69oHogsWJ4sLbnUiDoWmkHOU,3133
    +pip/_internal/req/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/constructors.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/req_file.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/req_install.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/req_set.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc,,
    +pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc,,
    +pip/_internal/req/constructors.py,sha256=LrSHbRHu52-h6HM1qJKG68o1Jw5q8MvJGfr4As6j2uU,16387
    +pip/_internal/req/req_file.py,sha256=p7n3Y0q275Eisqfxd0vtfnxYvlT6TCCY0tj75p-yiOY,19448
    +pip/_internal/req/req_install.py,sha256=5IYle0AaLivlkZo6mhU9sj30CbzPqLe92csBnAfJq8U,33610
    +pip/_internal/req/req_set.py,sha256=dxcfbieWYfYkTJNE07U8xaO40zLxl8BhWOcIHVFTmoo,7886
    +pip/_internal/req/req_tracker.py,sha256=qWaiejNK6o6cqeyTOIGKIU1CoyrXCcqgMHYi3cqelOA,4690
    +pip/_internal/req/req_uninstall.py,sha256=opMGDGb7ZaFippRbaarJaljtzl2CNZmBGEUSnTubE-A,23706
    +pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/resolution/__pycache__/base.cpython-39.pyc,,
    +pip/_internal/resolution/base.py,sha256=xi72YmIS-lEjyK13PN_3qkGGthA4yGoK0C6qWynyHrE,682
    +pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc,,
    +pip/_internal/resolution/legacy/resolver.py,sha256=d-qW6UUxbZqKyXmX2bqnW5C8UtnO0ZcsQuKw_QXualc,18755
    +pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc,,
    +pip/_internal/resolution/resolvelib/base.py,sha256=n8Rilea9jCzhlbtFiJKwCwIQSPW0ATjEKsCc0Vpm894,2342
    +pip/_internal/resolution/resolvelib/candidates.py,sha256=RHo9r9g25FWzufKv93Ti9nS4hvAPUrhAjSDL7GCZFNQ,20339
    +pip/_internal/resolution/resolvelib/factory.py,sha256=--ahYsr-r9zIhdyJJ1ZuETgaQrWiPIqwILWiMDn1IIU,17169
    +pip/_internal/resolution/resolvelib/provider.py,sha256=BP8nh07Z1FlcT-Iaw4FblRM-DjUeUkiItKdKARYeM6M,6134
    +pip/_internal/resolution/resolvelib/requirements.py,sha256=lGvoHRhkusRfaz4cFxYBoQNqxS6TeuO3K68qlui6g-0,4511
    +pip/_internal/resolution/resolvelib/resolver.py,sha256=kI8g0NVlYIsDMRmDplWQdox6WO-0H7CI2wN-1ixnaew,10149
    +pip/_internal/self_outdated_check.py,sha256=q6_nqUHPpt-DScwD97h7FCSqd4nI1s-xkpOI4I5Za3Y,6779
    +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_internal/utils/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/compat.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/datetime.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/encoding.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/glibc.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/hashes.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/logging.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/misc.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/models.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/packaging.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/parallel.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/typing.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/urls.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc,,
    +pip/_internal/utils/__pycache__/wheel.cpython-39.pyc,,
    +pip/_internal/utils/appdirs.py,sha256=RZzUG-Bkh2b-miX0DSZ3v703_-bgK-v0PfWCCjwVE9g,1349
    +pip/_internal/utils/compat.py,sha256=GoCSUMoUmTGeg5irQGLDZ7v12As87yHrMzBXEke-njg,8865
    +pip/_internal/utils/compatibility_tags.py,sha256=EtBJj-pstj_U0STUZ8FjlG7YDTjuRZUy6GY1cM86yv8,5439
    +pip/_internal/utils/datetime.py,sha256=KL-vIdGU9JIpGB5NYkmwXWkH-G_2mvvABlmRtoSZsao,295
    +pip/_internal/utils/deprecation.py,sha256=pBnNogoA4UGTxa_JDnPXBRRYpKMbExAhXpBwAwklOBs,3318
    +pip/_internal/utils/direct_url_helpers.py,sha256=bZCBNwPQVyZpYGjX_VcomvVvRHvKw-9JzEV-Ft09LQc,4359
    +pip/_internal/utils/distutils_args.py,sha256=a56mblNxk9BGifbpEETG61mmBrqhjtjRkJ4HYn-oOEE,1350
    +pip/_internal/utils/encoding.py,sha256=wHDJ25yCT_T4ySscCL3P978OpLrfDCpitg8D64IEXMY,1284
    +pip/_internal/utils/entrypoints.py,sha256=vHcNpnksCv6mllihU6hfifdsKPEjwcaJ1aLIXEaynaU,1152
    +pip/_internal/utils/filesystem.py,sha256=-fU3XteCAIJwf_9FvCZU7vhywvt3nuf_cqkCdwgy1Y8,6943
    +pip/_internal/utils/filetypes.py,sha256=R2FwzoeX7b-rZALOXx5cuO8VPPMhUQ4ne7wm3n3IcWA,571
    +pip/_internal/utils/glibc.py,sha256=LOeNGgawCKS-4ke9fii78fwXD73dtNav3uxz1Bf-Ab8,3297
    +pip/_internal/utils/hashes.py,sha256=xHmrqNwC1eBN0oY0R_RXLJLXGvFdo5gwmbz_pas94k8,4358
    +pip/_internal/utils/inject_securetransport.py,sha256=M17ZlFVY66ApgeASVjKKLKNz0LAfk-SyU0HZ4ZB6MmI,810
    +pip/_internal/utils/logging.py,sha256=YIfuDUEkmdn9cIRQ_Ec8rgXs1m5nOwDECtZqM4CBH5U,13093
    +pip/_internal/utils/misc.py,sha256=QQZWMJkKKADPSWQYmrwlasc8b03eCcghn0yDNprYgrI,28001
    +pip/_internal/utils/models.py,sha256=HqiBVtTbW_b_Umvj2fjhDWOHo2RKhPwSz4iAYkQZ688,1201
    +pip/_internal/utils/packaging.py,sha256=VtiwcAAL7LBi7tGL2je7LeW4bE11KMHGCsJ1NZY5XtM,3035
    +pip/_internal/utils/parallel.py,sha256=7az3aaTMCkqpaLFbpYYOvk0rj7Hu5YH1NPXXomqjgf4,3404
    +pip/_internal/utils/pkg_resources.py,sha256=ZX-k7V5q_aNWyDse92nN7orN1aCpRLsaxzpkBZ1XKzU,1254
    +pip/_internal/utils/setuptools_build.py,sha256=E1KswI7wfNnCDE5R6G8c9ZbByENpu7NqocjY26PCQDw,5058
    +pip/_internal/utils/subprocess.py,sha256=UkPe89gcjxBMx73uutoeJXgD3kwdlL6YO16BkjDdVSI,9924
    +pip/_internal/utils/temp_dir.py,sha256=blmG0jEvEgdxbYUt_V15bgcTIJIrxZwAw8QZlCTJYDE,8378
    +pip/_internal/utils/typing.py,sha256=xkYwOeHlf4zsHXBDC4310HtEqwhQcYXFPq2h35Tcrl0,1401
    +pip/_internal/utils/unpacking.py,sha256=YFAckhqqvmehA8Kan5vd3b1kN_9TafqmOk4b-yz4fho,9488
    +pip/_internal/utils/urls.py,sha256=q2rw1kMiiig_XZcoyJSsWMJQqYw-2wUmrMoST4mCW_I,1527
    +pip/_internal/utils/virtualenv.py,sha256=fNGrRp-8QmNL5OzXajBd-z7PbwOsx1XY6G-AVMAhfso,3706
    +pip/_internal/utils/wheel.py,sha256=wFzn3h8GqYvgsyWPZtUyn0Rb3MJzmtyP3owMOhKnmL0,7303
    +pip/_internal/vcs/__init__.py,sha256=viJxJRqRE_mVScum85bgQIXAd6o0ozFt18VpC-qIJrM,617
    +pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc,,
    +pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc,,
    +pip/_internal/vcs/__pycache__/git.cpython-39.pyc,,
    +pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc,,
    +pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc,,
    +pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc,,
    +pip/_internal/vcs/bazaar.py,sha256=5rRR02uDZTLaxQT-R5Obd8FZDOMlShqYds-pwVSJJs8,3887
    +pip/_internal/vcs/git.py,sha256=kvB729wrKY0OWMSgOS1pUly4LosZp8utrd3kOQsWalA,13985
    +pip/_internal/vcs/mercurial.py,sha256=FzCGmYzVZvB-vyM73fKcQk2B4jMNXGnXlQ2bJ7nmglM,5162
    +pip/_internal/vcs/subversion.py,sha256=rldcn9ZDt5twjNPzFn_FKRn4qdfkjlxHMJEsR2MFfoA,12399
    +pip/_internal/vcs/versioncontrol.py,sha256=WpxeTRC0NoGB2uXJdmfq4pPxY-p7sk1rV_WkxMxgzQA,25966
    +pip/_internal/wheel_builder.py,sha256=6w1VPXrpUvCCPlV0cI1wNaCqNz4laF6B6whvaxl9cns,9522
    +pip/_vendor/__init__.py,sha256=CsxnpYPbi_2agrDI79iQrCmQeZRcwwIF0C6cm_1RynU,4588
    +pip/_vendor/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/__pycache__/appdirs.cpython-39.pyc,,
    +pip/_vendor/__pycache__/contextlib2.cpython-39.pyc,,
    +pip/_vendor/__pycache__/distro.cpython-39.pyc,,
    +pip/_vendor/__pycache__/ipaddress.cpython-39.pyc,,
    +pip/_vendor/__pycache__/pyparsing.cpython-39.pyc,,
    +pip/_vendor/__pycache__/retrying.cpython-39.pyc,,
    +pip/_vendor/__pycache__/six.cpython-39.pyc,,
    +pip/_vendor/appdirs.py,sha256=M6IYRJtdZgmSPCXCSMBRB0VT3P8MdFbWCDbSLrB2Ebg,25907
    +pip/_vendor/cachecontrol/__init__.py,sha256=pJtAaUxOsMPnytI1A3juAJkXYDr8krdSnsg4Yg3OBEg,302
    +pip/_vendor/cachecontrol/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/adapter.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/cache.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/serialize.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/_cmd.py,sha256=URGE0KrA87QekCG3SGPatlSPT571dZTDjNa-ZXX3pDc,1295
    +pip/_vendor/cachecontrol/adapter.py,sha256=sSwaSYd93IIfCFU4tOMgSo6b2LCt_gBSaQUj8ktJFOA,4882
    +pip/_vendor/cachecontrol/cache.py,sha256=1fc4wJP8HYt1ycnJXeEw5pCpeBL2Cqxx6g9Fb0AYDWQ,805
    +pip/_vendor/cachecontrol/caches/__init__.py,sha256=-gHNKYvaeD0kOk5M74eOrsSgIKUtC6i6GfbmugGweEo,86
    +pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-39.pyc,,
    +pip/_vendor/cachecontrol/caches/file_cache.py,sha256=nYVKsJtXh6gJXvdn1iWyrhxvkwpQrK-eKoMRzuiwkKk,4153
    +pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=HxelMpNCo-dYr2fiJDwM3hhhRmxUYtB5tXm1GpAAT4Y,856
    +pip/_vendor/cachecontrol/compat.py,sha256=kHNvMRdt6s_Xwqq_9qJmr9ou3wYMOMUMxPPcwNxT8Mc,695
    +pip/_vendor/cachecontrol/controller.py,sha256=CWEX3pedIM9s60suf4zZPtm_JvVgnvogMGK_OiBG5F8,14149
    +pip/_vendor/cachecontrol/filewrapper.py,sha256=vACKO8Llzu_ZWyjV1Fxn1MA4TGU60N5N3GSrAFdAY2Q,2533
    +pip/_vendor/cachecontrol/heuristics.py,sha256=BFGHJ3yQcxvZizfo90LLZ04T_Z5XSCXvFotrp7Us0sc,4070
    +pip/_vendor/cachecontrol/serialize.py,sha256=vIa4jvq4x_KSOLdEIedoknX2aXYHQujLDFV4-F21Dno,7091
    +pip/_vendor/cachecontrol/wrapper.py,sha256=5LX0uJwkNQUtYSEw3aGmGu9WY8wGipd81mJ8lG0d0M4,690
    +pip/_vendor/certifi/__init__.py,sha256=u1E_DrSGj_nnEkK5VglvEqP8D80KpghLVWL0A_pq41A,62
    +pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255
    +pip/_vendor/certifi/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/certifi/__pycache__/__main__.cpython-39.pyc,,
    +pip/_vendor/certifi/__pycache__/core.cpython-39.pyc,,
    +pip/_vendor/certifi/cacert.pem,sha256=GhT24f0R7_9y4YY_hkXwkO7BthZhRGDCEMO348E9S14,282394
    +pip/_vendor/certifi/core.py,sha256=jBrwKEWpG0IKcuozK0BQ2HHGp8adXAOyBPC7ddgR6vM,2315
    +pip/_vendor/chardet/__init__.py,sha256=YsP5wQlsHJ2auF1RZJfypiSrCA7_bQiRm3ES_NI76-Y,1559
    +pip/_vendor/chardet/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/enums.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/gb2312prober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/jisfreq.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/sjisprober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/utf8prober.cpython-39.pyc,,
    +pip/_vendor/chardet/__pycache__/version.cpython-39.pyc,,
    +pip/_vendor/chardet/big5freq.py,sha256=D_zK5GyzoVsRes0HkLJziltFQX0bKCLOrFe9_xDvO_8,31254
    +pip/_vendor/chardet/big5prober.py,sha256=kBxHbdetBpPe7xrlb-e990iot64g_eGSLd32lB7_h3M,1757
    +pip/_vendor/chardet/chardistribution.py,sha256=3woWS62KrGooKyqz4zQSnjFbJpa6V7g02daAibTwcl8,9411
    +pip/_vendor/chardet/charsetgroupprober.py,sha256=6bDu8YIiRuScX4ca9Igb0U69TA2PGXXDej6Cc4_9kO4,3787
    +pip/_vendor/chardet/charsetprober.py,sha256=KSmwJErjypyj0bRZmC5F5eM7c8YQgLYIjZXintZNstg,5110
    +pip/_vendor/chardet/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
    +pip/_vendor/chardet/cli/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-39.pyc,,
    +pip/_vendor/chardet/cli/chardetect.py,sha256=DI8dlV3FBD0c0XA_y3sQ78z754DUv1J8n34RtDjOXNw,2774
    +pip/_vendor/chardet/codingstatemachine.py,sha256=VYp_6cyyki5sHgXDSZnXW4q1oelHc3cu9AyQTX7uug8,3590
    +pip/_vendor/chardet/compat.py,sha256=PKTzHkSbtbHDqS9PyujMbX74q1a8mMpeQTDVsQhZMRw,1134
    +pip/_vendor/chardet/cp949prober.py,sha256=TZ434QX8zzBsnUvL_8wm4AQVTZ2ZkqEEQL_lNw9f9ow,1855
    +pip/_vendor/chardet/enums.py,sha256=Aimwdb9as1dJKZaFNUH2OhWIVBVd6ZkJJ_WK5sNY8cU,1661
    +pip/_vendor/chardet/escprober.py,sha256=kkyqVg1Yw3DIOAMJ2bdlyQgUFQhuHAW8dUGskToNWSc,3950
    +pip/_vendor/chardet/escsm.py,sha256=RuXlgNvTIDarndvllNCk5WZBIpdCxQ0kcd9EAuxUh84,10510
    +pip/_vendor/chardet/eucjpprober.py,sha256=iD8Jdp0ISRjgjiVN7f0e8xGeQJ5GM2oeZ1dA8nbSeUw,3749
    +pip/_vendor/chardet/euckrfreq.py,sha256=-7GdmvgWez4-eO4SuXpa7tBiDi5vRXQ8WvdFAzVaSfo,13546
    +pip/_vendor/chardet/euckrprober.py,sha256=MqFMTQXxW4HbzIpZ9lKDHB3GN8SP4yiHenTmf8g_PxY,1748
    +pip/_vendor/chardet/euctwfreq.py,sha256=No1WyduFOgB5VITUA7PLyC5oJRNzRyMbBxaKI1l16MA,31621
    +pip/_vendor/chardet/euctwprober.py,sha256=13p6EP4yRaxqnP4iHtxHOJ6R2zxHq1_m8hTRjzVZ95c,1747
    +pip/_vendor/chardet/gb2312freq.py,sha256=JX8lsweKLmnCwmk8UHEQsLgkr_rP_kEbvivC4qPOrlc,20715
    +pip/_vendor/chardet/gb2312prober.py,sha256=gGvIWi9WhDjE-xQXHvNIyrnLvEbMAYgyUSZ65HUfylw,1754
    +pip/_vendor/chardet/hebrewprober.py,sha256=c3SZ-K7hvyzGY6JRAZxJgwJ_sUS9k0WYkvMY00YBYFo,13838
    +pip/_vendor/chardet/jisfreq.py,sha256=vpmJv2Bu0J8gnMVRPHMFefTRvo_ha1mryLig8CBwgOg,25777
    +pip/_vendor/chardet/jpcntx.py,sha256=PYlNqRUQT8LM3cT5FmHGP0iiscFlTWED92MALvBungo,19643
    +pip/_vendor/chardet/langbulgarianmodel.py,sha256=1HqQS9Pbtnj1xQgxitJMvw8X6kKr5OockNCZWfEQrPE,12839
    +pip/_vendor/chardet/langcyrillicmodel.py,sha256=LODajvsetH87yYDDQKA2CULXUH87tI223dhfjh9Zx9c,17948
    +pip/_vendor/chardet/langgreekmodel.py,sha256=8YAW7bU8YwSJap0kIJSbPMw1BEqzGjWzqcqf0WgUKAA,12688
    +pip/_vendor/chardet/langhebrewmodel.py,sha256=JSnqmE5E62tDLTPTvLpQsg5gOMO4PbdWRvV7Avkc0HA,11345
    +pip/_vendor/chardet/langhungarianmodel.py,sha256=RhapYSG5l0ZaO-VV4Fan5sW0WRGQqhwBM61yx3yxyOA,12592
    +pip/_vendor/chardet/langthaimodel.py,sha256=8l0173Gu_W6G8mxmQOTEF4ls2YdE7FxWf3QkSxEGXJQ,11290
    +pip/_vendor/chardet/langturkishmodel.py,sha256=W22eRNJsqI6uWAfwXSKVWWnCerYqrI8dZQTm_M0lRFk,11102
    +pip/_vendor/chardet/latin1prober.py,sha256=S2IoORhFk39FEFOlSFWtgVybRiP6h7BlLldHVclNkU8,5370
    +pip/_vendor/chardet/mbcharsetprober.py,sha256=AR95eFH9vuqSfvLQZN-L5ijea25NOBCoXqw8s5O9xLQ,3413
    +pip/_vendor/chardet/mbcsgroupprober.py,sha256=h6TRnnYq2OxG1WdD5JOyxcdVpn7dG0q-vB8nWr5mbh4,2012
    +pip/_vendor/chardet/mbcssm.py,sha256=SY32wVIF3HzcjY3BaEspy9metbNSKxIIB0RKPn7tjpI,25481
    +pip/_vendor/chardet/sbcharsetprober.py,sha256=LDSpCldDCFlYwUkGkwD2oFxLlPWIWXT09akH_2PiY74,5657
    +pip/_vendor/chardet/sbcsgroupprober.py,sha256=1IprcCB_k1qfmnxGC6MBbxELlKqD3scW6S8YIwdeyXA,3546
    +pip/_vendor/chardet/sjisprober.py,sha256=IIt-lZj0WJqK4rmUZzKZP4GJlE8KUEtFYVuY96ek5MQ,3774
    +pip/_vendor/chardet/universaldetector.py,sha256=qL0174lSZE442eB21nnktT9_VcAye07laFWUeUrjttY,12485
    +pip/_vendor/chardet/utf8prober.py,sha256=IdD8v3zWOsB8OLiyPi-y_fqwipRFxV9Nc1eKBLSuIEw,2766
    +pip/_vendor/chardet/version.py,sha256=sp3B08mrDXB-pf3K9fqJ_zeDHOCLC8RrngQyDFap_7g,242
    +pip/_vendor/colorama/__init__.py,sha256=DqjXH9URVP3IJwmMt7peYw50ns1RNAymIB9-XdPEFV8,239
    +pip/_vendor/colorama/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/colorama/__pycache__/ansi.cpython-39.pyc,,
    +pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc,,
    +pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc,,
    +pip/_vendor/colorama/__pycache__/win32.cpython-39.pyc,,
    +pip/_vendor/colorama/__pycache__/winterm.cpython-39.pyc,,
    +pip/_vendor/colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524
    +pip/_vendor/colorama/ansitowin32.py,sha256=u8QaqdqS_xYSfNkPM1eRJLHz6JMWPodaJaP0mxgHCDc,10462
    +pip/_vendor/colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915
    +pip/_vendor/colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404
    +pip/_vendor/colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438
    +pip/_vendor/contextlib2.py,sha256=5HjGflUzwWAUfcILhSmC2GqvoYdZZzFzVfIDztHigUs,16915
    +pip/_vendor/distlib/__init__.py,sha256=3veAk2rPznOB2gsK6tjbbh0TQMmGE5P82eE9wXq6NIk,581
    +pip/_vendor/distlib/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/database.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/index.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/locators.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/manifest.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/markers.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/metadata.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/scripts.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/util.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/version.cpython-39.pyc,,
    +pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/__init__.py,sha256=bqS_dTOH6uW9iGgd0uzfpPjo6vZ4xpPZ7kyfZJ2vNaw,274
    +pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-39.pyc,,
    +pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971
    +pip/_vendor/distlib/_backport/shutil.py,sha256=IX_G2NPqwecJibkIDje04bqu0xpHkfSQ2GaGdEVqM5Y,25707
    +pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617
    +pip/_vendor/distlib/_backport/sysconfig.py,sha256=BQHFlb6pubCl_dvT1NjtzIthylofjKisox239stDg0U,26854
    +pip/_vendor/distlib/_backport/tarfile.py,sha256=Ihp7rXRcjbIKw8COm9wSePV9ARGXbSF9gGXAMn2Q-KU,92628
    +pip/_vendor/distlib/compat.py,sha256=ADA56xiAxar3mU6qemlBhNbsrFPosXRhO44RzsbJPqk,41408
    +pip/_vendor/distlib/database.py,sha256=Kl0YvPQKc4OcpVi7k5cFziydM1xOK8iqdxLGXgbZHV4,51059
    +pip/_vendor/distlib/index.py,sha256=SXKzpQCERctxYDMp_OLee2f0J0e19ZhGdCIoMlUfUQM,21066
    +pip/_vendor/distlib/locators.py,sha256=c9E4cDEacJ_uKbuE5BqAVocoWp6rsuBGTkiNDQq3zV4,52100
    +pip/_vendor/distlib/manifest.py,sha256=nQEhYmgoreaBZzyFzwYsXxJARu3fo4EkunU163U16iE,14811
    +pip/_vendor/distlib/markers.py,sha256=6Ac3cCfFBERexiESWIOXmg-apIP8l2esafNSX3KMy-8,4387
    +pip/_vendor/distlib/metadata.py,sha256=z2KPy3h3tcDnb9Xs7nAqQ5Oz0bqjWAUFmKWcFKRoodg,38962
    +pip/_vendor/distlib/resources.py,sha256=2FGv0ZHF14KXjLIlL0R991lyQQGcewOS4mJ-5n-JVnc,10766
    +pip/_vendor/distlib/scripts.py,sha256=_MAj3sMuv56kuM8FsiIWXqbT0gmumPGaOR_atOzn4a4,17180
    +pip/_vendor/distlib/t32.exe,sha256=NS3xBCVAld35JVFNmb-1QRyVtThukMrwZVeXn4LhaEQ,96768
    +pip/_vendor/distlib/t64.exe,sha256=oAqHes78rUWVM0OtVqIhUvequl_PKhAhXYQWnUf7zR0,105984
    +pip/_vendor/distlib/util.py,sha256=f2jZCPrcLCt6LcnC0gUy-Fur60tXD8reA7k4rDpHMDw,59845
    +pip/_vendor/distlib/version.py,sha256=_n7F6juvQGAcn769E_SHa7fOcf5ERlEVymJ_EjPRwGw,23391
    +pip/_vendor/distlib/w32.exe,sha256=lJtnZdeUxTZWya_EW5DZos_K5rswRECGspIl8ZJCIXs,90112
    +pip/_vendor/distlib/w64.exe,sha256=0aRzoN2BO9NWW4ENy4_4vHkHR4qZTFZNVSAJJYlODTI,99840
    +pip/_vendor/distlib/wheel.py,sha256=v6DnwTqhNHwrEVFr8_YeiTW6G4ftP_evsywNgrmdb2o,41144
    +pip/_vendor/distro.py,sha256=xxMIh2a3KmippeWEHzynTdHT3_jZM0o-pos0dAWJROM,43628
    +pip/_vendor/html5lib/__init__.py,sha256=BYzcKCqeEii52xDrqBFruhnmtmkiuHXFyFh-cglQ8mk,1160
    +pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/_utils.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/constants.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc,,
    +pip/_vendor/html5lib/__pycache__/serializer.cpython-39.pyc,,
    +pip/_vendor/html5lib/_ihatexml.py,sha256=ifOwF7pXqmyThIXc3boWc96s4MDezqRrRVp7FwDYUFs,16728
    +pip/_vendor/html5lib/_inputstream.py,sha256=jErNASMlkgs7MpOM9Ve_VdLDJyFFweAjLuhVutZz33U,32353
    +pip/_vendor/html5lib/_tokenizer.py,sha256=04mgA2sNTniutl2fxFv-ei5bns4iRaPxVXXHh_HrV_4,77040
    +pip/_vendor/html5lib/_trie/__init__.py,sha256=nqfgO910329BEVJ5T4psVwQtjd2iJyEXQ2-X8c1YxwU,109
    +pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-39.pyc,,
    +pip/_vendor/html5lib/_trie/__pycache__/py.cpython-39.pyc,,
    +pip/_vendor/html5lib/_trie/_base.py,sha256=CaybYyMro8uERQYjby2tTeSUatnWDfWroUN9N7ety5w,1013
    +pip/_vendor/html5lib/_trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775
    +pip/_vendor/html5lib/_utils.py,sha256=Dx9AKntksRjFT1veBj7I362pf5OgIaT0zglwq43RnfU,4931
    +pip/_vendor/html5lib/constants.py,sha256=Ll-yzLU_jcjyAI_h57zkqZ7aQWE5t5xA4y_jQgoUUhw,83464
    +pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/base.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-39.pyc,,
    +pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=lViZc2JMCclXi_5gduvmdzrRxtO5Xo9ONnbHBVCsykU,919
    +pip/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286
    +pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945
    +pip/_vendor/html5lib/filters/lint.py,sha256=jk6q56xY0ojiYfvpdP-OZSm9eTqcAdRqhCoPItemPYA,3643
    +pip/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588
    +pip/_vendor/html5lib/filters/sanitizer.py,sha256=m6oGmkBhkGAnn2nV6D4hE78SCZ6WEnK9rKdZB3uXBIc,26897
    +pip/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214
    +pip/_vendor/html5lib/html5parser.py,sha256=anr-aXre_ImfrkQ35c_rftKXxC80vJCREKe06Tq15HA,117186
    +pip/_vendor/html5lib/serializer.py,sha256=_PpvcZF07cwE7xr9uKkZqh5f4UEaI8ltCU2xPJzaTpk,15759
    +pip/_vendor/html5lib/treeadapters/__init__.py,sha256=A0rY5gXIe4bJOiSGRO_j_tFhngRBO8QZPzPtPw5dFzo,679
    +pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-39.pyc,,
    +pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-39.pyc,,
    +pip/_vendor/html5lib/treeadapters/genshi.py,sha256=CH27pAsDKmu4ZGkAUrwty7u0KauGLCZRLPMzaO3M5vo,1715
    +pip/_vendor/html5lib/treeadapters/sax.py,sha256=BKS8woQTnKiqeffHsxChUqL4q2ZR_wb5fc9MJ3zQC8s,1776
    +pip/_vendor/html5lib/treebuilders/__init__.py,sha256=AysSJyvPfikCMMsTVvaxwkgDieELD5dfR8FJIAuq7hY,3592
    +pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-39.pyc,,
    +pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-39.pyc,,
    +pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-39.pyc,,
    +pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-39.pyc,,
    +pip/_vendor/html5lib/treebuilders/base.py,sha256=z-o51vt9r_l2IDG5IioTOKGzZne4Fy3_Fc-7ztrOh4I,14565
    +pip/_vendor/html5lib/treebuilders/dom.py,sha256=22whb0C71zXIsai5mamg6qzBEiigcBIvaDy4Asw3at0,8925
    +pip/_vendor/html5lib/treebuilders/etree.py,sha256=w5ZFpKk6bAxnrwD2_BrF5EVC7vzz0L3LMi9Sxrbc_8w,12836
    +pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9gqDjs-IxsPhBYa5cpvv2FZ1KZlG83Giusy2lFmvIkE,14766
    +pip/_vendor/html5lib/treewalkers/__init__.py,sha256=OBPtc1TU5mGyy18QDMxKEyYEz0wxFUUNj5v0-XgmYhY,5719
    +pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-39.pyc,,
    +pip/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476
    +pip/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413
    +pip/_vendor/html5lib/treewalkers/etree.py,sha256=xo1L5m9VtkfpFJK0pFmkLVajhqYYVisVZn3k9kYpPkI,4551
    +pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=_b0LAVWLcVu9WaU_-w3D8f0IRSpCbjf667V-3NRdhTw,6357
    +pip/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309
    +pip/_vendor/idna/__init__.py,sha256=9Nt7xpyet3DmOrPUGooDdAwmHZZu1qUAy2EaJ93kGiQ,58
    +pip/_vendor/idna/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/codec.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/core.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/idnadata.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/intranges.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/package_data.cpython-39.pyc,,
    +pip/_vendor/idna/__pycache__/uts46data.cpython-39.pyc,,
    +pip/_vendor/idna/codec.py,sha256=lvYb7yu7PhAqFaAIAdWcwgaWI2UmgseUua-1c0AsG0A,3299
    +pip/_vendor/idna/compat.py,sha256=R-h29D-6mrnJzbXxymrWUW7iZUvy-26TQwZ0ij57i4U,232
    +pip/_vendor/idna/core.py,sha256=jCoaLb3bA2tS_DDx9PpGuNTEZZN2jAzB369aP-IHYRE,11951
    +pip/_vendor/idna/idnadata.py,sha256=gmzFwZWjdms3kKZ_M_vwz7-LP_SCgYfSeE03B21Qpsk,42350
    +pip/_vendor/idna/intranges.py,sha256=TY1lpxZIQWEP6tNqjZkFA5hgoMWOj1OBmnUG8ihT87E,1749
    +pip/_vendor/idna/package_data.py,sha256=bxBjpLnE06_1jSYKEy5svOMu1zM3OMztXVUb1tPlcp0,22
    +pip/_vendor/idna/uts46data.py,sha256=lMdw2zdjkH1JUWXPPEfFUSYT3Fyj60bBmfLvvy5m7ko,202084
    +pip/_vendor/ipaddress.py,sha256=-0RmurI31XgAaN20WCi0zrcuoat90nNA70_6yGlx2PU,79875
    +pip/_vendor/msgpack/__init__.py,sha256=2gJwcsTIaAtCM0GMi2rU-_Y6kILeeQuqRkrQ22jSANc,1118
    +pip/_vendor/msgpack/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/msgpack/__pycache__/_version.cpython-39.pyc,,
    +pip/_vendor/msgpack/__pycache__/exceptions.cpython-39.pyc,,
    +pip/_vendor/msgpack/__pycache__/ext.cpython-39.pyc,,
    +pip/_vendor/msgpack/__pycache__/fallback.cpython-39.pyc,,
    +pip/_vendor/msgpack/_version.py,sha256=hu7lzmZ_ClOaOOmRsWb4xomhzQ4UIsLsvv8KY6UysHE,20
    +pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081
    +pip/_vendor/msgpack/ext.py,sha256=nV19BzE9Be8SJHrxxYJHFbvEHJaXcP3avRkHVp5wovM,6034
    +pip/_vendor/msgpack/fallback.py,sha256=Z8V3iYUUPqKVy4WWTk64Vq3G0PylQIOmlWvgnMhmkdU,37133
    +pip/_vendor/packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736
    +pip/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562
    +pip/_vendor/packaging/__pycache__/__about__.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/_compat.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/_structures.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/_typing.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/markers.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/requirements.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/tags.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/utils.cpython-39.pyc,,
    +pip/_vendor/packaging/__pycache__/version.cpython-39.pyc,,
    +pip/_vendor/packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128
    +pip/_vendor/packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022
    +pip/_vendor/packaging/_typing.py,sha256=VgA0AAvsc97KB5nF89zoudOyCMEsV7FlaXzZbYqEkzA,1824
    +pip/_vendor/packaging/markers.py,sha256=V_RdoQqOUbSfy7y9o2vRk7BkzAh3yneC82cuWpKrqOg,9491
    +pip/_vendor/packaging/requirements.py,sha256=F93hkn7i8NKRZP-FtdTIlhz1PUsRjhe6eRbsBXX0Uh4,4903
    +pip/_vendor/packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944
    +pip/_vendor/packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067
    +pip/_vendor/packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811
    +pip/_vendor/packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470
    +pip/_vendor/pep517/__init__.py,sha256=r5uA106NGJa3slspaD2m32aFpFUiZX-mZ9vIlzAEOp4,84
    +pip/_vendor/pep517/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/_in_process.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/build.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/check.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/colorlog.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/dirtools.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/envbuild.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/meta.cpython-39.pyc,,
    +pip/_vendor/pep517/__pycache__/wrappers.cpython-39.pyc,,
    +pip/_vendor/pep517/_in_process.py,sha256=XrKOTURJdia5R7i3i_OQmS89LASFXE3HQXfX63qZBIE,8438
    +pip/_vendor/pep517/build.py,sha256=DN4ouyj_bd00knOKqv0KHRtN0-JezJoNNZQmcDi4juk,3335
    +pip/_vendor/pep517/check.py,sha256=YoaNE3poJGpz96biVCYwtcDshwEGE2HRU5KKya9yfpY,5961
    +pip/_vendor/pep517/colorlog.py,sha256=Tk9AuYm_cLF3BKTBoSTJt9bRryn0aFojIQOwbfVUTxQ,4098
    +pip/_vendor/pep517/compat.py,sha256=M-5s4VNp8rjyT76ZZ_ibnPD44DYVzSQlyCEHayjtDPw,780
    +pip/_vendor/pep517/dirtools.py,sha256=2mkAkAL0mRz_elYFjRKuekTJVipH1zTn4tbf1EDev84,1129
    +pip/_vendor/pep517/envbuild.py,sha256=szKUFlO50X1ahQfXwz4hD9V2VE_bz9MLVPIeidsFo4w,6041
    +pip/_vendor/pep517/meta.py,sha256=8mnM5lDnT4zXQpBTliJbRGfesH7iioHwozbDxALPS9Y,2463
    +pip/_vendor/pep517/wrappers.py,sha256=yFU4Lp7TIYbmuVOTY-pXnlyGZ3F_grIi-JlLkpGN8Gk,10783
    +pip/_vendor/pkg_resources/__init__.py,sha256=XpGBfvS9fafA6bm5rx7vnxdxs7yqyoc_NnpzKApkJ64,108277
    +pip/_vendor/pkg_resources/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-39.pyc,,
    +pip/_vendor/pkg_resources/py31compat.py,sha256=CRk8fkiPRDLsbi5pZcKsHI__Pbmh_94L8mr9Qy9Ab2U,562
    +pip/_vendor/progress/__init__.py,sha256=fcbQQXo5np2CoQyhSH5XprkicwLZNLePR3uIahznSO0,4857
    +pip/_vendor/progress/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/progress/__pycache__/bar.cpython-39.pyc,,
    +pip/_vendor/progress/__pycache__/counter.cpython-39.pyc,,
    +pip/_vendor/progress/__pycache__/spinner.cpython-39.pyc,,
    +pip/_vendor/progress/bar.py,sha256=QuDuVNcmXgpxtNtxO0Fq72xKigxABaVmxYGBw4J3Z_E,2854
    +pip/_vendor/progress/counter.py,sha256=MznyBrvPWrOlGe4MZAlGUb9q3aODe6_aNYeAE_VNoYA,1372
    +pip/_vendor/progress/spinner.py,sha256=k8JbDW94T0-WXuXfxZIFhdoNPYp3jfnpXqBnfRv5fGs,1380
    +pip/_vendor/pyparsing.py,sha256=J1b4z3S_KwyJW7hKGnoN-hXW9pgMIzIP6QThyY5yJq4,273394
    +pip/_vendor/requests/__init__.py,sha256=orzv4-1uejMDc2v3LnTVneINGXiwqXSfrASoFBsYblE,4465
    +pip/_vendor/requests/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/__version__.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/_internal_utils.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/adapters.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/api.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/auth.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/certs.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/compat.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/cookies.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/exceptions.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/help.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/hooks.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/models.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/packages.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/sessions.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/status_codes.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/structures.cpython-39.pyc,,
    +pip/_vendor/requests/__pycache__/utils.cpython-39.pyc,,
    +pip/_vendor/requests/__version__.py,sha256=Xwky1FMlMkJJGidBM50JC7FKcosWzkjIW-WhQGrBdFM,441
    +pip/_vendor/requests/_internal_utils.py,sha256=Zx3PnEUccyfsB-ie11nZVAW8qClJy0gx1qNME7rgT18,1096
    +pip/_vendor/requests/adapters.py,sha256=e-bmKEApNVqFdylxuMJJfiaHdlmS_zhWhIMEzlHvGuc,21548
    +pip/_vendor/requests/api.py,sha256=PlHM-HT3PQ5lyufoeGmV-nJxRi7UnUyGVh7OV7B9XV4,6496
    +pip/_vendor/requests/auth.py,sha256=OMoJIVKyRLy9THr91y8rxysZuclwPB-K1Xg1zBomUhQ,10207
    +pip/_vendor/requests/certs.py,sha256=nXRVq9DtGmv_1AYbwjTu9UrgAcdJv05ZvkNeaoLOZxY,465
    +pip/_vendor/requests/compat.py,sha256=LQWuCR4qXk6w7-qQopXyz0WNHUdAD40k0mKnaAEf1-g,2045
    +pip/_vendor/requests/cookies.py,sha256=Y-bKX6TvW3FnYlE6Au0SXtVVWcaNdFvuAwQxw-G0iTI,18430
    +pip/_vendor/requests/exceptions.py,sha256=d9fJJw8YFBB9VzG9qhvxLuOx6be3c_Dwbck-dVUEAcs,3173
    +pip/_vendor/requests/help.py,sha256=SJPVcoXeo7KfK4AxJN5eFVQCjr0im87tU2n7ubLsksU,3578
    +pip/_vendor/requests/hooks.py,sha256=QReGyy0bRcr5rkwCuObNakbYsc7EkiKeBwG4qHekr2Q,757
    +pip/_vendor/requests/models.py,sha256=_tKIbrscbGvaTdX1UHCwRaiYmPF9VBIuBeydr4Qx1Tg,34287
    +pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695
    +pip/_vendor/requests/sessions.py,sha256=OBtwQs1vjkB1xamFdi_p5y8BVeX16BJoQcwSwx_Y3fI,29316
    +pip/_vendor/requests/status_codes.py,sha256=gT79Pbs_cQjBgp-fvrUgg1dn2DQO32bDj4TInjnMPSc,4188
    +pip/_vendor/requests/structures.py,sha256=msAtr9mq1JxHd-JRyiILfdFlpbJwvvFuP3rfUQT_QxE,3005
    +pip/_vendor/requests/utils.py,sha256=VBs99cvV8Z29WGXeWZqHzZ80_nu1AwwjYzJfe0wQIvs,30176
    +pip/_vendor/resolvelib/__init__.py,sha256=sqMOy4CbVJQiaG9bCPj0oAntGAVy-RWdPfVaC9XDIEQ,537
    +pip/_vendor/resolvelib/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/resolvelib/__pycache__/providers.cpython-39.pyc,,
    +pip/_vendor/resolvelib/__pycache__/reporters.cpython-39.pyc,,
    +pip/_vendor/resolvelib/__pycache__/resolvers.cpython-39.pyc,,
    +pip/_vendor/resolvelib/__pycache__/structs.cpython-39.pyc,,
    +pip/_vendor/resolvelib/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-39.pyc,,
    +pip/_vendor/resolvelib/compat/collections_abc.py,sha256=mtTkpr3Gf3OGvU1PD8YuvrJRhVbioxV82T-niFPoX3o,127
    +pip/_vendor/resolvelib/providers.py,sha256=TZDCmL-Ic-R5JRIZY8G4FLG5xB2343B0DfuK7aw2Yqw,4547
    +pip/_vendor/resolvelib/reporters.py,sha256=ZPSJnVfK8WvXTbX8jE0Nren0-_Hg9ym4epCUPtU8Y0U,1405
    +pip/_vendor/resolvelib/resolvers.py,sha256=lQTGcc-2fgHbmdiLzeNDUxVmGc5ZFjkAL6JrVqnqJIw,15018
    +pip/_vendor/resolvelib/structs.py,sha256=yrdhd-n7DercimPGclXe20rgqhlxw8PnxC0wmcXO19Y,2016
    +pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972
    +pip/_vendor/six.py,sha256=U4Z_yv534W5CNyjY9i8V1OXY2SjAny8y2L5vDLhhThM,34159
    +pip/_vendor/toml/__init__.py,sha256=rJ1pu933HgUtyeeNiusoPd5jJOPNhaKHhSSld3o8AQo,747
    +pip/_vendor/toml/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/toml/__pycache__/common.cpython-39.pyc,,
    +pip/_vendor/toml/__pycache__/decoder.cpython-39.pyc,,
    +pip/_vendor/toml/__pycache__/encoder.cpython-39.pyc,,
    +pip/_vendor/toml/__pycache__/ordered.cpython-39.pyc,,
    +pip/_vendor/toml/__pycache__/tz.cpython-39.pyc,,
    +pip/_vendor/toml/common.py,sha256=ViBccAduP6eZNJAb1POhRhjOAi56TDsNgWJ1TjgXAug,242
    +pip/_vendor/toml/decoder.py,sha256=atpXmyFCzNGiqhkcYLySBuJQkPeSHDzBz47sEaX1amw,38696
    +pip/_vendor/toml/encoder.py,sha256=fPqLyFdPAam17X9SELz2TMp9affkfHCmgWZxRKcmzhY,9955
    +pip/_vendor/toml/ordered.py,sha256=UWt5Eka90IWVBYdvLgY5PXnkBcVYpHjnw9T67rM85T8,378
    +pip/_vendor/toml/tz.py,sha256=DrAgI3wZxZiGcLuV_l8ueA_nPrYoxQ3hZA9tJSjWRsQ,618
    +pip/_vendor/urllib3/__init__.py,sha256=rdFZCO1L7e8861ZTvo8AiSKwxCe9SnWQUQwJ599YV9c,2683
    +pip/_vendor/urllib3/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/_collections.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/connection.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/connectionpool.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/exceptions.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/fields.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/filepost.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/poolmanager.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/request.cpython-39.pyc,,
    +pip/_vendor/urllib3/__pycache__/response.cpython-39.pyc,,
    +pip/_vendor/urllib3/_collections.py,sha256=GouVsNzwg6jADZTmimMI6oqmwKSswnMo9dh5tGNVWO4,10792
    +pip/_vendor/urllib3/connection.py,sha256=Fln8a_bkegdNMkFoSOwyI0PJvL1OqzVUO6ifihKOTpc,14461
    +pip/_vendor/urllib3/connectionpool.py,sha256=egdaX-Db_LVXifDxv3JY0dHIpQqDv0wC0_9Eeh8FkPM,35725
    +pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957
    +pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-39.pyc,,
    +pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=mullWYFaghBdRWla6HYU-TBgFRTPLBEfxj3jplbeJmQ,16886
    +pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=V7GnujxnWZh2N2sMsV5N4d9Imymokkm3zBwgt77_bSE,11956
    +pip/_vendor/urllib3/contrib/appengine.py,sha256=gfdK4T7CRin7v9HRhHDbDh-Hbk66hHDWeoz7nV3PJo8,11034
    +pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=a402AwGN_Ll3N-4ur_AS6UrU-ycUtlnYqoBF76lORg8,4160
    +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=9gm5kpC0ScbDCWobeCrh5LDqS8HgU8FNhmk5v8qQ5Bs,16582
    +pip/_vendor/urllib3/contrib/securetransport.py,sha256=vBDFjSnH2gWa-ztMKVaiwW46K1mlDZKqvo_VAonfdcY,32401
    +pip/_vendor/urllib3/contrib/socks.py,sha256=nzDMgDIFJWVubKHqvIn2-SKCO91hhJInP92WgHChGzA,7036
    +pip/_vendor/urllib3/exceptions.py,sha256=D2Jvab7M7m_n0rnmBmq481paoVT32VvVeB6VeQM0y-w,7172
    +pip/_vendor/urllib3/fields.py,sha256=kroD76QK-GdHHW7f_AUN4XxDC3OQPI2FFrS9eSL4BCs,8553
    +pip/_vendor/urllib3/filepost.py,sha256=vj0qbrpT1AFzvvW4SuC8M5kJiw7wftHcSr-7b8UpPpw,2440
    +pip/_vendor/urllib3/packages/__init__.py,sha256=h4BLhD4tLaBx1adaDtKXfupsgqY0wWLXb_f1_yVlV6A,108
    +pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/__pycache__/six.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/backports/makefile.py,sha256=005wrvH-_pWSnTFqQ2sdzzh4zVCtQUUQ4mR2Yyxwc0A,1418
    +pip/_vendor/urllib3/packages/six.py,sha256=adx4z-eM_D0Vvu0IIqVzFACQ_ux9l64y7DkSEfbxCDs,32536
    +pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py,sha256=ywgKMtfHi1-DrXlzPfVAhzsLzzqcK7GT6eLgdode1Fg,688
    +pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-39.pyc,,
    +pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=rvQDQviqQLtPJB6MfEgABnBFj3nXft7ZJ3Dx-BC0AQY,5696
    +pip/_vendor/urllib3/poolmanager.py,sha256=iWEAIGrVNGoOmQyfiFwCqG-IyYy6GIQ-jJ9QCsX9li4,17861
    +pip/_vendor/urllib3/request.py,sha256=hhoHvEEatyd9Tn5EbGjQ0emn-ENMCyY591yNWTneINA,6018
    +pip/_vendor/urllib3/response.py,sha256=eo1Sfkn2x44FtjgP3qwwDsG9ak84spQAxEGy7Ovd4Pc,28221
    +pip/_vendor/urllib3/util/__init__.py,sha256=bWNaav_OT-1L7-sxm59cGb59rDORlbhb_4noduM5m0U,1038
    +pip/_vendor/urllib3/util/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/connection.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/queue.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/request.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/response.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/retry.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/timeout.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/url.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/__pycache__/wait.cpython-39.pyc,,
    +pip/_vendor/urllib3/util/connection.py,sha256=NsxUAKQ98GKywta--zg57CdVpeTCI6N-GElCq78Dl8U,4637
    +pip/_vendor/urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497
    +pip/_vendor/urllib3/util/request.py,sha256=C-6-AWffxZG03AdRGoY59uqsn4CVItKU6gjxz7Hc3Mc,3815
    +pip/_vendor/urllib3/util/response.py,sha256=_WbTQr8xRQuJuY2rTIZxVdJD6mnEOtQupjaK_bF_Vj8,2573
    +pip/_vendor/urllib3/util/retry.py,sha256=3wbv7SdzYNOxPcBiFkPCubTbK1_6vWSepznOXirhUfA,15543
    +pip/_vendor/urllib3/util/ssl_.py,sha256=N7gqt2iqzKBsWGmc61YeKNSPri6Ns2iZ_MD5hV2y8tU,14523
    +pip/_vendor/urllib3/util/timeout.py,sha256=3qawUo-TZq4q7tyeRToMIOdNGEOBjOOQVq7nHnLryP4,9947
    +pip/_vendor/urllib3/util/url.py,sha256=S4YyAwWKJPjFFECC7l9Vp9EKqRH1XAb-uQFANn1Tak0,13981
    +pip/_vendor/urllib3/util/wait.py,sha256=k46KzqIYu3Vnzla5YW3EvtInNlU_QycFqQAghIOxoAg,5406
    +pip/_vendor/vendor.txt,sha256=bWUiaRjMJhuUsqFZHEJkBH_6lJ_Avl9cOyszcI74IHs,437
    +pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579
    +pip/_vendor/webencodings/__pycache__/__init__.cpython-39.pyc,,
    +pip/_vendor/webencodings/__pycache__/labels.cpython-39.pyc,,
    +pip/_vendor/webencodings/__pycache__/mklabels.cpython-39.pyc,,
    +pip/_vendor/webencodings/__pycache__/tests.cpython-39.pyc,,
    +pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-39.pyc,,
    +pip/_vendor/webencodings/labels.py,sha256=4AO_KxTddqGtrL9ns7kAPjb0CcN6xsCIxbK37HY9r3E,8979
    +pip/_vendor/webencodings/mklabels.py,sha256=GYIeywnpaLnP0GSic8LFWgd0UVvO_l1Nc6YoF-87R_4,1305
    +pip/_vendor/webencodings/tests.py,sha256=OtGLyjhNY1fvkW1GvLJ_FV9ZoqC9Anyjr7q3kxTbzNs,6563
    +pip/_vendor/webencodings/x_user_defined.py,sha256=yOqWSdmpytGfUgh_Z6JYgDNhoc-BAHyyeeT15Fr42tM,4307
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/REQUESTED b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/REQUESTED
    new file mode 100644
    index 0000000..e69de29
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/WHEEL b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/WHEEL
    new file mode 100644
    index 0000000..6d38aa0
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/WHEEL
    @@ -0,0 +1,6 @@
    +Wheel-Version: 1.0
    +Generator: bdist_wheel (0.35.1)
    +Root-Is-Purelib: true
    +Tag: py2-none-any
    +Tag: py3-none-any
    +
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/entry_points.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/entry_points.txt
    new file mode 100644
    index 0000000..d48bd8a
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/entry_points.txt
    @@ -0,0 +1,5 @@
    +[console_scripts]
    +pip = pip._internal.cli.main:main
    +pip3 = pip._internal.cli.main:main
    +pip3.8 = pip._internal.cli.main:main
    +
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/top_level.txt b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/top_level.txt
    new file mode 100644
    index 0000000..a1b589e
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip-20.2.3.dist-info/top_level.txt
    @@ -0,0 +1 @@
    +pip
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__init__.py
    new file mode 100644
    index 0000000..9fb68d4
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__init__.py
    @@ -0,0 +1,18 @@
    +from pip._internal.utils.typing import MYPY_CHECK_RUNNING
    +
    +if MYPY_CHECK_RUNNING:
    +    from typing import List, Optional
    +
    +
    +__version__ = "20.2.3"
    +
    +
    +def main(args=None):
    +    # type: (Optional[List[str]]) -> int
    +    """This is an internal API only meant for use by pip's own console scripts.
    +
    +    For additional details, see https://github.com/pypa/pip/issues/7498.
    +    """
    +    from pip._internal.utils.entrypoints import _wrapper
    +
    +    return _wrapper(args)
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__main__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__main__.py
    new file mode 100644
    index 0000000..7c2505f
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__main__.py
    @@ -0,0 +1,26 @@
    +from __future__ import absolute_import
    +
    +import os
    +import sys
    +
    +# Remove '' and current working directory from the first entry
    +# of sys.path, if present to avoid using current directory
    +# in pip commands check, freeze, install, list and show,
    +# when invoked as python -m pip 
    +if sys.path[0] in ('', os.getcwd()):
    +    sys.path.pop(0)
    +
    +# If we are running from a wheel, add the wheel to sys.path
    +# This allows the usage python pip-*.whl/pip install pip-*.whl
    +if __package__ == '':
    +    # __file__ is pip-*.whl/pip/__main__.py
    +    # first dirname call strips of '/__main__.py', second strips off '/pip'
    +    # Resulting path is the name of the wheel itself
    +    # Add that to sys.path so we can import pip
    +    path = os.path.dirname(os.path.dirname(__file__))
    +    sys.path.insert(0, path)
    +
    +from pip._internal.cli.main import main as _main  # isort:skip # noqa
    +
    +if __name__ == '__main__':
    +    sys.exit(_main())
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..f75ed00273fd60ff99faee153c45c9f976331f05
    GIT binary patch
    literal 701
    zcmZuv&2AGh5Vm(WX$Y-^P)}U&4Mierlp;YDjXnbVa`cOT!w7wk;G(4f9)SB11c3xD
    zQH7@%BZ(aWoRmqGPSYxzW*CWd+7WA_Bf6jPv@5bVXwdrs=g(G+(%&RrO0o#S`g@elRan%syf78DjpY<-QssRuJ-u^#`)=PJAHp(}r_f2Q(zXfG2WFjlCyM}7BOFS7OiMM3aAEXTzD>Of
    za%g#R#OI(OiebP}1;%pQv{}WKj?cws5KwnLaS;vhWU$s=`=6lDSf%GKUS8Y%1LBKv
    qu|Rv7v13pIH_zCz(o*1sa4GKU?Nmn8rMw@f=;H|1lMG);v-CH9%fIFT
    
    literal 0
    HcmV?d00001
    
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..a5c53c1292e3cb4c378b05d25b1922dfee2b0bdc
    GIT binary patch
    literal 545
    zcmYjNJ#X7E5GARPD2|6
    zzr?ju{zAL-sH8v%@Q&~C-rXa`VlgAQe(e5;?+GElBKUu02;Sp%-!MobX-hgZ{(57)fHVu!es76%^d*y!;dxf`C_`R?0v5%{ot?0!@
    z_fk|JbTf3&dn7efpTr8&3ibx9dZEq1gY(>2{RoY(Uf>G)r>a#C5pH#VQ@*X7^00b+
    og{#nL_3t-Ia|p$|PRpT%TZ(2}bTnrLE%IwRPmAn|p0kMm0s(QKhX4Qo
    
    literal 0
    HcmV?d00001
    
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__init__.py
    new file mode 100644
    index 0000000..264c2ca
    --- /dev/null
    +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__init__.py
    @@ -0,0 +1,17 @@
    +import pip._internal.utils.inject_securetransport  # noqa
    +from pip._internal.utils.typing import MYPY_CHECK_RUNNING
    +
    +if MYPY_CHECK_RUNNING:
    +    from typing import Optional, List
    +
    +
    +def main(args=None):
    +    # type: (Optional[List[str]]) -> int
    +    """This is preserved for old console scripts that may still be referencing
    +    it.
    +
    +    For additional details, see https://github.com/pypa/pip/issues/7498.
    +    """
    +    from pip._internal.utils.entrypoints import _wrapper
    +
    +    return _wrapper(args)
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..819f0777d9d076347ab917f7c716b5dd8d6d1632
    GIT binary patch
    literal 750
    zcmZuvJ&zMH5VdzV$r9a32vO1Ck|44!K)@k{kiw^cfPgD5&B|HNCBECWjqMyPIvU_l
    z(9rXjwxyz?hlX*2PFLK>9{Y`EJnz{S+uK8e_U-Vm_)Q7<8Nq2w5!}b=Z=#b#(wfXD
    zu1TFV=`5uLQg;P$T4zmf)@%B+J|(g*Guit}X9G#!kkQ~5){^Onb{Agkz1k}$PaaO5
    zm9KZF)2GwNqom7U+MtXUb(cL?K2*r##1h=aIl<{KqLUPRlZJkxlpNDz@+EdiKYo9^
    zRG#6m4m`LckZj*LW@^bQqrItt`N}C9JPS(^SR-2IgQ{yb2j*ZO9Oz2v#gL(?Pz=|b
    zM|gygQf=rl2|=jZUt=DCEkm&W4(AIMmaBPDnTFfe3T~C<%KH^~e(T20?P7h9i<&2R+%Y<8Ozz^bbjQ#n{cTuC~)lz7&
    zFj@-!z*MUSi!o?`zY}9z#_$0RHm1j-5*n)yhF|LVIgm
    z=u*t>E}c~nT8l5@;`4X?{~-QH7dx)It*_&hI~P;)yN!{n8t%nA_mN^ww~~yWPyYcz
    C3Dk)I
    
    literal 0
    HcmV?d00001
    
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..e704418cb021da800f48a39c6f89d847c73bb061
    GIT binary patch
    literal 7585
    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
    
    literal 0
    HcmV?d00001
    
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..5772d185f48b1f98f0539d7195b6466d490bf8e4
    GIT binary patch
    literal 9142
    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
    T?5sTli0J=+m!6t2?Zf{A)GHHl
    
    literal 0
    HcmV?d00001
    
    diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc
    new file mode 100644
    index 0000000000000000000000000000000000000000..23adb3b21f7d65d5fd9bdeb255aeebd1b3bb984c
    GIT binary patch
    literal 14576
    zcmbVTOLG)icCM_KNqMLq9s~bi_h{sG*Fd%S@+}%a0GF3{*$}DbX
    zAyi1?h@rz{FJ@(L>uD^Qp2ji>vbGd&bkiCikrcwM_K9@6d3prCW^N!ZaFXT1e
    z7o0+?xKM1B7D}!1LRp>_ouO7`p(6JsXSg-8FrwwG5%n)&Mi)j~+ZVRCb}Z~@
    z?OfQ|8e15XwnNUY*6xMfa$j-wwDvCSZS7mwC-5-(+`q72?nj&ht%C~(<$jxUsC9VZ
    zF#6xG{sH}uI!9V>Exe`W+)+T=0UafDEobg9cYc#IcUo^hFDx7bJZA0!yvsTUSO>h@
    z+yi)zz{dgaHTMDDC-4ct`^^J@4+wk`@Imts;6v7NeE$yM!{!mdM+80v_$~7&;G@$1
    zG~l<*V}OqddAN`BT6@wRH6V
    zi8*~YH$KxnG554#wFBF8eZAvbrXDOSuhU%iI)UCc8jp;o^&Po!Tnl#^i@xV{0;_Jf
    z+MWu=3t_3TY}jr%)WkRK#nrH^thQwYXgq8OmO^Ry0CwN}A7G~VluyRWVH=MBeh3yAn@rvC*J#Rf_FQZrROxcSaA~>E8D|alv4OFdg=#KTc
    zWBF`Uxc$ouY}@eMCA--+ly4~xdc*Yt!*STqR#wY$*|=1{ZurY`vy7Vt=a#)R0MjV`
    z7%D5bpkc@5O>LoI=0UGQSh;Pt=alkPclTw(xAeB%)~$quwU@=PQm?y4%c|GIk$S!5
    znH`7sqxJgZj^V^_hU#_GYt-v6bLt3&k?Q+zChq}pe-b2hji%?C#^e>R(IMV==vkAG
    zjS2jju%5O-+30b5H2@9jZRI_*8o^`)PFU`fNylC!xa_$Xs-I5!Sc!>vIsHlCJXyD0
    zERbtBlPUIowY{qL0UzSzL)hdhn!s;Nq}{W!6dh%7-6C?GxMsPQGMpZ@blcUVHH#b8
    z)c%ATJ-Vp3`0OaEV<-t)gx?sgT`@;wusXF7E3>0)w+ZBgd!+3ah`kL&PG%4z{Kkmw
    zx#wnwiwJgRQ`9rl+dyN6osje6OR>Q#C6JbZ?OiuN$r@Up+7(C#u-c
    z^`y(QXOcnBj6cwo6?7CN+`Q^oI;2VFqox)7$VZ&7eilRNzTL7NLpiHNSo-KqX1pp7
    z8U;`>xp+_V&Z3AwS<}{X_1sz>wPtD{XTJM)@)?OY1<_@+bWUYTpV0@*HyR?-Hkqx6
    zoGE>N+0rRubZ-fA-0IPH(c(HpD`G_?V=uhUHVju^v^HI&cwTH;g9I)Ty>NTqzz~Z?
    z-*T4Jam-VlV3mqB*}fEtwrdCV`iGcyQaYO2i`-i7
    zA*lH<|1AY`zSezD*6G=LZ`Mm*2a87DBGQQ(nbM!F1LHEDw~Qb{mx#aA)Jgn#6?MpJ
    zd|R(y!aKi$Dpx9Ml^^mybPo-}UfR}Pmem3Fb&%B|R)<-=!-^X+Gl)=1WSou{@1gi*
    zR28jQsf;K-YoPR9B*!$$W;$wbmUU`gaFulE&h~B2K9cC*0oM!21`*8yPxm4D{H0ah
    zz~)I6=oYeO90=VYQe=224#IDY`QePR_OIHGb(`8el80CN@oy&Tgvwa=8jX(fVX-KY
    zIDJU_`VxB59ox04TTI|xAbc}30m5%Qfg_ipkMBc`L+{Nv$}-H=Sm{`1x0aoO>`%`R
    z^iF#s@FpNkE&WOKA-AuQSYNvB>qgX*NQzidSmsu9AshW7GZ(^dJeQHnhB@oCT9AC*
    z^I6oN*eZZA12cp2+<+37M#Pj{BrLQAq0a%C`x)d2zcF&7F=i2Oe3C^ElvS3H&DAa8
    z4ly(Y#|d(QZm4EtS8aji1h87jU`hCmvD|sLvv^y1RD=@qb^Ed-G)!T7n
    zvFjXI^2C0TUqap}XlzfdZh`S+jA0@}5*UwrmuL@wJ~wEey)yKh+!Fj@v-r(0tQOcS
    zv|4@x&9Oksr4sJ1S;2~@9>F?wflF$d?z=uef14=BPb03WHIk7yHt$O)1r)!AN|bnR
    ztw2>SY8^`bA^h=@;?p=Yk4#OJTI^)tGX5xgVI{=dHzKi-{jW{-xlaY1G_@E
    zks1Owpek*{vAdQ&7-N0K4wiNO0VOl!%>&v&gd*SQLE2GdCHXzGp%xCs-@uToBp)b*
    z9dhBY?3H@UZ-zs#q>La?Vae~n*-+>4UX<5(G18G2_}~Lp{PZQ*6^IS+9OQec(KabS`6@x}
    zG~Q-(tYAaEyUI`5M=sxed(*K+hr^_(y&T$FjfdOgX*Vp#Nv19e{bLLfc`l?~6D85=
    z`5iljo^QMp_ug=yfGNyBTDB~Qx{;P%_dxuVHn-)7rsMOil*8x1=2yfh*3^XG7@M(s
    zE_j1s04u_%yF2qF!e%QZE@IGMXOJMg0SO{F%|<$^zO9}g=$h=k89Nj=TAYBRysJ(9h2LA|l4}X6|lI{RFRQ&_i554oge6<(OfK
    zB0NYo|4-3EO|zO|)o<#HnD{N83%}>`-16O{gVts9O-?y0Xi{!uO?}8!NX*FGpQNb>
    z;fKk?=7+$7hFMyI=63@=fXYvN6d3%+)jF_0MRZ@meltn&7riMIwb38CZ*4M#$lh0H
    z*>#T+J)@p(!=EO7!Kg1+%8Ji6=XEZ?*1T>GL9vz1iaCtGL*|IN4Sy@YT>uQ
    zC5V)Mkx!oIujIZu`(=JTzg}3=UO@RjED~Sbmm*_it#BpxT;}lj^qQu=Hiu{id<*?$
    zR3^Z*y6V_ep7_;dP!PuuF!-}5bp>z>+ju1Wh)8wnB4
    zidd-VgXOyTimuVXQ4#7PTJ`!rp+z!e|BjE|*T#o8
    zx30JfQKUo$`eTBOvPRLT|eOm%2allCW5%+T`kahASeJ8fdM9js;hUi{g6>VoXAm3pw
    zEC~{b&_RATJdD4ppLM(yOCh2Y^}^1_&!Jl}J7_z=7_of!41zu$9cGvsR=^?Cu8c+I
    zfI4p5hF@eR;Rrtp?jpDd)O6EAzDL1G@}Y{)A;j{?((ljSP5UtM+BGe=Ve>U;V_L`n
    z(0i%sGY+tMgbw`JOOJF9&9d0x<%CQNX^Iwthkjr;A|ge`O++>BQwB@@$e+^*bH;Jd
    zuk9F!U@d|qdfTHh2)7FM6+#-TDUQATM-(=N#rMdL4P`Vw!
    zwMJYe?I&1m+YsEChw%hlXDl*SfT*->sn~L@DF%NwY-q|i5hpuv6WLWSv~P1%K;wDi
    zA|%coiIeLF0t^V3fQ=A|LO3}~O@tC-++$kDV{)-%#-_DofGu?-2_4D0NeYFr^EOa!
    zBe@bVRA5?uL)nu2i|B6kCK!>E>j=^(q|S^JN6LS6T@WLA;yvnHmUF^==}^Sl20jq#
    zkCHGOSf$-@xCq>5wD_5XaImeg5cT9`ou8aVbUf{=TV`(vF6YJ440j=Ukzy|q`auFy
    zeSyXJSCmLrZtLzyyZ^J64X36e!6&VPBk;h8Lq43hP^$`ZSBa4>?gp4w9c)G=5
    zI()v{GmK!HiFLaH14>chBiiS*O4L{&#t~idIIZ!!l$ACvbaqu=-9T_%dlcP@pOTc0
    zBM)8@0
    z7^v!s*!HDx_x*;?31qzY?>jS*;#q-+=N__6>;(z|BXMexiFN}x5EK3?gj#E25xH

    (Vv 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..103a7f8f1865feddd386bc0455a7ce89227974c8 GIT binary patch literal 687 zcmZuvy^ho{5Vn)-534%~Au1Z&uEfR#NN@<9kd|Ko3j(gVZC2iRZeuQqjmLWsbkM*{ z(9rV$yi8guUI7}$yXbTUqs(N!k>}?dTkY}Le$B_(i|6mv3~XAuc%?+c&zboz>o2frwXjd^$J+2M=B#q`nr>669FgW2ry z?BO`+hRX3f;_PR7E=yM(r z{0V4rWWBJJ7KLrlRv@tO#sw6iR6*3L6&Orai6saRN8q6;OtTsZ5)<;##(6*`l-6cD zpU@DLsqmUW08xhE@V1mI6Uy~6FKjJc>y&iHNrSisls9ke-O4wGeB?wM7%tv<V`dCwN> znvyAKLB3TJ`c2?Hbu!zqYXe|dIB(xT5opv1C(xY8$}A%+ZF4=}lV}1=oGOmhiWo#P zXCum5nI?B_mqlkllVC=M4R#VAU(cA&#{KTf|7eBQ(QO{GCN7eFQ$J1)q}jm literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a79cca6b1b3df04dc7c4b090f18307fe8afbc05 GIT binary patch literal 3790 zcmb7H&5snv74Pcqnd$l5Wx)<0OX-l0Wg>PKoJ1iA6Jmp69I{4W$6_SZ>FuuB>7_r0 zsv2O29tn9Y->h(rlmq0JoU9ytj`9cOm_uK4Nu=Czi5%JF_o`=SXAvASqp7Z{e)a0T z-+T3|H-5X_F!1^9-T(Ukc*8LMPJ`u-jlpI78IVd z@wDPfQtel@j2+h!r|%^7eqE0%aU*H=n@OwR(&K8}PCEUL?$_d#WVOG_48AHJLyr?5 zN!I#n$CkW zRnc4;VD*t}sZ@R(`zp%Pt3qS~lWV`cedD(G&bwFN`Gt4$!>z4rTR-nwh4r&^uW;TU zk)$6N_Vq}rfRSbM5A`kl-oZ}-e?u9$q2Nw-QkfwO%}+4CV$9f#!6|F&&|(wv6dy^y zlW20|&f1hg1ygL-oSDBem~jsg!cw*ca~hG1$o4egf)*AnsYI0ScxrFNCA>F#XA3)0 zJo$qmjzHkc+2)6m3%MB#{nX#d($L?$k_BUe1(O-yyytJ=vB4jXxQNQ>(VpT`c_WeS z@<45t!wsI^-;ASe8Veu2pdM^cDN9|3#RmLuRYUVf|&rAfs+OmRLFK16{ z%6VFUW;|n4mQyUvtx4y=OPzg>`= zDEan3E9ejhX)ync12G4a0V;dmH_@-;w)izcn=?8XpP9coIjyS7(`sJD+uBFq9UI4C zW-%jOJ!i=G&LaEidmF#J_Ouq-kaJQyw5ASpeVSL~rwExfX5w*f$j8bFD>L>n3#(Hj zy_h@Vi?EhE_YLvIT)$ER+8s$*?`GhaK$5o-lD-IeC4zo?FCfpP94|WIHE}X+t&g z#tbs@;zH)gLZ)%WxN~{hoU~LkZ_dEe9~tQx+fc11Y%eeISgcxuAI)cMf9-7}HP0GP z*uBfS1GH*DtEGEKcMPO8n0DI9TjT}o`%~DQwn|^79o5boc`NVa?U{|#KIXp-PP?A@ zp{s`6UF1`bvfQOa?KK)MJ~w;;BF2%I7xb9s#u6FGM?X6EV|T-S*OyuaCWTA61!Cc$ zd*kX2mju8O0hi#kou9f}S;`kmNYUbxa6esAGxTM;u3Yd*90iez_gp#RK{SYX*jp%A z*z9SWH{9!B7h-WY8z$iza)0t46ObWs6Ax&w(E$_U(qfg)XkpI~&O0ra|Zn2qC* z0-`;R`Hmm#xx_jy5yKfbLv+Q12=9GYU#5$FPcM38!IHhw9f&M>0kboQr1w4D5AvKi z4htPpd0?yN8rKIR`ksqpB#S>lg1G>h+&;7u*)|?&=eCK>T-;HS$Wj9Kkl=zfwd2U8 z%q@$Fa&5_UrDvz-=Hg;g!|q|DODw2vxDQCP+0<%m9ZLZoX4%@>~s3o zq@_*WD5-vO?Mfv5?HHcD%6f=XFi-9b6lti)Qlvqj@1Qg+(v{2A|mx>v}v#i`?Z}QfwmIPUfdl}T{r~7HJd=%mAx5O0rRHkp4 zupM!NFr`bss78{wx-g@xu(J_QMTMsA7{p#!C^-a`1w|EvllV$kNuoxpsdN(GM$=s> z8V7t{9Mn6SRSQQ)AM~i}2F-Rw8+ad7AVr;qTC89Lz%?pYmWRMA4?)q<+~%S58R2ko z9lN5rL=ob{s^lPNRWYn>E@tb4+mwHgr!E4bK~lLDt;!1Qq_GM~#lwH_f+z)Y5dX%ssG*Wt7m_+pw2w% Rm93g(Y>ruu<1{;!{{cK%vq7(C?x9z3(j@j*mAC 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 Y;Of9FyUTbg?ge+oExKpj_uc6~0^?W)qW}N^ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/build_env.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/build_env.py new file mode 100644 index 0000000..28d1ad6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/build_env.py @@ -0,0 +1,241 @@ +"""Build Environment used for isolation during sdist building +""" + +import logging +import os +import sys +import textwrap +from collections import OrderedDict +from distutils.sysconfig import get_python_lib +from sysconfig import get_paths + +from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet + +from pip import __file__ as pip_location +from pip._internal.cli.spinners import open_spinner +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import TracebackType + from typing import Tuple, Set, Iterable, Optional, List, Type + from pip._internal.index.package_finder import PackageFinder + +logger = logging.getLogger(__name__) + + +class _Prefix: + + def __init__(self, path): + # type: (str) -> None + self.path = path + self.setup = False + self.bin_dir = get_paths( + 'nt' if os.name == 'nt' else 'posix_prefix', + vars={'base': path, 'platbase': path} + )['scripts'] + # Note: prefer distutils' sysconfig to get the + # library paths so PyPy is correctly supported. + purelib = get_python_lib(plat_specific=False, prefix=path) + platlib = get_python_lib(plat_specific=True, prefix=path) + if purelib == platlib: + self.lib_dirs = [purelib] + else: + self.lib_dirs = [purelib, platlib] + + +class BuildEnvironment(object): + """Creates and manages an isolated environment to install build deps + """ + + def __init__(self): + # type: () -> None + temp_dir = TempDirectory( + kind=tempdir_kinds.BUILD_ENV, globally_managed=True + ) + + self._prefixes = OrderedDict(( + (name, _Prefix(os.path.join(temp_dir.path, name))) + for name in ('normal', 'overlay') + )) + + self._bin_dirs = [] # type: List[str] + self._lib_dirs = [] # type: List[str] + for prefix in reversed(list(self._prefixes.values())): + self._bin_dirs.append(prefix.bin_dir) + self._lib_dirs.extend(prefix.lib_dirs) + + # Customize site to: + # - ensure .pth files are honored + # - prevent access to system site packages + system_sites = { + os.path.normcase(site) for site in ( + get_python_lib(plat_specific=False), + get_python_lib(plat_specific=True), + ) + } + self._site_dir = os.path.join(temp_dir.path, 'site') + if not os.path.exists(self._site_dir): + os.mkdir(self._site_dir) + with open(os.path.join(self._site_dir, 'sitecustomize.py'), 'w') as fp: + fp.write(textwrap.dedent( + ''' + import os, site, sys + + # First, drop system-sites related paths. + original_sys_path = sys.path[:] + known_paths = set() + for path in {system_sites!r}: + site.addsitedir(path, known_paths=known_paths) + system_paths = set( + os.path.normcase(path) + for path in sys.path[len(original_sys_path):] + ) + original_sys_path = [ + path for path in original_sys_path + if os.path.normcase(path) not in system_paths + ] + sys.path = original_sys_path + + # Second, add lib directories. + # ensuring .pth file are processed. + for path in {lib_dirs!r}: + assert not path in sys.path + site.addsitedir(path) + ''' + ).format(system_sites=system_sites, lib_dirs=self._lib_dirs)) + + def __enter__(self): + # type: () -> None + self._save_env = { + name: os.environ.get(name, None) + for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH') + } + + path = self._bin_dirs[:] + old_path = self._save_env['PATH'] + if old_path: + path.extend(old_path.split(os.pathsep)) + + pythonpath = [self._site_dir] + + os.environ.update({ + 'PATH': os.pathsep.join(path), + 'PYTHONNOUSERSITE': '1', + 'PYTHONPATH': os.pathsep.join(pythonpath), + }) + + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None + for varname, old_value in self._save_env.items(): + if old_value is None: + os.environ.pop(varname, None) + else: + os.environ[varname] = old_value + + def check_requirements(self, reqs): + # type: (Iterable[str]) -> Tuple[Set[Tuple[str, str]], Set[str]] + """Return 2 sets: + - conflicting requirements: set of (installed, wanted) reqs tuples + - missing requirements: set of reqs + """ + missing = set() + conflicting = set() + if reqs: + ws = WorkingSet(self._lib_dirs) + for req in reqs: + try: + if ws.find(Requirement.parse(req)) is None: + missing.add(req) + except VersionConflict as e: + conflicting.add((str(e.args[0].as_requirement()), + str(e.args[1]))) + return conflicting, missing + + def install_requirements( + self, + finder, # type: PackageFinder + requirements, # type: Iterable[str] + prefix_as_string, # type: str + message # type: str + ): + # type: (...) -> None + prefix = self._prefixes[prefix_as_string] + assert not prefix.setup + prefix.setup = True + if not requirements: + return + args = [ + sys.executable, os.path.dirname(pip_location), 'install', + '--ignore-installed', '--no-user', '--prefix', prefix.path, + '--no-warn-script-location', + ] # type: List[str] + if logger.getEffectiveLevel() <= logging.DEBUG: + args.append('-v') + for format_control in ('no_binary', 'only_binary'): + formats = getattr(finder.format_control, format_control) + args.extend(('--' + format_control.replace('_', '-'), + ','.join(sorted(formats or {':none:'})))) + + index_urls = finder.index_urls + if index_urls: + args.extend(['-i', index_urls[0]]) + for extra_index in index_urls[1:]: + args.extend(['--extra-index-url', extra_index]) + else: + args.append('--no-index') + for link in finder.find_links: + args.extend(['--find-links', link]) + + for host in finder.trusted_hosts: + args.extend(['--trusted-host', host]) + if finder.allow_all_prereleases: + args.append('--pre') + if finder.prefer_binary: + args.append('--prefer-binary') + args.append('--') + args.extend(requirements) + with open_spinner(message) as spinner: + call_subprocess(args, spinner=spinner) + + +class NoOpBuildEnvironment(BuildEnvironment): + """A no-op drop-in replacement for BuildEnvironment + """ + + def __init__(self): + # type: () -> None + pass + + def __enter__(self): + # type: () -> None + pass + + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None + pass + + def cleanup(self): + # type: () -> None + pass + + def install_requirements( + self, + finder, # type: PackageFinder + requirements, # type: Iterable[str] + prefix_as_string, # type: str + message # type: str + ): + # type: (...) -> None + raise NotImplementedError() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..07db948 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cache.py @@ -0,0 +1,346 @@ +"""Cache Management +""" + +import hashlib +import json +import logging +import os + +from pip._vendor.packaging.tags import interpreter_name, interpreter_version +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url + +if MYPY_CHECK_RUNNING: + from typing import Optional, Set, List, Any, Dict + + from pip._vendor.packaging.tags import Tag + + from pip._internal.models.format_control import FormatControl + +logger = logging.getLogger(__name__) + + +def _hash_dict(d): + # type: (Dict[str, str]) -> str + """Return a stable sha224 of a dictionary.""" + s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + return hashlib.sha224(s.encode("ascii")).hexdigest() + + +class Cache(object): + """An abstract class - provides cache directories for data from links + + + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) + """ + + def __init__(self, cache_dir, format_control, allowed_formats): + # type: (str, FormatControl, Set[str]) -> None + super(Cache, self).__init__() + assert not cache_dir or os.path.isabs(cache_dir) + self.cache_dir = cache_dir or None + self.format_control = format_control + self.allowed_formats = allowed_formats + + _valid_formats = {"source", "binary"} + assert self.allowed_formats.union(_valid_formats) == _valid_formats + + def _get_cache_path_parts_legacy(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + + Legacy cache key (pip < 20) for compatibility with older caches. + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_cache_path_parts(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = {"url": link.url_without_fragment} + if link.hash_name is not None and link.hash is not None: + key_parts[link.hash_name] = link.hash + if link.subdirectory_fragment: + key_parts["subdirectory"] = link.subdirectory_fragment + + # Include interpreter name, major and minor version in cache key + # to cope with ill-behaved sdists that build a different wheel + # depending on the python version their setup.py is being run on, + # and don't encode the difference in compatibility tags. + # https://github.com/pypa/pip/issues/7296 + key_parts["interpreter_name"] = interpreter_name() + key_parts["interpreter_version"] = interpreter_version() + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = _hash_dict(key_parts) + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_candidates(self, link, canonical_package_name): + # type: (Link, str) -> List[Any] + can_not_cache = ( + not self.cache_dir or + not canonical_package_name or + not link + ) + if can_not_cache: + return [] + + formats = self.format_control.get_allowed_formats( + canonical_package_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + candidates = [] + path = self.get_path_for_link(link) + if os.path.isdir(path): + for candidate in os.listdir(path): + candidates.append((candidate, path)) + # TODO remove legacy path lookup in pip>=21 + legacy_path = self.get_path_for_link_legacy(link) + if os.path.isdir(legacy_path): + for candidate in os.listdir(legacy_path): + candidates.append((candidate, legacy_path)) + return candidates + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + raise NotImplementedError() + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached items in for link. + """ + raise NotImplementedError() + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(SimpleWheelCache, self).__init__( + cache_dir, format_control, {"binary"} + ) + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + parts = self._get_cache_path_parts_legacy(link) + assert self.cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached wheels for link + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were + not unique. E.g. ./package might have dozens of installs done for it + and build a version of 0.0...and if we built and cached a wheel, we'd + end up using the same wheel even if the source has been edited. + + :param link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + assert self.cache_dir + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + candidates = [] + + if not package_name: + return link + + canonical_package_name = canonicalize_name(package_name) + for wheel_name, wheel_dir in self._get_candidates( + link, canonical_package_name + ): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if canonicalize_name(wheel.name) != canonical_package_name: + logger.debug( + "Ignoring cached wheel %s for %s as it " + "does not match the expected distribution name %s.", + wheel_name, link, package_name, + ) + continue + if not wheel.supported(supported_tags): + # Built for a different python/arch/etc + continue + candidates.append( + ( + wheel.support_index_min(supported_tags), + wheel_name, + wheel_dir, + ) + ) + + if not candidates: + return link + + _, wheel_name, wheel_dir = min(candidates) + return Link(path_to_url(os.path.join(wheel_dir, wheel_name))) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory + """ + + def __init__(self, format_control): + # type: (FormatControl) -> None + self._temp_dir = TempDirectory( + kind=tempdir_kinds.EPHEM_WHEEL_CACHE, + globally_managed=True, + ) + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + +class CacheEntry(object): + def __init__( + self, + link, # type: Link + persistent, # type: bool + ): + self.link = link + self.persistent = persistent + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(WheelCache, self).__init__( + cache_dir, format_control, {'binary'} + ) + self._wheel_cache = SimpleWheelCache(cache_dir, format_control) + self._ephem_cache = EphemWheelCache(format_control) + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link_legacy(link) + + def get_path_for_link(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link): + # type: (Link) -> str + return self._ephem_cache.get_path_for_link(link) + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + cache_entry = self.get_cache_entry(link, package_name, supported_tags) + if cache_entry is None: + return link + return cache_entry.link + + def get_cache_entry( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Optional[CacheEntry] + """Returns a CacheEntry with a link to a cached item if it exists or + None. The cache entry indicates if the item was found in the persistent + or ephemeral cache. + """ + retval = self._wheel_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=True) + + retval = self._ephem_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=False) + + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..827d6dcc9644ceba81b43e96ff7efa7d34437f1a GIT binary patch literal 308 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c8a9eb06cf511d04250a8d1f64006febb28a29d GIT binary patch literal 1388 zcmZuwOK;Oa5Z+xoj^ie!yd}h;T##~TTpmI~LI^3m3L-)fK(&xo#=C&QFLpOoTlGXL zaYCHA!I59XeXpGO3vgk^P9POsYiDL>J+{>+@pp%iA9z??E*x&a*3W?m zB4|c(8c~We%UJG2PAxl`o42ACC9ZoHmXuquO5?&L7;tex1L~#M zfk_fkK_d2nyrhB(w(m!dbcG|_eKMs{3(MIjk@tbPBx*weZF_uJ=8(0RXOM1h6bYR< zMTRm4!L3(;7-HxinX)hJD_jbYxntZZ0p1b>oU+mFzGqu{Qc0x{D(iEe7OCOfu7JSv z7|5(+3jNyN0o^xhT`jYhz&o5LS*$hZ-^j0z;S(*D4wKi|r?L=nc(Y6T1VGZFr^1H!?9{b6k_fR+yzP@c6ncE)A}RIyG{w0=NJ_9ad=-LZ*OE z5ockNrQp79(>(1#wWGR_`7&(n0U>OedUW^fA?+N@tN*XBKG-GbVGLxCydf&MN8ST; z&`Yv=1vK>2DV)`((CKHuUHGOjy~WA}qA#z57QfBrmZ4SQi<+;)-++ZP8sF|S)dT+u zb5%!>9REvBfMmOifS^WWX~YSbM;Z0$dA7THSY3-|2}6_FLK$%#!q%sNU~Rgt4Qu+s z_UKm%+0jwo`InpR0gzJ7TMTKhfT>-XO$uVTl?s@tgXt>_unJq_!zV0YyXOwe_Mmeg zhq8!s2}2oho|j^hq1@&C-6YPMn;K$;)u@w5FcQpaGm?qSGR_y0If}nv`|6>`mTLsq zr(KWHV5!!Q`aK)W3W5QwecRJ8ZUeq03sI`Uzsrlw+c;C-&WCX@F=?g;W~WMvR|+e( u-t5rA&^U{#jRa#`uY7@XU&mfZweDbzy??DK{W`w$8uu>4&!s0=*ZBjiXjOs$ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7375dbaa515b6ffc1fb5c0bfc90b0f976533f19d GIT binary patch literal 1492 zcmZuwTW=dh6rPz~uh)+4xXz_%QILhmgCFcIAR#~qfkFfo!HrT?RaQu=$?nwIboRo` zIEjtjhZf%82k>S~yzo!>4f6^w{R{BInQ;_aU{`Z4-U<;H3^%i_a0@VZvCVETak!H- zleMJrJr3_A&O_8|O}j@T&8V1kX<70#rYbG+F3Xjeltr4WLH#AX1@*9Ar>C;uW5uF0 zD+{59fHmk?6>*W3oGAu+v!p_@2#}2C2>`8FFbYThLHwh%JP@J~U|maCDcI^xP&;3r z9Gyh_Umom#6&*hs4(|`Y=n?H6rc!DDL1`kRJjN!T>(2vjO0o}2{sCkLX&)o8t!CEH#E=Nli%PD z7w*_mw-%kmow+N!_2NErtry_Ni`tmsq%tEHDDi$G^UAEU*qo6ihN4x6FHI7rwS(j1 z2ge_Ezng0`-Mc@^3z6odE;HHf?#b@u8OytpDP>Sz#FVpXz28rvj8GF(9{)z((vF-+ z?NTwi5LNK5x6@oI%6W82MGhV!UW`WI+cddw4Kdl*o>U2-x^gK}#kBXN5E)gP6jE1t znls5t?Xs&>X>S#Ru0crGqIb9-eRg=LE4Cxd4s(UBB8QvP8hl>F;|!V`z=8$e(mt?0 zyDAyXfQ8G6Vp2t=D9%}|f|c55`9;9fQ=^_0`Fn#80|{^Kmo$D#M@$A~S_Tme3lq?w zLCn*@1U)DxViy8=2bSytLBz)`$0r^kxQ$!51FP#2;BE32EW5jhhrPSH8bwJFN0F{a z(UY+n3l>F!fD3UO2xJx#1vL?y25K3o3qcHSf*3#$gI9(VM#mOT@BVk2i8Zo=D{Ni$ z_P_S5rdh7)vGckSa?k8!O4|O1WJ1)7}5E4Ai7F&+yF)ylbUq-ejg)*G5bo zu3Ey6Y}p|TZy2-5tF7`YWX~M&KhBC|%-JVqLgbr3T+)EN+n+S>%}-p&czyS8-N>TX literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79b44fde53b69a2ecbf546f51939710c6e1852c5 GIT binary patch literal 2279 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 UX86odB6``VeLOc)J2$ucKmD9JjsO4v literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f26bb891e6bbc4d576e174d56c50f1598a2875a GIT binary patch literal 7743 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 D3Q{Fp literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69df27402c026236cae0093a76a039e325526088 GIT binary patch literal 9950 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae41b6a2dad59208cff37d49a632d01741cbe5ad GIT binary patch literal 4837 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 K8VUBi#s3G(qz0G( literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fef0ada6cfb6a3f5a56fc128737643cd6c910a22 GIT binary patch literal 437 zcmYk2PfEi;6vmS@{X=P~3qg-y7wIfq2_n`eVz5pmX)0thOfzE}n`B~UBK8X2K=1-y zDO*>$f^M7`1>a+SZ@!O@8F+X&>?^Wfw_o^8&W~FBFHMSbdH9h8QHUBVi5jS?yayW5 zf);57I?;nRX$KwB3A&`~DdS!x)5=&aM|j0qTnLJyBxAX#ROwgsqgAVhUgJOlj(cSv zq5DDda*DNI%m@F(3nhI$$ z3Ldc`he4k+nn#VD?F8ilWjTAGp#Y6EX!-==XjRJ%OOGcffJcHF84kC2O*zP-44^0# nG*591!Z-q4U{P=sGD7(z+m*f3gpneqXZ5=HKw_Zj>R*2YJ$rld literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000..329de60 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,164 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys +from itertools import chain + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, create_command +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Iterable, List, Optional + + +def autocomplete(): + # type: () -> None + """Entry Point for completion of main and subcommand options. + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + parser = create_main_parser() + subcommands = list(commands_dict) + options = [] + + # subcommand + subcommand_name = None # type: Optional[str] + for word in cwords: + if word in subcommands: + subcommand_name = word + break + # subcommand options + if subcommand_name is not None: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for show and uninstall + should_list_installed = ( + subcommand_name in ['show', 'uninstall'] and + not current.startswith('-') + ) + if should_list_installed: + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = create_command(subcommand_name) + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, cword, subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ````, ``

    `` or ```` + if completion_type: + paths = auto_complete_paths(current, completion_type) + options = [(path, 0) for path in paths] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + flattened_opts = chain.from_iterable(opts) + if current.startswith('-'): + for opt in flattened_opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, + flattened_opts) + if completion_type: + subcommands = list(auto_complete_paths(current, + completion_type)) + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type(cwords, cword, opts): + # type: (List[str], int, Iterable[Any]) -> Optional[str] + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith('-'): + return None + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: + if not opt.metavar or any( + x in ('path', 'file', 'dir') + for x in opt.metavar.split('/')): + return opt.metavar + return None + + +def auto_complete_paths(current, completion_type): + # type: (str, str) -> Iterable[str] + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(`file`, `path` or `dir`)i + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = (x for x in os.listdir(current_path) + if os.path.normcase(x).startswith(filename)) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ```` after option + # complete directories when there is ````, ```` or + # ````after option + if completion_type != 'dir' and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, '') diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/base_command.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..197400a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,265 @@ +"""Base Command class, and related routines""" + +from __future__ import absolute_import, print_function + +import logging +import logging.config +import optparse +import os +import platform +import sys +import traceback + +from pip._internal.cli import cmdoptions +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.cli.parser import ( + ConfigOptionParser, + UpdatingDefaultsHelpFormatter, +) +from pip._internal.cli.status_codes import ( + ERROR, + PREVIOUS_BUILD_DIR_ERROR, + UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.exceptions import ( + BadCommand, + CommandError, + InstallationError, + NetworkConnectionError, + PreviousBuildDirError, + SubProcessError, + UninstallationError, +) +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.temp_dir import ( + global_tempdir_manager, + tempdir_registry, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple, Any + from optparse import Values + + from pip._internal.utils.temp_dir import ( + TempDirectoryTypeRegistry as TempDirRegistry + ) + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(CommandContextMixIn): + usage = None # type: str + ignore_require_venv = False # type: bool + + def __init__(self, name, summary, isolated=False): + # type: (str, str, bool) -> None + super(Command, self).__init__() + parser_kw = { + 'usage': self.usage, + 'prog': '{} {}'.format(get_prog(), name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.name = name + self.summary = summary + self.parser = ConfigOptionParser(**parser_kw) + + self.tempdir_registry = None # type: Optional[TempDirRegistry] + + # Commands should add options to this option group + optgroup_name = '{} Options'.format(self.name.capitalize()) + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + self.add_options() + + def add_options(self): + # type: () -> None + pass + + def handle_pip_version_check(self, options): + # type: (Values) -> None + """ + This is a no-op so that commands by default do not do the pip version + check. + """ + # Make sure we do the pip version check if the index_group options + # are present. + assert not hasattr(options, 'no_index') + + def run(self, options, args): + # type: (Values, List[Any]) -> int + raise NotImplementedError + + def parse_args(self, args): + # type: (List[str]) -> Tuple[Any, Any] + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + # type: (List[str]) -> int + try: + with self.main_context(): + return self._main(args) + finally: + logging.shutdown() + + def _main(self, args): + # type: (List[str]) -> int + # We must initialize this before the tempdir manager, otherwise the + # configuration would not be accessible by the time we clean up the + # tempdir manager. + self.tempdir_registry = self.enter_context(tempdir_registry()) + # Intentionally set as early as possible so globally-managed temporary + # directories are available to the rest of the code. + self.enter_context(global_tempdir_manager()) + + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + level_number = setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + if ( + sys.version_info[:2] == (2, 7) and + not options.no_python_version_warning + ): + message = ( + "pip 21.0 will drop support for Python 2.7 in January 2021. " + "More details about Python 2 support in pip can be found at " + "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa + ) + if platform.python_implementation() == "CPython": + message = ( + "Python 2.7 reached the end of its life on January " + "1st, 2020. Please upgrade your Python as Python 2.7 " + "is no longer maintained. " + ) + message + deprecated(message, replacement=None, gone_in="21.0") + + if ( + sys.version_info[:2] == (3, 5) and + not options.no_python_version_warning + ): + message = ( + "Python 3.5 reached the end of its life on September " + "13th, 2020. Please upgrade your Python as Python 3.5 " + "is no longer maintained. pip 21.0 will drop support " + "for Python 3.5 in January 2021." + ) + deprecated(message, replacement=None, gone_in="21.0") + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv and not self.ignore_require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + if options.cache_dir: + options.cache_dir = normalize_path(options.cache_dir) + if not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "or is not writable by the current user. The cache " + "has been disabled. Check the permissions and owner of " + "that directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + if getattr(options, "build_dir", None): + deprecated( + reason=( + "The -b/--build/--build-dir/--build-directory " + "option is deprecated." + ), + replacement=( + "use the TMPDIR/TEMP/TMP environment variable, " + "possibly combined with --no-clean" + ), + gone_in="20.3", + issue=8333, + ) + + if 'resolver' in options.unstable_features: + logger.critical( + "--unstable-feature=resolver is no longer supported, and " + "has been replaced with --use-feature=2020-resolver instead." + ) + sys.exit(ERROR) + + try: + status = self.run(options, args) + assert isinstance(status, int) + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand, + SubProcessError, NetworkConnectionError) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('%s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BrokenStdoutLoggingError: + # Bypass our logger and write any remaining messages to stderr + # because stdout no longer works. + print('ERROR: Pipe to stdout was broken', file=sys.stderr) + if level_number <= logging.DEBUG: + traceback.print_exc(file=sys.stderr) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BaseException: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + self.handle_pip_version_check(options) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..ed42c5f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,975 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import textwrap +import warnings +from distutils.util import strtobool +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup +from textwrap import dedent + +from pip._internal.cli.progress_bars import BAR_TYPES +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, get_src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.target_python import TargetPython +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Dict, Optional, Tuple + from optparse import OptionParser, Values + from pip._internal.cli.parser import ConfigOptionParser + + +def raise_option_error(parser, option, msg): + # type: (OptionParser, Option, str) -> None + """ + Raise an option parsing error using parser.error(). + + Args: + parser: an OptionParser instance. + option: an Option instance. + msg: the error text. + """ + msg = '{} error: {}'.format(option, msg) + msg = textwrap.fill(' '.join(msg.split())) + parser.error(msg) + + +def make_option_group(group, parser): + # type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def check_install_build_global(options, check_options=None): + # type: (Values, Optional[Values]) -> None + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + # type: (str) -> Optional[Any] + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + control.disallow_binaries() + warnings.warn( + 'Disabling all use of wheels due to the use of --build-option ' + '/ --global-option / --install-option.', stacklevel=2, + ) + + +def check_dist_restriction(options, check_target=False): + # type: (Values, bool) -> None + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any([ + options.python_version, + options.platform, + options.abi, + options.implementation, + ]) + + binary_only = FormatControl(set(), {':all:'}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and + not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # guaranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target'" + ) + + +def _path_option_check(option, opt, value): + # type: (Option, str, str) -> str + return os.path.expanduser(value) + + +class PipOption(Option): + TYPES = Option.TYPES + ("path",) + TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER["path"] = _path_option_check + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.', +) # type: Callable[..., Option] + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) # type: Callable[..., Option] + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Callable[..., Option] + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) # type: Callable[..., Option] + +no_color = partial( + Option, + '--no-color', + dest='no_color', + action='store_true', + default=False, + help="Suppress colored output", +) # type: Callable[..., Option] + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.', +) # type: Callable[..., Option] + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=( + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' + ), +) # type: Callable[..., Option] + +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help=( + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + ), +) # type: Callable[..., Option] + +log = partial( + PipOption, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + type="path", + help="Path to a verbose appending log." +) # type: Callable[..., Option] + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help="Disable prompting for input." +) # type: Callable[..., Option] + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port." +) # type: Callable[..., Option] + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) # type: Callable[..., Option] + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).', +) # type: Callable[..., Option] + + +def exists_action(): + # type: () -> Option + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + ) + + +cert = partial( + PipOption, + '--cert', + dest='cert', + type='path', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Callable[..., Option] + +client_cert = partial( + PipOption, + '--client-cert', + dest='client_cert', + type='path', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.", +) # type: Callable[..., Option] + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help="Base URL of the Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) # type: Callable[..., Option] + + +def extra_index_url(): + # type: () -> Option + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url.", + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).', +) # type: Callable[..., Option] + + +def find_links(): + # type: () -> Option + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a URL or path to an html file, then parse for links to " + "archives such as sdist (.tar.gz) or wheel (.whl) files. " + "If a local path or file:// URL that's a directory, " + "then look for archives in the directory listing. " + "Links to VCS project URLs are not supported.", + ) + + +def trusted_host(): + # type: () -> Option + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host or host:port pair as trusted, even though it " + "does not have valid or any HTTPS.", + ) + + +def constraints(): + # type: () -> Option + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.' + ) + + +def requirements(): + # type: () -> Option + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.' + ) + + +def editable(): + # type: () -> Option + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + + +def _handle_src(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +src = partial( + PipOption, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + type='path', + metavar='dir', + default=get_src_prefix(), + action='callback', + callback=_handle_src, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".' +) # type: Callable[..., Option] + + +def _get_format_control(values, option): + # type: (Values, Option) -> Any + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.no_binary, existing.only_binary, + ) + + +def _handle_only_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.only_binary, existing.no_binary, + ) + + +def no_binary(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=format_control, + help='Do not use binary packages. Can be supplied multiple times, and ' + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all binary packages, ":none:" to empty the set (notice ' + 'the colons), or one or more package names with commas between ' + 'them (no colons). Note that some packages are tricky to compile ' + 'and may fail to install when this option is used on them.', + ) + + +def only_binary(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=format_control, + help='Do not use source packages. Can be supplied multiple times, and ' + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all source packages, ":none:" to empty the set, or one ' + 'or more package names with commas between them. Packages ' + 'without binary distributions will fail to install when this ' + 'option is used on them.', + ) + + +platform = partial( + Option, + '--platform', + dest='platform', + metavar='platform', + default=None, + help=("Only use wheels compatible with . " + "Defaults to the platform of the running system."), +) # type: Callable[..., Option] + + +# This was made a separate function for unit-testing purposes. +def _convert_python_version(value): + # type: (str) -> Tuple[Tuple[int, ...], Optional[str]] + """ + Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. + + :return: A 2-tuple (version_info, error_msg), where `error_msg` is + non-None if and only if there was a parsing error. + """ + if not value: + # The empty string is the same as not providing a value. + return (None, None) + + parts = value.split('.') + if len(parts) > 3: + return ((), 'at most three version parts are allowed') + + if len(parts) == 1: + # Then we are in the case of "3" or "37". + value = parts[0] + if len(value) > 1: + parts = [value[0], value[1:]] + + try: + version_info = tuple(int(part) for part in parts) + except ValueError: + return ((), 'each version part must be an integer') + + return (version_info, None) + + +def _handle_python_version(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Handle a provided --python-version value. + """ + version_info, error_msg = _convert_python_version(value) + if error_msg is not None: + msg = ( + 'invalid --python-version value: {!r}: {}'.format( + value, error_msg, + ) + ) + raise_option_error(parser, option=option, msg=msg) + + parser.values.python_version = version_info + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + action='callback', + callback=_handle_python_version, type='str', + default=None, + help=dedent("""\ + The Python interpreter version to use for wheel and "Requires-Python" + compatibility checks. Defaults to a version derived from the running + interpreter. The version can be specified using up to three dot-separated + integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor + version can also be given as a string without dots (e.g. "37" for 3.7.0). + """), +) # type: Callable[..., Option] + + +implementation = partial( + Option, + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), +) # type: Callable[..., Option] + + +abi = partial( + Option, + '--abi', + dest='abi', + metavar='abi', + default=None, + help=("Only use wheels compatible with Python " + "abi , e.g. 'pypy_41'. If not specified, then the " + "current interpreter abi tag is used. Generally " + "you will need to specify --implementation, " + "--platform, and --python-version when using " + "this option."), +) # type: Callable[..., Option] + + +def add_target_python_options(cmd_opts): + # type: (OptionGroup) -> None + cmd_opts.add_option(platform()) + cmd_opts.add_option(python_version()) + cmd_opts.add_option(implementation()) + cmd_opts.add_option(abi()) + + +def make_target_python(options): + # type: (Values) -> TargetPython + target_python = TargetPython( + platform=options.platform, + py_version_info=options.python_version, + abi=options.abi, + implementation=options.implementation, + ) + + return target_python + + +def prefer_binary(): + # type: () -> Option + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help="Prefer older binary packages over newer source packages." + ) + + +cache_dir = partial( + PipOption, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + type='path', + help="Store the cache data in ." +) # type: Callable[..., Option] + + +def _handle_no_cache_dir(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Process a value provided for the --no-cache-dir option. + + This is an optparse.Option callback for the --no-cache-dir option. + """ + # The value argument will be None if --no-cache-dir is passed via the + # command-line, since the option doesn't accept arguments. However, + # the value can be non-None if the option is triggered e.g. by an + # environment variable, like PIP_NO_CACHE_DIR=true. + if value is not None: + # Then parse the string value to get argument error-checking. + try: + strtobool(value) + except ValueError as exc: + raise_option_error(parser, option=option, msg=str(exc)) + + # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() + # converted to 0 (like "false" or "no") caused cache_dir to be disabled + # rather than enabled (logic would say the latter). Thus, we disable + # the cache directory not just on values that parse to True, but (for + # backwards compatibility reasons) also on values that parse to False. + # In other words, always set it to False if the option is provided in + # some (valid) form. + parser.values.cache_dir = False + + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="callback", + callback=_handle_no_cache_dir, + help="Disable the cache.", +) # type: Callable[..., Option] + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.", +) # type: Callable[..., Option] + + +def _handle_build_dir(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + if value: + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +build_dir = partial( + PipOption, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + type='path', + metavar='dir', + action='callback', + callback=_handle_build_dir, + help='(DEPRECATED) ' + 'Directory to unpack packages into and build in. Note that ' + 'an initial build still takes place in a temporary directory. ' + 'The location of temporary directories can be controlled by setting ' + 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' + 'When passed, build directories are not cleaned in case of failures.' +) # type: Callable[..., Option] + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.' +) # type: Callable[..., Option] + +no_build_isolation = partial( + Option, + '--no-build-isolation', + dest='build_isolation', + action='store_false', + default=True, + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.' +) # type: Callable[..., Option] + + +def _handle_no_use_pep517(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Process a value provided for the --no-use-pep517 option. + + This is an optparse.Option callback for the no_use_pep517 option. + """ + # Since --no-use-pep517 doesn't accept arguments, the value argument + # will be None if --no-use-pep517 is passed via the command-line. + # However, the value can be non-None if the option is triggered e.g. + # by an environment variable, for example "PIP_NO_USE_PEP517=true". + if value is not None: + msg = """A value was passed for --no-use-pep517, + probably using either the PIP_NO_USE_PEP517 environment variable + or the "no-use-pep517" config file option. Use an appropriate value + of the PIP_USE_PEP517 environment variable or the "use-pep517" + config file option instead. + """ + raise_option_error(parser, option=option, msg=msg) + + # Otherwise, --no-use-pep517 was passed via the command-line. + parser.values.use_pep517 = False + + +use_pep517 = partial( + Option, + '--use-pep517', + dest='use_pep517', + action='store_true', + default=None, + help='Use PEP 517 for building source distributions ' + '(use --no-use-pep517 to force legacy behaviour).' +) # type: Any + +no_use_pep517 = partial( + Option, + '--no-use-pep517', + dest='use_pep517', + action='callback', + callback=_handle_no_use_pep517, + default=None, + help=SUPPRESS_HELP +) # type: Any + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.", +) # type: Callable[..., Option] + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.", +) # type: Callable[..., Option] + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories." +) # type: Callable[..., Option] + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) # type: Callable[..., Option] + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=False, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.", +) # type: Callable[..., Option] + + +def _handle_merge_hash(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to {} must be a hash name ' # noqa + 'followed by a value, like --hash=sha256:' + 'abcde...'.format(opt_str)) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for {} are {}.'.format( # noqa + opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_handle_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...', +) # type: Callable[..., Option] + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.', +) # type: Callable[..., Option] + + +list_path = partial( + PipOption, + '--path', + dest='path', + type='path', + action='append', + help='Restrict to the specified installation path for listing ' + 'packages (can be used multiple times).' +) # type: Callable[..., Option] + + +def check_list_path_option(options): + # type: (Values) -> None + if options.path and (options.user or options.local): + raise CommandError( + "Cannot combine '--path' with '--user' or '--local'" + ) + + +no_python_version_warning = partial( + Option, + '--no-python-version-warning', + dest='no_python_version_warning', + action='store_true', + default=False, + help='Silence deprecation warnings for upcoming unsupported Pythons.', +) # type: Callable[..., Option] + + +unstable_feature = partial( + Option, + '--unstable-feature', + dest='unstable_features', + metavar='feature', + action='append', + default=[], + choices=['resolver'], + help=SUPPRESS_HELP, # TODO: drop this in pip 20.3 +) # type: Callable[..., Option] + +use_new_feature = partial( + Option, + '--use-feature', + dest='features_enabled', + metavar='feature', + action='append', + default=[], + choices=['2020-resolver', 'fast-deps'], + help='Enable new functionality, that may be backward incompatible.', +) # type: Callable[..., Option] + +use_deprecated_feature = partial( + Option, + '--use-deprecated', + dest='deprecated_features_enabled', + metavar='feature', + action='append', + default=[], + choices=[], + help=( + 'Enable deprecated functionality, that will be removed in the future.' + ), +) # type: Callable[..., Option] + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + no_python_version_warning, + unstable_feature, + use_new_feature, + use_deprecated_feature, + ] +} # type: Dict[str, Any] + +index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + ] +} # type: Dict[str, Any] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/command_context.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/command_context.py new file mode 100644 index 0000000..d1a64a7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/command_context.py @@ -0,0 +1,36 @@ +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator, ContextManager, TypeVar + + _T = TypeVar('_T', covariant=True) + + +class CommandContextMixIn(object): + def __init__(self): + # type: () -> None + super(CommandContextMixIn, self).__init__() + self._in_main_context = False + self._main_context = ExitStack() + + @contextmanager + def main_context(self): + # type: () -> Iterator[None] + assert not self._in_main_context + + self._in_main_context = True + try: + with self._main_context: + yield + finally: + self._in_main_context = False + + def enter_context(self, context_provider): + # type: (ContextManager[_T]) -> _T + assert self._in_main_context + + return self._main_context.enter_context(context_provider) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main.py new file mode 100644 index 0000000..172f30d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main.py @@ -0,0 +1,75 @@ +"""Primary application entrypoint. +""" +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import create_command +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) + + +# Do not import and use main() directly! Using it directly is actively +# discouraged by pip's maintainers. The name, location and behavior of +# this function is subject to change, so calling it directly is not +# portable across different pip versions. + +# In addition, running pip in-process is unsupported and unsafe. This is +# elaborated in detail at +# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program. +# That document also provides suggestions that should work for nearly +# all users that are considering importing and using main() directly. + +# However, we know that certain users will still want to invoke pip +# in-process. If you understand and accept the implications of using pip +# in an unsupported manner, the best approach is to use runpy to avoid +# depending on the exact location of this entry point. + +# The following example shows how to use runpy to invoke pip in that +# case: +# +# sys.argv = ["pip", your, args, here] +# runpy.run_module("pip", run_name="__main__") +# +# Note that this will exit the process after running, unlike a direct +# call to main. As it is not safe to do any processing after calling +# main, this should not be an issue in practice. + +def main(args=None): + # type: (Optional[List[str]]) -> int + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parse_command(args) + except PipError as exc: + sys.stderr.write("ERROR: {}".format(exc)) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = create_command(cmd_name, isolated=("--isolated" in cmd_args)) + + return command.main(cmd_args) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..08c82c1 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,99 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, + UpdatingDefaultsHelpFormatter, +) +from pip._internal.commands import commands_dict, get_similar_commands +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_pip_version, get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple, List + + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser(): + # type: () -> ConfigOptionParser + """Creates and returns the main parser for pip's CLI + """ + + parser_kw = { + 'usage': '\n%prog [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + parser.version = get_pip_version() + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + # so the help formatter knows + parser.main = True # type: ignore + + # create command listing for description + description = [''] + [ + '{name:27} {command_info.summary}'.format(**locals()) + for name, command_info in commands_dict.items() + ] + parser.description = '\n'.join(description) + + return parser + + +def parse_command(args): + # type: (List[str]) -> Tuple[str, List[str]] + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) # type: ignore + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(guess)) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/parser.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..04e00b7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,266 @@ +"""Base option parser setup""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import optparse +import sys +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.compat import get_terminal_size + +logger = logging.getLogger(__name__) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option) + + def _format_option_strings(self, option, mvarfmt=' <{}>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt.format(metavar.lower())) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: {}\n'.format( + self.indent_lines(textwrap.dedent(usage), " ")) + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '{}:\n{}\n'.format(label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ + + def expand_default(self, option): + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class CustomOptionParser(optparse.OptionParser): + + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.name = kwargs.pop('name') + + isolated = kwargs.pop("isolated", False) + self.config = Configuration(isolated) + + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: {}".format(exc)) + sys.exit(3) + + def _get_ordered_configuration_items(self): + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items = {name: [] for name in override_order} + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + option = self.get_option('--' + key) + + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + try: + val = strtobool(val) + except ValueError: + error_msg = invalid_config_error_message( + option.action, key, val + ) + self.error(error_msg) + + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def get_default_values(self): + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(UNKNOWN_ERROR, "{}\n".format(msg)) + + +def invalid_config_error_message(action, key, val): + """Returns a better error message when invalid configuration option + is provided.""" + if action in ('store_true', 'store_false'): + return ("{0} is not a valid value for {1} option, " + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead.").format(val, key) + + return ("{0} is not a valid value for {1} option, " + "please specify a numerical value like 1/0 " + "instead.").format(val, key) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py new file mode 100644 index 0000000..6933855 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py @@ -0,0 +1,280 @@ +from __future__ import division + +import itertools +import sys +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar +from pip._vendor.progress.spinner import Spinner + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, List + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +def _select_progress_class(preferred, fallback): + # type: (Bar, Bar) -> Bar + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + """ + Save the original SIGINT handler for later. + """ + # https://github.com/python/mypy/issues/5887 + super(InterruptibleMixin, self).__init__( # type: ignore + *args, + **kwargs + ) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + # type: () -> None + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() # type: ignore + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): # type: ignore + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class SilentBar(Bar): + + def update(self): + # type: () -> None + pass + + +class BlueEmojiBar(IncrementalBar): + + suffix = "%(percent)d%%" + bar_prefix = " " + bar_suffix = " " + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + # https://github.com/python/mypy/issues/5887 + super(DownloadProgressMixin, self).__init__( # type: ignore + *args, + **kwargs + ) + self.message = (" " * ( + get_indentation() + 2 + )) + self.message # type: str + + @property + def downloaded(self): + # type: () -> str + return format_size(self.index) # type: ignore + + @property + def download_speed(self): + # type: () -> str + # Avoid zero division errors... + if self.avg == 0.0: # type: ignore + return "..." + return format_size(1 / self.avg) + "/s" # type: ignore + + @property + def pretty_eta(self): + # type: () -> str + if self.eta: # type: ignore + return "eta {}".format(self.eta_td) # type: ignore + return "" + + def iter(self, it): # type: ignore + for x in it: + yield x + # B305 is incorrectly raised here + # https://github.com/PyCQA/flake8-bugbear/issues/59 + self.next(len(x)) # noqa: B305 + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call needs to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: # type: ignore + self.hide_cursor = False + + # https://github.com/python/mypy/issues/5887 + super(WindowsMixin, self).__init__(*args, **kwargs) # type: ignore + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) # type: ignore + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + + +class DefaultDownloadProgressBar(BaseDownloadProgressBar, + _BaseBar): + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): + pass + + +class DownloadBar(BaseDownloadProgressBar, + Bar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, + FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, + BlueEmojiBar): + pass + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + # type: () -> str + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + # type: () -> None + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): # type: ignore + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/req_command.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/req_command.py new file mode 100644 index 0000000..78b5ce6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/req_command.py @@ -0,0 +1,402 @@ +"""Contains the Command base classes that depend on PipSession. + +The classes in this module are in a separate module so the commands not +needing download / PackageFinder capability don't unnecessarily import the +PackageFinder machinery and all its vendored dependencies, etc. +""" + +import logging +import os +from functools import partial + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.network.download import Downloader +from pip._internal.network.session import PipSession +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, + install_req_from_parsed_requirement, + install_req_from_req_string, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.self_outdated_check import pip_self_version_check +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List, Optional, Tuple + + from pip._internal.cache import WheelCache + from pip._internal.models.target_python import TargetPython + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolution.base import BaseResolver + from pip._internal.utils.temp_dir import ( + TempDirectory, + TempDirectoryTypeRegistry, + ) + + +logger = logging.getLogger(__name__) + + +class SessionCommandMixin(CommandContextMixIn): + + """ + A class mixin for command classes needing _build_session(). + """ + def __init__(self): + # type: () -> None + super(SessionCommandMixin, self).__init__() + self._session = None # Optional[PipSession] + + @classmethod + def _get_index_urls(cls, options): + # type: (Values) -> Optional[List[str]] + """Return a list of index urls from user-provided options.""" + index_urls = [] + if not getattr(options, "no_index", False): + url = getattr(options, "index_url", None) + if url: + index_urls.append(url) + urls = getattr(options, "extra_index_urls", None) + if urls: + index_urls.extend(urls) + # Return None rather than an empty list + return index_urls or None + + def get_default_session(self, options): + # type: (Values) -> PipSession + """Get a default-managed session.""" + if self._session is None: + self._session = self.enter_context(self._build_session(options)) + # there's no type annotation on requests.Session, so it's + # automatically ContextManager[Any] and self._session becomes Any, + # then https://github.com/python/mypy/issues/7696 kicks in + assert self._session is not None + return self._session + + def _build_session(self, options, retries=None, timeout=None): + # type: (Values, Optional[int], Optional[int]) -> PipSession + assert not options.cache_dir or os.path.isabs(options.cache_dir) + session = PipSession( + cache=( + os.path.join(options.cache_dir, "http") + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + trusted_hosts=options.trusted_hosts, + index_urls=self._get_index_urls(options), + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + +class IndexGroupCommand(Command, SessionCommandMixin): + + """ + Abstract base class for commands with the index_group options. + + This also corresponds to the commands that permit the pip version check. + """ + + def handle_pip_version_check(self, options): + # type: (Values) -> None + """ + Do the pip version check if not disabled. + + This overrides the default behavior of not doing the check. + """ + # Make sure the index_group options are present. + assert hasattr(options, 'no_index') + + if options.disable_pip_version_check or options.no_index: + return + + # Otherwise, check if we're using the latest version of pip available. + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_self_version_check(session, options) + + +KEEPABLE_TEMPDIR_TYPES = [ + tempdir_kinds.BUILD_ENV, + tempdir_kinds.EPHEM_WHEEL_CACHE, + tempdir_kinds.REQ_BUILD, +] + + +def with_cleanup(func): + # type: (Any) -> Any + """Decorator for common logic related to managing temporary + directories. + """ + def configure_tempdir_registry(registry): + # type: (TempDirectoryTypeRegistry) -> None + for t in KEEPABLE_TEMPDIR_TYPES: + registry.set_delete(t, False) + + def wrapper(self, options, args): + # type: (RequirementCommand, Values, List[Any]) -> Optional[int] + assert self.tempdir_registry is not None + if options.no_clean: + configure_tempdir_registry(self.tempdir_registry) + + try: + return func(self, options, args) + except PreviousBuildDirError: + # This kind of conflict can occur when the user passes an explicit + # build directory with a pre-existing folder. In that case we do + # not want to accidentally remove it. + configure_tempdir_registry(self.tempdir_registry) + raise + + return wrapper + + +class RequirementCommand(IndexGroupCommand): + + def __init__(self, *args, **kw): + # type: (Any, Any) -> None + super(RequirementCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option(cmdoptions.no_clean()) + + @staticmethod + def make_requirement_preparer( + temp_build_dir, # type: TempDirectory + options, # type: Values + req_tracker, # type: RequirementTracker + session, # type: PipSession + finder, # type: PackageFinder + use_user_site, # type: bool + download_dir=None, # type: str + wheel_download_dir=None, # type: str + ): + # type: (...) -> RequirementPreparer + """ + Create a RequirementPreparer instance for the given parameters. + """ + downloader = Downloader(session, progress_bar=options.progress_bar) + + temp_build_dir_path = temp_build_dir.path + assert temp_build_dir_path is not None + + return RequirementPreparer( + build_dir=temp_build_dir_path, + src_dir=options.src_dir, + download_dir=download_dir, + wheel_download_dir=wheel_download_dir, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + downloader=downloader, + finder=finder, + require_hashes=options.require_hashes, + use_user_site=use_user_site, + ) + + @staticmethod + def make_resolver( + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + options, # type: Values + wheel_cache=None, # type: Optional[WheelCache] + use_user_site=False, # type: bool + ignore_installed=True, # type: bool + ignore_requires_python=False, # type: bool + force_reinstall=False, # type: bool + upgrade_strategy="to-satisfy-only", # type: str + use_pep517=None, # type: Optional[bool] + py_version_info=None # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> BaseResolver + """ + Create a Resolver instance for the given parameters. + """ + make_install_req = partial( + install_req_from_req_string, + isolated=options.isolated_mode, + use_pep517=use_pep517, + ) + # The long import name and duplicated invocation is needed to convince + # Mypy into correctly typechecking. Otherwise it would complain the + # "Resolver" class being redefined. + if '2020-resolver' in options.features_enabled: + import pip._internal.resolution.resolvelib.resolver + return pip._internal.resolution.resolvelib.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + lazy_wheel='fast-deps' in options.features_enabled, + ) + import pip._internal.resolution.legacy.resolver + return pip._internal.resolution.legacy.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + + def get_requirements( + self, + args, # type: List[str] + options, # type: Values + finder, # type: PackageFinder + session, # type: PipSession + ): + # type: (...) -> List[InstallRequirement] + """ + Parse command-line arguments into the corresponding requirements. + """ + requirements = [] # type: List[InstallRequirement] + for filename in options.constraints: + for parsed_req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + user_supplied=False, + ) + requirements.append(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, None, isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + ) + requirements.append(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + user_supplied=True, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + ) + requirements.append(req_to_add) + + # NOTE: options.require_hashes may be set if --require-hashes is True + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, + finder=finder, options=options, session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + ) + requirements.append(req_to_add) + + # If any requirement has hash options, enable hash checking. + if any(req.has_hash_options for req in requirements): + options.require_hashes = True + + if not (args or options.editables or options.requirements): + opts = {'name': self.name} + if options.find_links: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(maybe you meant "pip {name} {links}"?)'.format( + **dict(opts, links=' '.join(options.find_links)))) + else: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(see "pip help {name}")'.format(**opts)) + + return requirements + + @staticmethod + def trace_basic_info(finder): + # type: (PackageFinder) -> None + """ + Trace basic information about the provided objects. + """ + # Display where finder is looking for packages + search_scope = finder.search_scope + locations = search_scope.get_formatted_locations() + if locations: + logger.info(locations) + + def _build_package_finder( + self, + options, # type: Values + session, # type: PipSession + target_python=None, # type: Optional[TargetPython] + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> PackageFinder + """ + Create a package finder appropriate to this requirement command. + + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + link_collector = LinkCollector.create(session, options=options) + selection_prefs = SelectionPreferences( + allow_yanked=True, + format_control=options.format_control, + allow_all_prereleases=options.pre, + prefer_binary=options.prefer_binary, + ignore_requires_python=ignore_requires_python, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + target_python=target_python, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/spinners.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/spinners.py new file mode 100644 index 0000000..c6c4c5c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/spinners.py @@ -0,0 +1,173 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time + +from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator, IO + +logger = logging.getLogger(__name__) + + +class SpinnerInterface(object): + def spin(self): + # type: () -> None + raise NotImplementedError() + + def finish(self, final_status): + # type: (str) -> None + raise NotImplementedError() + + +class InteractiveSpinner(SpinnerInterface): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + # type: (str, IO[str], str, float) -> None + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + # type: (str) -> None + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + # type: (str) -> None + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(SpinnerInterface): + def __init__(self, message, min_update_interval_seconds=60): + # type: (str, float) -> None + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + # type: (str) -> None + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + # type: (str) -> None + if self._finished: + return + self._update( + "finished with status '{final_status}'".format(**locals())) + self._finished = True + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + # type: (float) -> None + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 # type: float + + def ready(self): + # type: () -> bool + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + # type: () -> None + self._last_update = time.time() + + +@contextlib.contextmanager +def open_spinner(message): + # type: (str) -> Iterator[SpinnerInterface] + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) # type: SpinnerInterface + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") + + +@contextlib.contextmanager +def hidden_cursor(file): + # type: (IO[str]) -> Iterator[None] + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..6825fa6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,122 @@ +""" +Package containing all pip commands +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False +# There is currently a bug in python/typeshed mentioned at +# https://github.com/python/typeshed/issues/3906 which causes the +# return type of difflib.get_close_matches to be reported +# as List[Sequence[str]] whereas it should have been List[str] + +from __future__ import absolute_import + +import importlib +from collections import OrderedDict, namedtuple + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any + from pip._internal.cli.base_command import Command + + +CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary') + +# The ordering matters for help display. +# Also, even though the module path starts with the same +# "pip._internal.commands" prefix in each case, we include the full path +# because it makes testing easier (specifically when modifying commands_dict +# in test setup / teardown by adding info for a FakeCommand class defined +# in a test-related module). +# Finally, we need to pass an iterable of pairs here rather than a dict +# so that the ordering won't be lost when using Python 2.7. +commands_dict = OrderedDict([ + ('install', CommandInfo( + 'pip._internal.commands.install', 'InstallCommand', + 'Install packages.', + )), + ('download', CommandInfo( + 'pip._internal.commands.download', 'DownloadCommand', + 'Download packages.', + )), + ('uninstall', CommandInfo( + 'pip._internal.commands.uninstall', 'UninstallCommand', + 'Uninstall packages.', + )), + ('freeze', CommandInfo( + 'pip._internal.commands.freeze', 'FreezeCommand', + 'Output installed packages in requirements format.', + )), + ('list', CommandInfo( + 'pip._internal.commands.list', 'ListCommand', + 'List installed packages.', + )), + ('show', CommandInfo( + 'pip._internal.commands.show', 'ShowCommand', + 'Show information about installed packages.', + )), + ('check', CommandInfo( + 'pip._internal.commands.check', 'CheckCommand', + 'Verify installed packages have compatible dependencies.', + )), + ('config', CommandInfo( + 'pip._internal.commands.configuration', 'ConfigurationCommand', + 'Manage local and global configuration.', + )), + ('search', CommandInfo( + 'pip._internal.commands.search', 'SearchCommand', + 'Search PyPI for packages.', + )), + ('cache', CommandInfo( + 'pip._internal.commands.cache', 'CacheCommand', + "Inspect and manage pip's wheel cache.", + )), + ('wheel', CommandInfo( + 'pip._internal.commands.wheel', 'WheelCommand', + 'Build wheels from your requirements.', + )), + ('hash', CommandInfo( + 'pip._internal.commands.hash', 'HashCommand', + 'Compute hashes of package archives.', + )), + ('completion', CommandInfo( + 'pip._internal.commands.completion', 'CompletionCommand', + 'A helper command used for command completion.', + )), + ('debug', CommandInfo( + 'pip._internal.commands.debug', 'DebugCommand', + 'Show information useful for debugging.', + )), + ('help', CommandInfo( + 'pip._internal.commands.help', 'HelpCommand', + 'Show help for commands.', + )), +]) # type: OrderedDict[str, CommandInfo] + + +def create_command(name, **kwargs): + # type: (str, **Any) -> Command + """ + Create an instance of the Command class with the given name. + """ + module_path, class_name, summary = commands_dict[name] + module = importlib.import_module(module_path) + command_class = getattr(module, class_name) + command = command_class(name=name, summary=summary, **kwargs) + + return command + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49d10f8b534d28798789ae76c5a0d9c539e67299 GIT binary patch literal 3008 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e186f3be938db6acb9406a3957b4d05d9d564c8 GIT binary patch literal 3230 zcmaJ@&2JpH6`!x2{nCd&QrHa=gH?3U1ex6^0SdIWj5upGHDXJG)=uGxjlpQhU5z|n zCONXT(w^jFe8@3C57M#!(p-BgdhD$^r0;Q8yDLlb3`jmcK0cDa_dy2p^9>EYUtazi z)aNwqU)Y)bEa-d#Mf?O6)u=>-Vz48!xRA_`l04&b19;U=mLx$+k?cP0bk@6FAZvXl{hVig zpl>{Ww)M>K{B6DSy}$i*W8>k*cfhqCMM6Sr{}99l6N3kh&qyBS zC`*Zu0hfm&1~GY^aVkiN&18K-I14Co?}ww|3j<+#$&5TcxCCjXA0WOG;3=GI z%PT7*z3wV;oU72i6xY!*t1r&OdnLM|jEVdG2N5kUN7U~{V)xAd7y>+l4k3bpsp2;? z-bPlyoES@Sl%ho)vb1#d6y;-N<&v`}f!sx1sSKi25H}*O`<`y;kdb#PH+KNV^G-d5 z1Ks*;s~7MjtLOJ8d6f2v$m2+o=R_^b_Q7>#*7|Yv#Z)=Q>{b_0A;u0EHGAe0`rlUdkEA#Dp9oGqS@-As8jSY|0)V1M*huNeGTH`H-$b=|BR&X@|x>#%rPQF(Uo>3RaTnr<3M%G7~yu49#Upb|t`QFBX+G9gO~ zu^3cb7jOst1CCeQ;FBwLp%`oj4Fuvn-bC@Mmqs~`c)|L$Frj{yOHnogswjZAwDW)q z#><=F;EYQY0Jh)fS&`%AB3Rt}r^auCj{m)DKLr`l4tIkz=w~Sn+7Gg@NLVU?nX&e( zz=g(TZ@|3Kh@BruI49m3EFRz$NPg zcMI}^6h!W_3oe)S6uTeoGq~~b4R9`cFpTkq)P*8Gf~pU3@?vfX;e4du(tb8xS+Il- z-sLK*J4R`=zT^uasrsYp_s}gHcVl>Zpi%Dq0R8_$(FV>%OnH&E?y9R#+ymllPKhre zOTBH3(jL7^)H;LP>)YF#+kYm{vVtUqkR%U;Acw=oz#@<#Hk@najqwftaC5`&cD5gG zJ?WO#OLibY6FjIGRhr70WN+ka9^|D>83jizO?Xh1HY6HI?!1D>sN@J{x%lnRyzrZ44t{qjrW;PRtoeQ#B+U2AhVLgCEn=kS zeE(Gu#N&~w@6#*<_RXlDV)EkbRS`i84zkZb#+`kM6~_2Xg(l|kzSjN-MPS0M!Hz7W zc1v-co491JkOgS+Il2}994ovY$~zFjtw{v8!Z>O{8pOP*!i-~RT_yxCR9OfRgX}mm z!0q#z3K_+sl|7`$Z-j5SJlcpmOx^#`o*oHJ)vP$(a- j-5sHN53{Ja1J#^S(`&|@u71P9*03r@-Dn#Bu|NAi4O3*y literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0e2998eb474f02c5af6c4b2ee315fdb3a8f94b3 GIT binary patch literal 8180 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 Dy%}U$ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7654bc381f1f3f557a7ee4bdc5340762d724c28 GIT binary patch literal 6545 zcmai2OK=-UdY%^sL+~MrFNu;Yk7aQ!I1;E$HmO9BwO*6*zGSQ;O5XBhT~kB!03322 zPa0@U=o|L*?# ze|&#O^Yb+WzkfdXpXi^i7{-56W%}o!@*zIaG!26p%uJ2U3`~>CR%!*7&h5a~xf3`# zuLKpHyFnGXow}J9czTYL&SbTqrpuMIn#~4ty6mR&*+Q_O%hmK;wiqnxvX`FEmVzZ+ zo=KOpm0(4eYw2pX7Od&=Yu$WY>agy1baan_UmCW3GL?5r5~z4Blhs+0vIraDy$g736ic%GQv- z&n~cw$p46~vrEWtvdioW@>}dJb`^Ppz0G{&o9rES4f*XK8th$m{Y!&gj~9>Z;JeJ) zG3p!N5U1;=Iv4GVBJIgIOtNmlWfN`l-GffZV^Q>YD;8*|Jll(TjDi=2?IexE5an7c zV?|dcMNUJiTSb;dIYV(Niw@#YM!XZtuzM)?ikt@QJl@*6zq3O#lbpr54AY{6+BrQe z>4r~ZE>acnYkU<}L?n=S+_vYLH& zL)`HR7OTorTI*|rUjT`3f3^8o;0T+oy(o`5Mb4tl?V{Do za8xuF@#d507P4FM^KQ(OvKj1&1-QhE{kSDJ%j&H-f4Z3_yHo}9{-p62n?YmKFv(@i^C$&r&3UoO;@w`S(LGc(u~$3`_2#b93H8w}zK(JW-{eQ_9zM;6 z0ELnbC(tozNt;M_^vQ z_+3LhxNVsF|2b04&PubtEDl8jVwT2qczr8()WYcoW$;yOr0(%$WPAmQs_sQ1l9DSM z1o$Otr!kz&hdxx@(yeOwG_8;>N#9MGTqI%_G;ctN1b%8K@1j#IATg|(SukdWMyC+nO|KSn#^X-myoSv_r%yn+t510 zupdn`42-QX7c(pxePlu!t4&pfhUBc^{aTqfF9Kp1d`wg+*FpIj4yJ^viQgIK3@w_wVAng?N z(RJe#OGA@(53_2XSf4?to>bA07XV z&*u)5vG@XvOTD;V_7Cr{H_0WUH${~ zp5haU8GV{hpQ0eaAt5 zv_<8#;WyRn!}v)r;o5|%N;l#nu2=YnG{7x+!s#$Um7F-PO;ty3<8?W^N?aUC30ytaS#C zcpu_t53He+FAbc5bzt+mS`;l}sJXRqw?7w(WbbPrN`%z2wqSPPl z+6V0LyBz+|kDp5(`Q)Shq)kJ4>?fk;;}`iYz=0In=ofs{`#Pak(My@17t-IwJdKB9 z*3i@WWt^)2iGSm99_^;FFAHBQ{bVyG@5g((>GuS#5>>L;WT}18jax}OiP?Bez5l2F zjjf+GKYsAztw(oG4ebGJ2r0%RefTWe8FuwzSnqoq;)4%1#D-|rE2;uykO_SyoUHFF zzlqM}DXTN$me8^iED{Q7$(W5rGnm)R3$s`VfQ(?FWb(NCb?LdR-RNWtopL->bg&Jv zhQz2WLi4o#!D>-%y>u-833^)8J^qoknz&Tb;AtFk1gk~%=_{aY>5z850dIV7+I^1U z+okgK;5fh(kNDx3pW+TwW*O^fe4(vp2_|S~@FEYW>oo_mQerB7pOcdwVxXX%12pIp z@+QisRAUAWle7H?Do_P;WMyDkj!jr)sKT*@9F}8P892NFWv~Z;62l5}$nFg6BXdwW zv4*ZB3*-_)10n-VI52p3)NdX*yvy8Sbzqaj|2u0Pl!4f)!>aVazmG7-hDt6CJ)$CK z2Hp{D78LB4gDU?W^Y(4nyqRB{)2o~TD!)I&>00v}jl{Hu%=MFtlxVuEF zOw<9Keldi*co%$l^l|_DjYgyP*B>WbNdJY5I$@e*i5&VU3X=I}dBguS62iZA3(~t= z2(5S2;YVrO_yT@nW-_f_kzFGSYOeG+VJCqgXys6LiqhofwJ|2L`t|`YC9lt@ z>L{w=`ZimIT}qJUD@;k0o53jbVGvRx<;^zg#CVHlnAy&tgrY9?EfAH@;^uNwHaL22tX^uDiX>*h!2$+1{X>~aWhU3*;S`{+MqT=fJbAL7$dL2Lv7Hh4ZU@pNM0*~G?^34k9S3tZ;1D)K7x z=swllHieX<(7u0Nd&2Dm0YP-=>u6K^T7q?muuDhTU(=&}e*-~n$Gd{{I90BzX_0+e+`muR;Md7X@7Nvl}`&MGI7SL?Wd)9(%8XjSFmk%B5f zn;I%{#E3cc9kQVx4#EyEdR^WoG7gC&Oo}7owEarJ%Db4QHAYl_Pn)Kd4QtL^H2W8) zxzv~%*dlEl|EN5Hn*WZmf1=!-R?MXd$bV+_e0B~wU z8Dy58W;(H64l`&ENh@yjXAyXeu|a>9UMWUkslPy#_<7vwNrHX-HL7DbEN(}=RK|I$ zVAxoHebi2`V<-ZWO}Z}YFej#2T^_e#kz%}ljpohKJgAc-htgkuuLaS3Pb1X?;E`UJ z)Y8NIfA#qO&ZC`h>j&XSkDJ>M?yH5Z#}6O8s`oEXhhszoK(nDA?QYcjtJ9s^_e&Dn z^-J0hs_L_d=Qx6XNa3@POa2xmq?xMr>ga0e?6K7<+Rh$%6xqdUa3>s}|U&sk&)ouQSMtw%39K1h@*R|N@ zz5L(ME8a%}hf8?WoipjbfInhIggA4iYb}}dN2}#pp4DGGrQsmbM2w5aP>MJa(;>_q zg`=UG3&SS}PDVY|Fl0pwLn=K&)v!LDyx=ijS9K5vAeleqwipZw|lY&njt6m$qAFHD!o_It&(lk7ph3e$02*kO|nR8d8)ZF z5}lIcWUTmYB*A$-X8hpTXr)PMc(0ymZ|p`Q4o5bCQ%D$GIXmdp%W(6OY6St43mtktDAb<4vpv1yLxS5U!f^Hg?69Sf-N zbCfJnvj^)^I+F8LeQi&rzaKp~a|)i-3O@nTYMVt*9r71y^V~bu= zq`x<$Y^|K;PzfB)n>X`j=KcA-$Dq@3 zHT?ea_{-p(Wlj4B5z{{t#Cv$e-_b-`q$gUc4|JXSMq&&M^==FrcsCL=Z4R1>XC_u^ z4{X(MCQjNKv{HBAD%wis()OUO`gYPuyMwOkJIQ>yFjzWRP1DF8jK>@ei|1;KTKFKDs!x~_a5!+yuY`H?kX+j*Gv3@2jOGJHyYAq zBT;hZM=_V(QO46CiBFlI2gMK)d5oiFWs#Q!#B@GpX?`EuhDF9t=y1RN^x>!e&adC! z`Hlb4qut#PcR#?W^+}MFj1F%;h(!_7U8jEy5bxm;Z=hk?K#w$7P>1!5ff+R-^SL%? zGAn9E)^qJ#AJ~x%ojG#;e)e>fWIw zt~gMi(n`!`CS)A))2v{g$VwhEFW})YKBj4EgFMc?AQ71-%A7nvcu{>zIvQQWdu{4~ zxLKSq@!F5Pr^7fL(*DPB#FVIDxl@l+()GT2h8y&|y8l5!QF{MJ@0i$KJ-4T9r1H-Yw8f!TH5wLt5x1pO6^_E@AoU2HzoD>OTwxf{Xc@X zjze)}0GMXm``?>xB`=wa-1|GH*H$8nZ(dCfp`NRsfgYjO4jw_CIM$A|3%xKd8kgE7 zu<436XXxe(ZOzd34DHO&tr^;#q334k_6*&bp}RBm{0zM?LoZ&_M|z}32E{BzYq4~+ z9NCfc!qGM8;TCCtG=cu@!Z=z1y?SY!>GZx>i&|s-(olM;D4GpA(h%+L?y1p>POV-q zVxl-*tlse`AYAMr+D=z*3{|is+mirNnAV`V_ih42JVFd?fw|q^Fr`VmNIPXka2#-H zN9-UdlR}z9mgJnwzHvu70A?Q!E~Fbos)w_1LNhrR0<8h6kSb(*DpZ7Q?U!*9sk7Ll z%#T2jzX@w<+GUOe)*-P2x>X3^n6IDm{ z4|!JRa!v((O;~xr1>nbKvmMK_rAl+&OY4L9we-pa<;!m58Fkftj!(bE~ zW}_(Byq|?7;S>flw)rIJ;nic$a>nCo7>N)D^J`q*tjHb^ZZnDZiA0)wtN-&&f&A5* zB!x|2?WPYCF+K_sh*o)1Y)<^8pF{oEmK8sw$bJ`%W-RF6@i#2p(GC1f-T2aS+J=dq z`L)^X>Zf-no>%9r{*?TXTxas@qSL~o;N&ZGB7Lm;+M$M4feM0!LdhKKN6mBMH4Cfi zQwUwy#6N2k&R9R!&rF0E!fV_Zo00LNan{64H~$^lxwdv@VSc;loFh2T?4nDfV+%p7 zN9GIj%mH_PY+!YB+_*H)T9CCchTO3Yc_gVx!T!QNbHQ00yEKN;rO3wWIjmkDHx&TR z9o-tYAk}$coVBCY#qG$A=ALWg_R-44>ZNuc$sH%?j63{a#ah%Jcg_+1#T|ryXZK^& zCiWygYysCsq{z-ot|^^~=`h)fSi%Y>&Bt&txllzILJ74IDuU=&8@B8gs9);HfV1UC;l;f7)OME9b_nd zR>C0*ANzGyXOaJO$XFu2(7hX3gxp{ck61$kgKM8J03-DyjgztUKO0ZXy((4^MysL> zLulimOmMl${aGZ>sq(>lU2Lq&h4>H!BuZI8VUQ#^Q;S7$5$vO2sbI2JN(6x@ zM*)~|NC3zwk1BO@3IVxHxFVU{tgY2N6B`bFqAvZJYFLJAwDGuxLkLFy|5(i~#uTCv{gq`e z0M!&vwdnubawj7u_%5N|HFJ0eHv#3m{-}e0{ zWsuYy+lOz57;csV;mhB`Svk2Z{{uCYIHnqMaK35vQuC|sp>%2Cp)dGkcH5Ks@GMxddun50t>WQ!^LD03+ka<(di cZ&OVrUPaS}+c`#CZzIK0dy85)6SZ#Rzd~Qm7ytkO literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f32f2f8cf71e870f1c694377fb8a5e5e3b25f6f GIT binary patch literal 3069 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f57810fc579e826fc4537d649fed836029c5badb GIT binary patch literal 17291 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

    P-IOg}m$T=E6=6%}j+gl}Bj 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 VJ4?u6@_BC5%*DCSa~I4P{tvuo?0^6O literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01d25ea9a74041c28bae07d43dff34c11e555c4f GIT binary patch literal 8871 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9470dff35693753ad4bc16d84647af4bd27c29bb GIT binary patch literal 4935 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/cache.py new file mode 100644 index 0000000..747277f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/cache.py @@ -0,0 +1,182 @@ +from __future__ import absolute_import + +import logging +import os +import textwrap + +import pip._internal.utils.filesystem as filesystem +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, PipError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List + + +logger = logging.getLogger(__name__) + + +class CacheCommand(Command): + """ + Inspect and manage pip's wheel cache. + + Subcommands: + + - dir: Show the cache directory. + - info: Show information about the cache. + - list: List filenames of packages stored in the cache. + - remove: Remove one or more package from the cache. + - purge: Remove all items from the cache. + + ```` can be a glob expression or a package name. + """ + + ignore_require_venv = True + usage = """ + %prog dir + %prog info + %prog list [] + %prog remove + %prog purge + """ + + def run(self, options, args): + # type: (Values, List[Any]) -> int + handlers = { + "dir": self.get_cache_dir, + "info": self.get_cache_info, + "list": self.list_cache_items, + "remove": self.remove_cache_items, + "purge": self.purge_cache, + } + + if not options.cache_dir: + logger.error("pip cache commands can not " + "function since cache is disabled.") + return ERROR + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def get_cache_dir(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + logger.info(options.cache_dir) + + def get_cache_info(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + num_packages = len(self._find_wheels(options, '*')) + + cache_location = self._wheels_cache_dir(options) + cache_size = filesystem.format_directory_size(cache_location) + + message = textwrap.dedent(""" + Location: {location} + Size: {size} + Number of wheels: {package_count} + """).format( + location=cache_location, + package_count=num_packages, + size=cache_size, + ).strip() + + logger.info(message) + + def list_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if args: + pattern = args[0] + else: + pattern = '*' + + files = self._find_wheels(options, pattern) + + if not files: + logger.info('Nothing cached.') + return + + results = [] + for filename in files: + wheel = os.path.basename(filename) + size = filesystem.format_file_size(filename) + results.append(' - {} ({})'.format(wheel, size)) + logger.info('Cache contents:\n') + logger.info('\n'.join(sorted(results))) + + def remove_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if not args: + raise CommandError('Please provide a pattern') + + files = self._find_wheels(options, args[0]) + if not files: + raise CommandError('No matching packages') + + for filename in files: + os.unlink(filename) + logger.debug('Removed %s', filename) + logger.info('Files removed: %s', len(files)) + + def purge_cache(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + return self.remove_cache_items(options, ['*']) + + def _wheels_cache_dir(self, options): + # type: (Values) -> str + return os.path.join(options.cache_dir, 'wheels') + + def _find_wheels(self, options, pattern): + # type: (Values, str) -> List[str] + wheel_dir = self._wheels_cache_dir(options) + + # The wheel filename format, as specified in PEP 427, is: + # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl + # + # Additionally, non-alphanumeric values in the distribution are + # normalized to underscores (_), meaning hyphens can never occur + # before `-{version}`. + # + # Given that information: + # - If the pattern we're given contains a hyphen (-), the user is + # providing at least the version. Thus, we can just append `*.whl` + # to match the rest of it. + # - If the pattern we're given doesn't contain a hyphen (-), the + # user is only providing the name. Thus, we append `-*.whl` to + # match the hyphen before the version, followed by anything else. + # + # PEP 427: https://www.python.org/dev/peps/pep-0427/ + pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") + + return filesystem.find_files(wheel_dir, pattern) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/check.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..b557ca6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,51 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.operations.check import ( + check_package_set, + create_package_set_from_installed, +) +from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from typing import List, Any + from optparse import Values + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + + usage = """ + %prog [options]""" + + def run(self, options, args): + # type: (Values, List[Any]) -> int + + package_set, parsing_probs = create_package_set_from_installed() + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + write_output( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + write_output( + "%s %s has requirement %s, but you have %s %s.", + project_name, version, req, dep_name, dep_version, + ) + + if missing or conflicting or parsing_probs: + return ERROR + else: + write_output("No broken requirements found.") + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/completion.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..9b99f51 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import + +import sys +import textwrap + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.misc import get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + from optparse import Values + +BASE_COMPLETION = """ +# pip {shell} completion start{script}# pip {shell} completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ + _pip_completion() + {{ + COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) ) + }} + complete -o default -F _pip_completion {prog} + """, + 'zsh': """ + function _pip_completion {{ + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )) + }} + compctl -K _pip_completion {prog} + """, + 'fish': """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c {prog} + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + self.cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + self.cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, '').format( + prog=get_prog()) + ) + print(BASE_COMPLETION.format(script=script, shell=options.shell)) + return SUCCESS + else: + sys.stderr.write( + 'ERROR: You must pass {}\n' .format(' or '.join(shell_options)) + ) + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/configuration.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..f9b3ab7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,284 @@ +import logging +import os +import subprocess + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import ( + Configuration, + get_configuration_files, + kinds, +) +from pip._internal.exceptions import PipError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_prog, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Any, Optional + from optparse import Values + + from pip._internal.configuration import Kind + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """ + Manage local and global configuration. + + Subcommands: + + - list: List the active configuration (or from the file specified) + - edit: Edit the configuration file in an editor + - get: Get the value associated with name + - set: Set the name=value + - unset: Unset the value associated with name + - debug: List the configuration files and values defined under them + + If none of --user, --global and --site are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen on the to the user file by + default. + """ + + ignore_require_venv = True + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + %prog [] debug + """ + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '--editor', + dest='editor', + action='store', + default=None, + help=( + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' + ) + ) + + self.cmd_opts.add_option( + '--global', + dest='global_file', + action='store_true', + default=False, + help='Use the system-wide configuration file only' + ) + + self.cmd_opts.add_option( + '--user', + dest='user_file', + action='store_true', + default=False, + help='Use the user configuration file only' + ) + + self.cmd_opts.add_option( + '--site', + dest='site_file', + action='store_true', + default=False, + help='Use the current environment configuration file only' + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name, + "debug": self.list_config_values, + } + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options, need_value): + # type: (Values, bool) -> Optional[Kind] + file_options = [key for key, value in ( + (kinds.USER, options.user_file), + (kinds.GLOBAL, options.global_file), + (kinds.SITE, options.site_file), + ) if value] + + if not file_options: + if not need_value: + return None + # Default to user, unless there's a site file. + elif any( + os.path.exists(site_config_file) + for site_config_file in get_configuration_files()[kinds.SITE] + ): + return kinds.SITE + else: + return kinds.USER + elif len(file_options) == 1: + return file_options[0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --site, --global) to perform." + ) + + def list_values(self, options, args): + # type: (Values, List[str]) -> None + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + write_output("%s=%r", key, value) + + def get_name(self, options, args): + # type: (Values, List[str]) -> None + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + write_output("%s", value) + + def set_name_value(self, options, args): + # type: (Values, List[str]) -> None + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options, args): + # type: (Values, List[str]) -> None + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def list_config_values(self, options, args): + # type: (Values, List[str]) -> None + """List config key-value pairs across different config files""" + self._get_n_args(args, "debug", n=0) + + self.print_env_var_values() + # Iterate over config files and print if they exist, and the + # key-value pairs present in them if they do + for variant, files in sorted(self.configuration.iter_config_files()): + write_output("%s:", variant) + for fname in files: + with indent_log(): + file_exists = os.path.exists(fname) + write_output("%s, exists: %r", + fname, file_exists) + if file_exists: + self.print_config_file_values(variant) + + def print_config_file_values(self, variant): + # type: (Kind) -> None + """Get key-value pairs from the file of a variant""" + for name, value in self.configuration.\ + get_values_in_config(variant).items(): + with indent_log(): + write_output("%s: %s", name, value) + + def print_env_var_values(self): + # type: () -> None + """Get key-values pairs present as environment variables""" + write_output("%s:", 'env_var') + with indent_log(): + for key, value in sorted(self.configuration.get_environ_vars()): + env_var = 'PIP_{}'.format(key.upper()) + write_output("%s=%r", env_var, value) + + def open_in_editor(self, options, args): + # type: (Values, List[str]) -> None + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + + try: + subprocess.check_call([editor, fname]) + except subprocess.CalledProcessError as e: + raise PipError( + "Editor Subprocess exited with exit code {}" + .format(e.returncode) + ) + + def _get_n_args(self, args, example, n): + # type: (List[str], str, int) -> Any + """Helper to make sure the command got the right number of arguments + """ + if len(args) != n: + msg = ( + 'Got unexpected number of arguments, expected {}. ' + '(example: "{} config {}")' + ).format(n, get_prog(), example) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self): + # type: () -> None + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.exception( + "Unable to save configuration. Please report this as a bug." + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options): + # type: (Values) -> str + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/debug.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/debug.py new file mode 100644 index 0000000..ff369d7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/debug.py @@ -0,0 +1,229 @@ +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +import pip._vendor +from pip._vendor import pkg_resources +from pip._vendor.certifi import where + +from pip import __file__ as pip_location +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_pip_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import ModuleType + from typing import List, Optional, Dict + from optparse import Values + from pip._internal.configuration import Configuration + +logger = logging.getLogger(__name__) + + +def show_value(name, value): + # type: (str, Optional[str]) -> None + logger.info('%s: %s', name, value) + + +def show_sys_implementation(): + # type: () -> None + logger.info('sys.implementation:') + if hasattr(sys, 'implementation'): + implementation = sys.implementation # type: ignore + implementation_name = implementation.name + else: + implementation_name = '' + + with indent_log(): + show_value('name', implementation_name) + + +def create_vendor_txt_map(): + # type: () -> Dict[str, str] + vendor_txt_path = os.path.join( + os.path.dirname(pip_location), + '_vendor', + 'vendor.txt' + ) + + with open(vendor_txt_path) as f: + # Purge non version specifying lines. + # Also, remove any space prefix or suffixes (including comments). + lines = [line.strip().split(' ', 1)[0] + for line in f.readlines() if '==' in line] + + # Transform into "module" -> version dict. + return dict(line.split('==', 1) for line in lines) # type: ignore + + +def get_module_from_module_name(module_name): + # type: (str) -> ModuleType + # Module name can be uppercase in vendor.txt for some reason... + module_name = module_name.lower() + # PATCH: setuptools is actually only pkg_resources. + if module_name == 'setuptools': + module_name = 'pkg_resources' + + __import__( + 'pip._vendor.{}'.format(module_name), + globals(), + locals(), + level=0 + ) + return getattr(pip._vendor, module_name) + + +def get_vendor_version_from_module(module_name): + # type: (str) -> Optional[str] + module = get_module_from_module_name(module_name) + version = getattr(module, '__version__', None) + + if not version: + # Try to find version in debundled module info + # The type for module.__file__ is Optional[str] in + # Python 2, and str in Python 3. The type: ignore is + # added to account for Python 2, instead of a cast + # and should be removed once we drop Python 2 support + pkg_set = pkg_resources.WorkingSet( + [os.path.dirname(module.__file__)] # type: ignore + ) + package = pkg_set.find(pkg_resources.Requirement.parse(module_name)) + version = getattr(package, 'version', None) + + return version + + +def show_actual_vendor_versions(vendor_txt_versions): + # type: (Dict[str, str]) -> None + """Log the actual version and print extra info if there is + a conflict or if the actual version could not be imported. + """ + for module_name, expected_version in vendor_txt_versions.items(): + extra_message = '' + actual_version = get_vendor_version_from_module(module_name) + if not actual_version: + extra_message = ' (Unable to locate actual module version, using'\ + ' vendor.txt specified version)' + actual_version = expected_version + elif actual_version != expected_version: + extra_message = ' (CONFLICT: vendor.txt suggests version should'\ + ' be {})'.format(expected_version) + logger.info('%s==%s%s', module_name, actual_version, extra_message) + + +def show_vendor_versions(): + # type: () -> None + logger.info('vendored library versions:') + + vendor_txt_versions = create_vendor_txt_map() + with indent_log(): + show_actual_vendor_versions(vendor_txt_versions) + + +def show_tags(options): + # type: (Values) -> None + tag_limit = 10 + + target_python = make_target_python(options) + tags = target_python.get_tags() + + # Display the target options that were explicitly provided. + formatted_target = target_python.format_given() + suffix = '' + if formatted_target: + suffix = ' (target: {})'.format(formatted_target) + + msg = 'Compatible tags: {}{}'.format(len(tags), suffix) + logger.info(msg) + + if options.verbose < 1 and len(tags) > tag_limit: + tags_limited = True + tags = tags[:tag_limit] + else: + tags_limited = False + + with indent_log(): + for tag in tags: + logger.info(str(tag)) + + if tags_limited: + msg = ( + '...\n' + '[First {tag_limit} tags shown. Pass --verbose to show all.]' + ).format(tag_limit=tag_limit) + logger.info(msg) + + +def ca_bundle_info(config): + # type: (Configuration) -> str + levels = set() + for key, _ in config.items(): + levels.add(key.split('.')[0]) + + if not levels: + return "Not specified" + + levels_that_override_global = ['install', 'wheel', 'download'] + global_overriding_level = [ + level for level in levels if level in levels_that_override_global + ] + if not global_overriding_level: + return 'global' + + if 'global' in levels: + levels.remove('global') + return ", ".join(levels) + + +class DebugCommand(Command): + """ + Display debug information. + """ + + usage = """ + %prog """ + ignore_require_venv = True + + def add_options(self): + # type: () -> None + cmdoptions.add_target_python_options(self.cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) + self.parser.config.load() + + def run(self, options, args): + # type: (Values, List[str]) -> int + logger.warning( + "This command is only meant for debugging. " + "Do not use this with automation for parsing and getting these " + "details, since the output and options of this command may " + "change without notice." + ) + show_value('pip version', get_pip_version()) + show_value('sys.version', sys.version) + show_value('sys.executable', sys.executable) + show_value('sys.getdefaultencoding', sys.getdefaultencoding()) + show_value('sys.getfilesystemencoding', sys.getfilesystemencoding()) + show_value( + 'locale.getpreferredencoding', locale.getpreferredencoding(), + ) + show_value('sys.platform', sys.platform) + show_sys_implementation() + + show_value("'cert' config value", ca_bundle_info(self.parser.config)) + show_value("REQUESTS_CA_BUNDLE", os.environ.get('REQUESTS_CA_BUNDLE')) + show_value("CURL_CA_BUNDLE", os.environ.get('CURL_CA_BUNDLE')) + show_value("pip._vendor.certifi.where()", where()) + show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) + + show_vendor_versions() + + show_tags(options) + + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/download.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..46e8371 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,143 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path, write_output +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] ... + %prog [options] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + + self.cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into

    ."), + ) + + cmdoptions.add_target_python_options(self.cmd_opts) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + cmdoptions.check_dist_restriction(options) + + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ) + build_delete = (not (options.no_clean or options.build_dir)) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="download", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + download_dir=options.download_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + py_version_info=options.python_version, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + downloaded = ' '.join([req.name # type: ignore + for req in requirement_set.requirements.values() + if req.successfully_downloaded]) + if downloaded: + write_output('Successfully downloaded %s', downloaded) + + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/freeze.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..2071fba --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + + usage = """ + %prog [options]""" + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' {}'.format(', '.join(DEV_PKGS))) + self.cmd_opts.add_option( + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + format_control = FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + cmdoptions.check_list_path_option(options) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + paths=options.path, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip, + exclude_editable=options.exclude_editable, + ) + + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/hash.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..37831c3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + """ + + usage = '%prog [options] ...' + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of {}'.format( + ', '.join(STRONG_HASHES))) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + write_output('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + return SUCCESS + + +def _hash_of_file(path, algorithm): + # type: (str, str) -> str + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/help.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..a2edc29 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + from optparse import Values + + +class HelpCommand(Command): + """Show help for commands""" + + usage = """ + %prog """ + ignore_require_venv = True + + def run(self, options, args): + # type: (Values, List[str]) -> int + from pip._internal.commands import ( + commands_dict, create_command, get_similar_commands, + ) + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(guess)) + + raise CommandError(' - '.join(msg)) + + command = create_command(cmd_name) + command.parser.print_help() + + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/install.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..704e2d6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,749 @@ +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +import site +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import distutils_scheme +from pip._internal.operations.check import check_install_conflicts +from pip._internal.req import install_given_reqs +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.datetime import today_is_later_than +from pip._internal.utils.distutils_args import parse_distutils_args +from pip._internal.utils.filesystem import test_writable_dir +from pip._internal.utils.misc import ( + ensure_dir, + get_installed_version, + get_pip_version, + protect_pip_from_modification_on_windows, + write_output, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import virtualenv_no_global +from pip._internal.wheel_builder import build, should_build_for_install_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Iterable, List, Optional + + from pip._internal.models.format_control import FormatControl + from pip._internal.operations.check import ConflictDetails + from pip._internal.req.req_install import InstallRequirement + from pip._internal.wheel_builder import BinaryAllowedPredicate + + +logger = logging.getLogger(__name__) + + +def get_check_binary_allowed(format_control): + # type: (FormatControl) -> BinaryAllowedPredicate + def check_binary_allowed(req): + # type: (InstallRequirement) -> bool + if req.use_pep517: + return True + canonical_name = canonicalize_name(req.name) + allowed_formats = format_control.get_allowed_formats(canonical_name) + return "binary" in allowed_formats + + return check_binary_allowed + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.pre()) + + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' + ) + cmdoptions.add_target_python_options(self.cmd_opts) + + self.cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)") + self.cmd_opts.add_option( + '--no-user', + dest='use_user_site', + action='store_false', + help=SUPPRESS_HELP) + self.cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + self.cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + self.cmd_opts.add_option(cmdoptions.build_dir()) + + self.cmd_opts.add_option(cmdoptions.src()) + + self.cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + self.cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled ' + '[default: %default]. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + self.cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already ' + 'up-to-date.') + + self.cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages, overwriting them. ' + 'This can break your system if the existing package ' + 'is of a different version or was installed ' + 'with a different package manager!' + ) + + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + + self.cmd_opts.add_option(cmdoptions.install_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) + + self.cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + self.cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + if options.use_user_site and options.target_dir is not None: + raise CommandError("Can not combine '--user' and '--target'") + + cmdoptions.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + cmdoptions.check_dist_restriction(options, check_target=True) + + install_options = options.install_options or [] + + logger.debug("Using %s", get_pip_version()) + options.use_user_site = decide_user_install( + options.use_user_site, + prefix_path=options.prefix_path, + target_dir=options.target_dir, + root_path=options.root_path, + isolated_mode=options.isolated_mode, + ) + + target_temp_dir = None # type: Optional[TempDirectory] + target_temp_dir_path = None # type: Optional[str] + if options.target_dir: + options.ignore_installed = True + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + + # Create a target directory for using with the target option + target_temp_dir = TempDirectory(kind="target") + target_temp_dir_path = target_temp_dir.path + self.enter_context(target_temp_dir) + + global_options = options.global_options or [] + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="install", + globally_managed=True, + ) + + try: + reqs = self.get_requirements(args, options, finder, session) + + reject_location_related_install_options( + reqs, options.install_options + ) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + use_user_site=options.use_user_site, + ) + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + ignore_installed=options.ignore_installed, + ignore_requires_python=options.ignore_requires_python, + force_reinstall=options.force_reinstall, + upgrade_strategy=upgrade_strategy, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=not options.target_dir + ) + + try: + pip_req = requirement_set.get_requirement("pip") + except KeyError: + modifying_pip = False + else: + # If we're not replacing an already installed pip, + # we're not modifying it. + modifying_pip = pip_req.satisfied_by is None + protect_pip_from_modification_on_windows( + modifying_pip=modifying_pip + ) + + check_binary_allowed = get_check_binary_allowed( + finder.format_control + ) + + reqs_to_build = [ + r for r in requirement_set.requirements.values() + if should_build_for_install_command( + r, check_binary_allowed + ) + ] + + _, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + build_options=[], + global_options=[], + ) + + # If we're using PEP 517, we cannot do a direct install + # so we fail here. + pep517_build_failure_names = [ + r.name # type: ignore + for r in build_failures if r.use_pep517 + ] # type: List[str] + if pep517_build_failure_names: + raise InstallationError( + "Could not build wheels for {} which use" + " PEP 517 and cannot be installed directly".format( + ", ".join(pep517_build_failure_names) + ) + ) + + # For now, we just warn about failures building legacy + # requirements, as we'll fall through to a direct + # install for those. + for r in build_failures: + if not r.use_pep517: + r.legacy_install_reason = 8368 + + to_install = resolver.get_installation_order( + requirement_set + ) + + # Check for conflicts in the package set we're installing. + conflicts = None # type: Optional[ConflictDetails] + should_warn_about_conflicts = ( + not options.ignore_dependencies and + options.warn_about_conflicts + ) + if should_warn_about_conflicts: + conflicts = self._determine_conflicts(to_install) + + # Don't warn about script install locations if + # --target has been specified + warn_script_location = options.warn_script_location + if options.target_dir: + warn_script_location = False + + installed = install_given_reqs( + to_install, + install_options, + global_options, + root=options.root_path, + home=target_temp_dir_path, + prefix=options.prefix_path, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + pycompile=options.compile, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir_path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + working_set = pkg_resources.WorkingSet(lib_locations) + + installed.sort(key=operator.attrgetter('name')) + items = [] + for result in installed: + item = result.name + try: + installed_version = get_installed_version( + result.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + + if conflicts is not None: + self._warn_about_conflicts( + conflicts, + new_resolver='2020-resolver' in options.features_enabled, + ) + + installed_desc = ' '.join(items) + if installed_desc: + write_output( + 'Successfully installed %s', installed_desc, + ) + except EnvironmentError as error: + show_traceback = (self.verbosity >= 1) + + message = create_env_error_message( + error, show_traceback, options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) # noqa + + return ERROR + + if options.target_dir: + assert target_temp_dir + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + + return SUCCESS + + def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + # type: (str, TempDirectory, bool) -> None + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + + def _determine_conflicts(self, to_install): + # type: (List[InstallRequirement]) -> Optional[ConflictDetails] + try: + return check_install_conflicts(to_install) + except Exception: + logger.exception( + "Error while checking for conflicts. Please file an issue on " + "pip's issue tracker: https://github.com/pypa/pip/issues/new" + ) + return None + + def _warn_about_conflicts(self, conflict_details, new_resolver): + # type: (ConflictDetails, bool) -> None + package_set, (missing, conflicting) = conflict_details + if not missing and not conflicting: + return + + parts = [] # type: List[str] + if not new_resolver: + parts.append( + "After October 2020 you may experience errors when installing " + "or updating packages. This is because pip will change the " + "way that it resolves dependency conflicts.\n" + ) + parts.append( + "We recommend you use --use-feature=2020-resolver to test " + "your packages with the new resolver before it becomes the " + "default.\n" + ) + elif not today_is_later_than(year=2020, month=7, day=31): + # NOTE: trailing newlines here are intentional + parts.append( + "Pip will install or upgrade your package(s) and its " + "dependencies without taking into account other packages you " + "already have installed. This may cause an uncaught " + "dependency conflict.\n" + ) + form_link = "https://forms.gle/cWKMoDs8sUVE29hz9" + parts.append( + "If you would like pip to take your other packages into " + "account, please tell us here: {}\n".format(form_link) + ) + + # NOTE: There is some duplication here, with commands/check.py + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + message = ( + "{name} {version} requires {requirement}, " + "which is not installed." + ).format( + name=project_name, + version=version, + requirement=dependency[1], + ) + parts.append(message) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + message = ( + "{name} {version} requires {requirement}, but you'll have " + "{dep_name} {dep_version} which is incompatible." + ).format( + name=project_name, + version=version, + requirement=req, + dep_name=dep_name, + dep_version=dep_version, + ) + parts.append(message) + + logger.critical("\n".join(parts)) + + +def get_lib_location_guesses( + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None # type: Optional[str] +): + # type:(...) -> List[str] + scheme = distutils_scheme('', user=user, home=home, root=root, + isolated=isolated, prefix=prefix) + return [scheme['purelib'], scheme['platlib']] + + +def site_packages_writable(root, isolated): + # type: (Optional[str], bool) -> bool + return all( + test_writable_dir(d) for d in set( + get_lib_location_guesses(root=root, isolated=isolated)) + ) + + +def decide_user_install( + use_user_site, # type: Optional[bool] + prefix_path=None, # type: Optional[str] + target_dir=None, # type: Optional[str] + root_path=None, # type: Optional[str] + isolated_mode=False, # type: bool +): + # type: (...) -> bool + """Determine whether to do a user install based on the input options. + + If use_user_site is False, no additional checks are done. + If use_user_site is True, it is checked for compatibility with other + options. + If use_user_site is None, the default behaviour depends on the environment, + which is provided by the other arguments. + """ + # In some cases (config from tox), use_user_site can be set to an integer + # rather than a bool, which 'use_user_site is False' wouldn't catch. + if (use_user_site is not None) and (not use_user_site): + logger.debug("Non-user install by explicit request") + return False + + if use_user_site: + if prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + logger.debug("User install by explicit request") + return True + + # If we are here, user installs have not been explicitly requested/avoided + assert use_user_site is None + + # user install incompatible with --prefix/--target + if prefix_path or target_dir: + logger.debug("Non-user install due to --prefix or --target option") + return False + + # If user installs are not enabled, choose a non-user install + if not site.ENABLE_USER_SITE: + logger.debug("Non-user install because user site-packages disabled") + return False + + # If we have permission for a non-user install, do that, + # otherwise do a user install. + if site_packages_writable(root=root_path, isolated=isolated_mode): + logger.debug("Non-user install because site-packages writeable") + return False + + logger.info("Defaulting to user installation because normal site-packages " + "is not writeable") + return True + + +def reject_location_related_install_options(requirements, options): + # type: (List[InstallRequirement], Optional[List[str]]) -> None + """If any location-changing --install-option arguments were passed for + requirements or on the command-line, then show a deprecation warning. + """ + def format_options(option_names): + # type: (Iterable[str]) -> List[str] + return ["--{}".format(name.replace("_", "-")) for name in option_names] + + offenders = [] + + for requirement in requirements: + install_options = requirement.install_options + location_options = parse_distutils_args(install_options) + if location_options: + offenders.append( + "{!r} from {}".format( + format_options(location_options.keys()), requirement + ) + ) + + if options: + location_options = parse_distutils_args(options) + if location_options: + offenders.append( + "{!r} from command line".format( + format_options(location_options.keys()) + ) + ) + + if not offenders: + return + + raise CommandError( + "Location-changing options found in --install-option: {}." + " This is unsupported, use pip-level options like --user," + " --prefix, --root, and --target instead.".format( + "; ".join(offenders) + ) + ) + + +def create_env_error_message(error, show_traceback, using_user_site): + # type: (EnvironmentError, bool, bool) -> str + """Format an error message for an EnvironmentError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an EnvironmentError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not using_user_site: + parts.extend([ + user_option_part, " or ", + permissions_part.lower(), + ]) + else: + parts.append(permissions_part) + parts.append(".\n") + + return "".join(parts).strip() + "\n" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/list.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..20e9bff --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,320 @@ +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six + +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.utils.misc import ( + dist_is_editable, + get_installed_distributions, + tabulate, + write_output, +) +from pip._internal.utils.packaging import get_installer +from pip._internal.utils.parallel import map_multithread +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Set, Tuple, Iterator + + from pip._internal.network.session import PipSession + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +class ListCommand(IndexGroupCommand): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + + ignore_require_venv = True + usage = """ + %prog [options]""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + self.cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + self.cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + self.cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + self.cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="columns", + choices=('columns', 'freeze', 'json'), + help="Select the output format among: columns (default), freeze, " + "or json", + ) + + self.cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + self.cmd_opts.add_option( + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', + ) + self.cmd_opts.add_option( + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', + default=True, + ) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, self.parser + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + def _build_package_finder(self, options, session): + # type: (Values, PipSession) -> PackageFinder + """ + Create a package finder appropriate to this list command. + """ + link_collector = LinkCollector.create(session, options=options) + + # Pass allow_yanked=False to ignore yanked versions. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=options.pre, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + cmdoptions.check_list_path_option(options) + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + paths=options.path, + ) + + # get_not_required must be called firstly in order to find and + # filter out all dependencies correctly. Otherwise a package + # can't be identified as requirement because some parent packages + # could be filtered out before. + if options.not_required: + packages = self.get_not_required(packages, options) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + self.output_package_listing(packages, options) + return SUCCESS + + def get_outdated(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def get_not_required(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + dep_keys = set() # type: Set[Distribution] + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.key not in dep_keys}) + + def iter_packages_latest_infos(self, packages, options): + # type: (List[Distribution], Values) -> Iterator[Distribution] + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + + def latest_info(dist): + # type: (Distribution) -> Distribution + typ = 'unknown' + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + evaluator = finder.make_candidate_evaluator( + project_name=dist.project_name, + ) + best_candidate = evaluator.sort_best_candidate(all_candidates) + if best_candidate is None: + return None + + remote_version = best_candidate.version + if best_candidate.link.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + return dist + + for dist in map_multithread(latest_info, packages): + if dist is not None: + yield dist + + def output_package_listing(self, packages, options): + # type: (List[Distribution], Values) -> None + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + if options.verbose >= 1: + write_output("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + write_output("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + write_output(format_for_json(packages, options)) + + def output_package_listing_columns(self, data, header): + # type: (List[List[str]], List[str]) -> None + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + write_output(val) + + +def format_for_columns(pkgs, options): + # type: (List[Distribution], Values) -> Tuple[List[List[str]], List[str]] + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if options.verbose >= 1 or dist_is_editable(proj): + row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + # type: (List[Distribution], Values) -> str + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/search.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..ff09472 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,160 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap +from collections import OrderedDict + +from pip._vendor import pkg_resources +from pip._vendor.packaging.version import parse as parse_version +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.network.xmlrpc import PipXmlrpcTransport +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_distribution, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Dict, Optional + from typing_extensions import TypedDict + TransformedHit = TypedDict( + 'TransformedHit', + {'name': str, 'summary': str, 'versions': List[str]}, + ) + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command, SessionCommandMixin): + """Search for PyPI packages whose name or summary contains .""" + + usage = """ + %prog [options] """ + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-i', '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + # type: (List[str], Values) -> List[Dict[str, str]] + index_url = options.index + + session = self.get_default_session(options) + + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + # type: (List[Dict[str, str]]) -> List[TransformedHit] + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = OrderedDict() # type: OrderedDict[str, TransformedHit] + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + + return list(packages.values()) + + +def print_results(hits, name_column_width=None, terminal_width=None): + # type: (List[TransformedHit], Optional[int], Optional[int]) -> None + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + latest = highest_version(hit.get('versions', ['-'])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary_lines = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join( + summary_lines) + + line = '{name_latest:{name_column_width}} - {summary}'.format( + name_latest='{name} ({latest})'.format(**locals()), + **locals()) + try: + write_output(line) + if name in installed_packages: + dist = get_distribution(name) + assert dist is not None + with indent_log(): + if dist.version == latest: + write_output('INSTALLED: %s (latest)', dist.version) + else: + write_output('INSTALLED: %s', dist.version) + if parse_version(latest).pre: + write_output('LATEST: %s (pre-release; install' + ' with "pip install --pre")', latest) + else: + write_output('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + # type: (List[str]) -> str + return max(versions, key=parse_version) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/show.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..3892c59 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,186 @@ +from __future__ import absolute_import + +import logging +import os +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List, Dict, Iterator + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + + usage = """ + %prog [options] ...""" + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS + + +def search_packages_info(query): + # type: (List[str]) -> Iterator[Dict[str, str]] + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) for name in query] + missing = sorted( + [name for name, pkg in zip(query, query_names) if pkg not in installed] + ) + if missing: + logger.warning('Package(s) not found: %s', ', '.join(missing)) + + def get_requiring_packages(package_name): + # type: (str) -> List[str] + canonical_name = canonicalize_name(package_name) + return [ + pkg.project_name for pkg in pkg_resources.working_set + if canonical_name in + [canonicalize_name(required.name) for required in + pkg.requires()] + ] + + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + 'required_by': get_requiring_packages(dist.project_name) + } + file_list = None + metadata = '' + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [line.split(',')[0] for line in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): + # type: (Iterator[Dict[str, str]], bool, bool) -> bool + """ + Print the information from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + write_output("---") + + write_output("Name: %s", dist.get('name', '')) + write_output("Version: %s", dist.get('version', '')) + write_output("Summary: %s", dist.get('summary', '')) + write_output("Home-page: %s", dist.get('home-page', '')) + write_output("Author: %s", dist.get('author', '')) + write_output("Author-email: %s", dist.get('author-email', '')) + write_output("License: %s", dist.get('license', '')) + write_output("Location: %s", dist.get('location', '')) + write_output("Requires: %s", ', '.join(dist.get('requires', []))) + write_output("Required-by: %s", ', '.join(dist.get('required_by', []))) + + if verbose: + write_output("Metadata-Version: %s", + dist.get('metadata-version', '')) + write_output("Installer: %s", dist.get('installer', '')) + write_output("Classifiers:") + for classifier in dist.get('classifiers', []): + write_output(" %s", classifier) + write_output("Entry-points:") + for entry in dist.get('entry_points', []): + write_output(" %s", entry.strip()) + if list_files: + write_output("Files:") + for line in dist.get('files', []): + write_output(" %s", line.strip()) + if "files" not in dist: + write_output("Cannot locate installed-files.txt") + return results_printed diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..3371fe4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,95 @@ +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import ( + install_req_from_line, + install_req_from_parsed_requirement, +) +from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +class UninstallCommand(Command, SessionCommandMixin): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + + usage = """ + %prog [options] ... + %prog [options] -r ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + session = self.get_default_session(options) + + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, + options=options, + session=session): + req = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + 'You must give at least one requirement to {self.name} (see ' + '"pip help {self.name}")'.format(**locals()) + ) + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() + + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..0f71856 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import logging +import os +import shutil + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel_builder import build, should_build_for_wheel_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + + self.cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into , where the default is the " + "current working directory."), + ) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", + ) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + self.cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + self.cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + self.cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + cmdoptions.check_install_build_global(options) + + session = self.get_default_session(options) + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + options.wheel_dir = normalize_path(options.wheel_dir) + ensure_dir(options.wheel_dir) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="wheel", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + wheel_download_dir=options.wheel_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + ignore_requires_python=options.ignore_requires_python, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + reqs_to_build = [ + r for r in requirement_set.requirements.values() + if should_build_for_wheel_command(r) + ] + + # build wheels + build_successes, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + for req in build_successes: + assert req.link and req.link.is_wheel + assert req.local_file_path + # copy from cache to target directory + try: + shutil.copy(req.local_file_path, options.wheel_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + build_failures.append(req) + if len(build_failures) != 0: + raise CommandError( + "Failed to build one or more wheels" + ) + + return SUCCESS diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/configuration.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..e49a5f4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/configuration.py @@ -0,0 +1,418 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import locale +import logging +import os +import sys + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, + ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser +from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, NewType, Optional, Tuple + ) + + RawConfigParser = configparser.RawConfigParser # Shorthand + Kind = NewType("Kind", str) + +logger = logging.getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name): + # type: (str) -> str + """Make a name consistent regardless of source (environment or file) + """ + name = name.lower().replace('_', '-') + if name.startswith('--'): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name): + # type: (str) -> List[str] + if "." not in name: + error_message = ( + "Key does not contain dot separated section and key. " + "Perhaps you wanted to use 'global.{}' instead?" + ).format(name) + raise ConfigurationError(error_message) + return name.split(".", 1) + + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + SITE="site", # [Virtual] Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) + + +CONFIG_BASENAME = 'pip.ini' if WINDOWS else 'pip.conf' + + +def get_configuration_files(): + # type: () -> Dict[Kind, List[str]] + global_config_files = [ + os.path.join(path, CONFIG_BASENAME) + for path in appdirs.site_config_dirs('pip') + ] + + site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME) + legacy_config_file = os.path.join( + expanduser('~'), + 'pip' if WINDOWS else '.pip', + CONFIG_BASENAME, + ) + new_config_file = os.path.join( + appdirs.user_config_dir("pip"), CONFIG_BASENAME + ) + return { + kinds.GLOBAL: global_config_files, + kinds.SITE: [site_config_file], + kinds.USER: [legacy_config_file, new_config_file], + } + + +class Configuration(object): + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated, load_only=None): + # type: (bool, Optional[Kind]) -> None + super(Configuration, self).__init__() + + _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.SITE, None] + if load_only not in _valid_load_only: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, _valid_load_only[:-1])) + ) + ) + self.isolated = isolated + self.load_only = load_only + + # The order here determines the override order. + self._override_order = [ + kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR + ] + + self._ignore_env_names = ["version", "help"] + + # Because we keep track of where we got the data from + self._parsers = { + variant: [] for variant in self._override_order + } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] + self._config = { + variant: {} for variant in self._override_order + } # type: Dict[Kind, Dict[str, Any]] + self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] + + def load(self): + # type: () -> None + """Loads configuration from configuration files and environment + """ + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self): + # type: () -> Optional[str] + """Returns the file with highest priority in configuration + """ + assert self.load_only is not None, \ + "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self): + # type: () -> Iterable[Tuple[str, Any]] + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key): + # type: (str) -> Any + """Get a value from the configuration. + """ + try: + return self._dictionary[key] + except KeyError: + raise ConfigurationError("No such key - {}".format(key)) + + def set_value(self, key, value): + # type: (str, Any) -> None + """Modify a value in the configuration. + """ + self._ensure_have_load_only() + + assert self.load_only + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key): + # type: (str) -> None + """Unset a value in the configuration.""" + self._ensure_have_load_only() + + assert self.load_only + if key not in self._config[self.load_only]: + raise ConfigurationError("No such key - {}".format(key)) + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + if not (parser.has_section(section) + and parser.remove_option(section, name)): + # The option was not removed. + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + # The section may be empty after the option was removed. + if not parser.items(section): + parser.remove_section(section) + self._mark_as_modified(fname, parser) + + del self._config[self.load_only][key] + + def save(self): + # type: () -> None + """Save the current in-memory state. + """ + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + with open(fname, "w") as f: + parser.write(f) + + # + # Private routines + # + + def _ensure_have_load_only(self): + # type: () -> None + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self): + # type: () -> Dict[str, Any] + """A dictionary representing the loaded configuration. + """ + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in self._override_order: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self): + # type: () -> None + """Loads configuration from configuration files + """ + config_files = dict(self.iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug( + "Skipping file '%s' (variant: %s)", fname, variant + ) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant, fname): + # type: (Kind, str) -> RawConfigParser + logger.debug("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname): + # type: (str) -> RawConfigParser + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + try: + parser.read(fname) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason="contains invalid {} characters".format( + locale.getpreferredencoding(False) + ), + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self): + # type: () -> None + """Loads configuration from environment variables + """ + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self.get_environ_vars()) + ) + + def _normalized_keys(self, section, items): + # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def get_environ_vars(self): + # type: () -> Iterable[Tuple[str, str]] + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + should_be_yielded = ( + key.startswith("PIP_") and + key[4:].lower() not in self._ignore_env_names + ) + if should_be_yielded: + yield key[4:].lower(), val + + # XXX: This is patched in the tests. + def iter_config_files(self): + # type: () -> Iterable[Tuple[Kind, List[str]]] + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. + """ + # SMELL: Move the conditions out of this function + + # environment variables have the lowest priority + config_file = os.environ.get('PIP_CONFIG_FILE', None) + if config_file is not None: + yield kinds.ENV, [config_file] + else: + yield kinds.ENV, [] + + config_files = get_configuration_files() + + # at the base we have any global configuration + yield kinds.GLOBAL, config_files[kinds.GLOBAL] + + # per-user configuration next + should_load_user_config = not self.isolated and not ( + config_file and os.path.exists(config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, config_files[kinds.USER] + + # finally virtualenv configuration first trumping others + yield kinds.SITE, config_files[kinds.SITE] + + def get_values_in_config(self, variant): + # type: (Kind) -> Dict[str, Any] + """Get values present in a config file""" + return self._config[variant] + + def _get_parser_to_modify(self): + # type: () -> Tuple[str, RawConfigParser] + # Determine which parser to modify + assert self.load_only + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname, parser): + # type: (str, RawConfigParser) -> None + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) + + def __repr__(self): + # type: () -> str + return "{}({!r})".format(self.__class__.__name__, self._dictionary) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py new file mode 100644 index 0000000..d5c1afc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py @@ -0,0 +1,24 @@ +from pip._internal.distributions.sdist import SourceDistribution +from pip._internal.distributions.wheel import WheelDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.distributions.base import AbstractDistribution + from pip._internal.req.req_install import InstallRequirement + + +def make_distribution_for_install_requirement(install_req): + # type: (InstallRequirement) -> AbstractDistribution + """Returns a Distribution for the given InstallRequirement + """ + # Editable requirements will always be source distributions. They use the + # legacy logic until we create a modern standard for them. + if install_req.editable: + return SourceDistribution(install_req) + + # If it's a wheel, it's a WheelDistribution + if install_req.is_wheel: + return WheelDistribution(install_req) + + # Otherwise, a SourceDistribution + return SourceDistribution(install_req) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c479f8a77e54c91aa63ad1c0b1f5ad8ac3a08d3 GIT binary patch literal 884 zcmaJ<&2G~`5Z;aR<0LH-6($S7HE(G-k z55Sd2;3;?yUpes#aADRV;1r3mW;}o1&)>|l{rv$!`F!z{eRT-=W`m}*FgQZhFVLhU zrGjKMqSX2=(aN02seMPZvrg2Z1Ul*nYi-fZdQq>gb%dMsqdxlGwD*xjgVdc7zyBRi zBtESUW_hU+IOf_YJ}(W=C1!8FUIK_~$%B_?ug>D}vnS){@$_^uIhj1g)?1I~_$f=w z_3jg?4HIGttCB0oK$?VF(BEQx%WsUT@1waQ=S1Bw^oo3-GaI$k9ph}GZPusn52s*C zCAG)ACZhKySDsmdx8Ro`y??<69*W%I3y<@pw45-RL%3oie2w5;0g7+Z#oB;2aiQ{aNKCj1M<6dl z!RIzy=JG-CFx1?@s9?zjTYwG=UW75129#tXOdDI#VT=WC;y5VQm7lQ-h@15o2Y?Or z%l(g1y+DHa8aMnxq7&8bCD)OFI-D53^&a>!l?YDr6LbC+w9 z>qn4NZ6KFkdhD$|`Y-LZr~ZZZ(iuu}q|`74Qo|vK{AT7GF6;F=1lsQh|FYj2g#3k* z)do899dx5%;DmdUB>3-3FZUDQLphK^9wwnjM5v#`Jd};RnKa#6L$>lLiJaV&?Yxt8 zoZON(@@~=v8()z9-kJB9@QAn132%#yv*652Ho3P;`klYwUNY``b&qjQb75I3nK7Wa z{p97-mvsBbN83NqXD`O%o$+JPMn9J}Qw5VCcOPcP>TFV>1}%09W0{oC#9@_bk&D8D zw)d2!2W%>S$OTk(V$5j)@FJ*DFrE4a*xX`Ib5vxyWj(>B`! zb2IF8Vrb82BGzx6G0u_|r*`&#OR&auDb=z0W^Nu2;$so#m9g;zmhlFwVlJ%Ec~-z= z{rI?Gxp4Qw)%O4+7^kfG+{UTWTBKH<&SB3j3snrNa;h1}ZASDLpy5_@u$)KT#K)PP z#YWhwT=V72OKeOUwFJ1l{#~4zUNd%ws>&{-hv5MJWOHgP54nVJ4*{XK20Vm=m{A3W zOIO9fW}xDt6u^!m&4lS+7rYdVEyOZj&2X$H`w*U3oRrES6u4@MflbsAKlMChHgR_h`-^zx_edS443|;9`0K-l(BC^H~6|)az=iEvfU-R4O4Lay|J=8c`4l! zAowG#mF|1G2dnz#u7Z0XX3!^HX3PV>he+GMuOIPsf{*@T7w@)#x%ZBoG z$n|a5&<`BWy1D1xt9o-%Ws=j(D9MoC?>ca|)S_fstig5|RuJ!=2} literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1902925379d38ac78744ce286f8603f9d456f7ad GIT binary patch literal 1280 zcmaJ=&2G~`5MIYl;-(3R3XnhoS=@4neJB?sgo;`y6(UsqIn{DN9Zg0%855XaADR?D+g6wX=mqe$1~rzX1!jA;QDd$m;G)K^3xdg<)E>FTOXj{ zgj+F5tkANI-iRBC9oiNFTWz3i$4=4=n`O+2TS+@?mwGdHla+AABHM(wc>9v@HdtTn zu*2Pb(qH+7l}XweBm zoW-bj|GUxKWzh+nzFPyWQ$wLK5XmJuYxE0#G!R<>DI^5w+Je$0Q{k4ln zJ5uK?j)B*|bMb8tG6foBu019_ah>F4)z|$_$HR3n724w|XiuiOCjmHS&po`FF~!Ub z9@Abv1y8~0Oel;CIZjnl#ikf!u>!{DWw{Z6^rAG$W;t-bbVXKeI-{GtiCeoUzK~;b zZ0(aPYuIlTHh!e&YOJlt0yrJb5kE&mJia2Ti|K#92ZtJz4x%ZOY?4aOf~_=~C1%bb zN+CF913U(Bo`DjTJDcaAbDF927@|C=%mK)=AQmHIPE)z=zY4U-VX%Nl2bstMDr61{ zX$*LcbsgNG>1Xre`2?`|$%G=@=}bkSX+3+*jA}?FjfG$AhONn#WMO0fadvg zvQLyjvc#uvt5wvC*0_9J#r@Gt#GDG9#wB58QYCsO?^#7OEOks1$QfrjdyFa9t*_tS z`knWO{Z`>pDp>-Q79C2Hl+R+LcPTxcv3L<_Q_9nbQgzSF@xTZ}c1@UORU3twPPb6F zmeX>b(l_j{79P^&-_YgP3DEwC;hQlO6i=7a@F$M7pU*QPC(1xuv=)YeHhfZMpMm75 y@|PG^O_Tg$xtf9e++RE(S}~wZuVt6DY0BYB{N2|TByS9-+W5Oxx8dN%)BXn_ux>d3 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c058496b5a00bac1d5c48d6b0e562faa8d7d907 GIT binary patch literal 3559 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/base.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/base.py new file mode 100644 index 0000000..b836b98 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/base.py @@ -0,0 +1,45 @@ +import abc + +from pip._vendor.six import add_metaclass + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + from pip._internal.req import InstallRequirement + from pip._internal.index.package_finder import PackageFinder + + +@add_metaclass(abc.ABCMeta) +class AbstractDistribution(object): + """A base class for handling installable artifacts. + + The requirements for anything installable are as follows: + + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req): + # type: (InstallRequirement) -> None + super(AbstractDistribution, self).__init__() + self.req = req + + @abc.abstractmethod + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + raise NotImplementedError() + + @abc.abstractmethod + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + raise NotImplementedError() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/installed.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/installed.py new file mode 100644 index 0000000..0d15bf4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/installed.py @@ -0,0 +1,24 @@ +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +class InstalledDistribution(AbstractDistribution): + """Represents an installed package. + + This does not need any preparation as the required information has already + been computed. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + return self.req.satisfied_by + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py new file mode 100644 index 0000000..be3d7d9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py @@ -0,0 +1,104 @@ +import logging + +from pip._internal.build_env import BuildEnvironment +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.exceptions import InstallationError +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Set, Tuple + + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +logger = logging.getLogger(__name__) + + +class SourceDistribution(AbstractDistribution): + """Represents a source distribution. + + The preparation step for these needs metadata for the packages to be + generated, either using PEP 517 or using the legacy `setup.py egg_info`. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + return self.req.get_dist() + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + # Load pyproject.toml, to determine whether PEP 517 is to be used + self.req.load_pyproject_toml() + + # Set up the build isolation, if this requirement should be isolated + should_isolate = self.req.use_pep517 and build_isolation + if should_isolate: + self._setup_isolation(finder) + + self.req.prepare_metadata() + + def _setup_isolation(self, finder): + # type: (PackageFinder) -> None + def _raise_conflicts(conflicting_with, conflicting_reqs): + # type: (str, Set[Tuple[str, str]]) -> None + format_string = ( + "Some build dependencies for {requirement} " + "conflict with {conflicting_with}: {description}." + ) + error_message = format_string.format( + requirement=self.req, + conflicting_with=conflicting_with, + description=', '.join( + '{} is incompatible with {}'.format(installed, wanted) + for installed, wanted in sorted(conflicting) + ) + ) + raise InstallationError(error_message) + + # Isolate in a BuildEnvironment and install the build-time + # requirements. + pyproject_requires = self.req.pyproject_requires + assert pyproject_requires is not None + + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, pyproject_requires, 'overlay', + "Installing build dependencies" + ) + conflicting, missing = self.req.build_env.check_requirements( + self.req.requirements_to_check + ) + if conflicting: + _raise_conflicts("PEP 517/518 supported requirements", + conflicting) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and " + "pip cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))) + ) + # Install any extra build dependencies that the backend requests. + # This must be done in a second pass, as the pyproject.toml + # dependencies must be installed before we can call the backend. + with self.req.build_env: + runner = runner_with_spinner_message( + "Getting requirements to build wheel" + ) + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + reqs = backend.get_requires_for_build_wheel() + + conflicting, missing = self.req.build_env.check_requirements(reqs) + if conflicting: + _raise_conflicts("the backend dependencies", conflicting) + self.req.build_env.install_requirements( + finder, missing, 'normal', + "Installing backend dependencies" + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py new file mode 100644 index 0000000..bf3482b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py @@ -0,0 +1,36 @@ +from zipfile import ZipFile + +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +class WheelDistribution(AbstractDistribution): + """Represents a wheel distribution. + + This does not need any preparation as wheels can be directly unpacked. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + """Loads the metadata from the wheel file into memory and returns a + Distribution that uses it, not relying on the wheel file or + requirement. + """ + # Set as part of preparation during download. + assert self.req.local_file_path + # Wheels are never unnamed. + assert self.req.name + + with ZipFile(self.req.local_file_path, allowZip64=True) as z: + return pkg_resources_distribution_for_wheel( + z, self.req.name, self.req.local_file_path + ) + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/exceptions.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..3f26215 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,381 @@ +"""Exceptions used throughout package""" + +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional, List, Dict, Text + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Response, Request + from pip._vendor.six import PY3 + from pip._vendor.six.moves import configparser + + from pip._internal.req.req_install import InstallRequirement + + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + + +class PipError(Exception): + """Base pip exception""" + + +class ConfigurationError(PipError): + """General exception in configuration""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class NoneMetadataError(PipError): + """ + Raised when accessing "METADATA" or "PKG-INFO" metadata for a + pip._vendor.pkg_resources.Distribution object and + `dist.has_metadata('METADATA')` returns True but + `dist.get_metadata('METADATA')` returns None (and similarly for + "PKG-INFO"). + """ + + def __init__(self, dist, metadata_name): + # type: (Distribution, str) -> None + """ + :param dist: A Distribution object. + :param metadata_name: The name of the metadata being accessed + (can be "METADATA" or "PKG-INFO"). + """ + self.dist = dist + self.metadata_name = metadata_name + + def __str__(self): + # type: () -> str + # Use `dist` in the error message because its stringification + # includes more information, like the version and location. + return ( + 'None {} metadata found for distribution: {}'.format( + self.metadata_name, self.dist, + ) + ) + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class SubProcessError(PipError): + """Raised when there is an error raised while executing a + command in subprocess""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class NetworkConnectionError(PipError): + """HTTP connection error""" + + def __init__(self, error_msg, response=None, request=None): + # type: (Text, Response, Request) -> None + """ + Initialize NetworkConnectionError with `request` and `response` + objects. + """ + self.response = response + self.request = request + self.error_msg = error_msg + if (self.response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(NetworkConnectionError, self).__init__( + error_msg, response, request) + + def __str__(self): + # type: () -> str + return str(self.error_msg) + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class MetadataInconsistent(InstallationError): + """Built metadata contains inconsistent information. + + This is raised when the metadata contains values (e.g. name and version) + that do not match the information previously obtained from sdist filename + or user-supplied ``#egg=`` value. + """ + def __init__(self, ireq, field, built): + # type: (InstallRequirement, str, Any) -> None + self.ireq = ireq + self.field = field + self.built = built + + def __str__(self): + # type: () -> str + return "Requested {} has different {} in metadata: {!r}".format( + self.ireq, self.field, self.built, + ) + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + # type: () -> None + self.errors = [] # type: List[HashError] + + def append(self, error): + # type: (HashError) -> None + self.errors.append(error) + + def __str__(self): + # type: () -> str + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + return '' + + def __nonzero__(self): + # type: () -> bool + return bool(self.errors) + + def __bool__(self): + # type: () -> bool + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None # type: Optional[InstallRequirement] + head = '' + order = None # type: Optional[int] + + def body(self): + # type: () -> str + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + its link already populated by the resolver's _populate_link(). + + """ + return ' {}'.format(self._requirement_name()) + + def __str__(self): + # type: () -> str + return '{}\n{}'.format(self.head, self.body()) + + def _requirement_name(self): + # type: () -> str + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + # type: (str) -> None + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + # type: () -> str + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' {} --hash={}:{}'.format(package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + # type: (Dict[str, List[str]], Dict[str, _Hash]) -> None + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + # type: () -> str + return ' {}:\n{}'.format(self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + # type: () -> str + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # type: (str) -> chain[str] + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] # type: List[str] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected {} {}'.format(next(prefix), e)) + for e in expecteds) + lines.append(' Got {}\n'.format( + self.gots[hash_name].hexdigest())) + return '\n'.join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file + """ + + def __init__(self, reason="could not be loaded", fname=None, error=None): + # type: (str, Optional[str], Optional[configparser.Error]) -> None + super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self): + # type: () -> str + if self.fname is not None: + message_part = " in {}.".format(self.fname) + else: + assert self.error is not None + message_part = ".\n{}\n".format(self.error) + return "Configuration file {}{}".format(self.reason, message_part) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__init__.py new file mode 100644 index 0000000..7a17b7b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__init__.py @@ -0,0 +1,2 @@ +"""Index interaction code +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc0fe1f3c1802559ca036e7d7ba632a06e93ab16 GIT binary patch literal 262 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb53fd1f1376264ed098fba43de1d2fa51b799b7 GIT binary patch literal 26015 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 Xea4u~e|&0sY8I(W_~pOR{DJ=q3;7-) literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/collector.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/collector.py new file mode 100644 index 0000000..6c35fc6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/collector.py @@ -0,0 +1,692 @@ +""" +The main purpose of this module is to expose LinkCollector.collect_links(). +""" + +import cgi +import functools +import itertools +import logging +import mimetypes +import os +import re +from collections import OrderedDict + +from pip._vendor import html5lib, requests +from pip._vendor.distlib.compat import unescape +from pip._vendor.requests.exceptions import RetryError, SSLError +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS +from pip._internal.utils.misc import pairwise, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import ( + Callable, Iterable, List, MutableMapping, Optional, + Protocol, Sequence, Tuple, TypeVar, Union, + ) + import xml.etree.ElementTree + + from pip._vendor.requests import Response + + from pip._internal.network.session import PipSession + + HTMLElement = xml.etree.ElementTree.Element + ResponseHeaders = MutableMapping[str, str] + + # Used in the @lru_cache polyfill. + F = TypeVar('F') + + class LruCache(Protocol): + def __call__(self, maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + raise NotImplementedError + + +logger = logging.getLogger(__name__) + + +# Fallback to noop_lru_cache in Python 2 +# TODO: this can be removed when python 2 support is dropped! +def noop_lru_cache(maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + def _wrapper(f): + # type: (F) -> F + return f + return _wrapper + + +_lru_cache = getattr(functools, "lru_cache", noop_lru_cache) # type: LruCache + + +def _match_vcs_scheme(url): + # type: (str) -> Optional[str] + """Look for VCS schemes in the URL. + + Returns the matched VCS scheme, or None if there's no match. + """ + for scheme in vcs.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + return scheme + return None + + +def _is_url_like_archive(url): + # type: (str) -> bool + """Return whether the URL looks like an archive. + """ + filename = Link(url).filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + return True + return False + + +class _NotHTML(Exception): + def __init__(self, content_type, request_desc): + # type: (str, str) -> None + super(_NotHTML, self).__init__(content_type, request_desc) + self.content_type = content_type + self.request_desc = request_desc + + +def _ensure_html_header(response): + # type: (Response) -> None + """Check the Content-Type header to ensure the response contains HTML. + + Raises `_NotHTML` if the content type is not text/html. + """ + content_type = response.headers.get("Content-Type", "") + if not content_type.lower().startswith("text/html"): + raise _NotHTML(content_type, response.request.method) + + +class _NotHTTP(Exception): + pass + + +def _ensure_html_response(url, session): + # type: (str, PipSession) -> None + """Send a HEAD request to the URL, and ensure the response contains HTML. + + Raises `_NotHTTP` if the URL is not available for a HEAD request, or + `_NotHTML` if the content type is not text/html. + """ + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in {'http', 'https'}: + raise _NotHTTP() + + resp = session.head(url, allow_redirects=True) + raise_for_status(resp) + + _ensure_html_header(resp) + + +def _get_html_response(url, session): + # type: (str, PipSession) -> Response + """Access an HTML page with GET, and return the response. + + This consists of three parts: + + 1. If the URL looks suspiciously like an archive, send a HEAD first to + check the Content-Type is HTML, to avoid downloading a large file. + Raise `_NotHTTP` if the content type cannot be determined, or + `_NotHTML` if it is not HTML. + 2. Actually perform the request. Raise HTTP exceptions on network failures. + 3. Check the Content-Type header to make sure we got HTML, and raise + `_NotHTML` otherwise. + """ + if _is_url_like_archive(url): + _ensure_html_response(url, session=session) + + logger.debug('Getting page %s', redact_auth_from_url(url)) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + raise_for_status(resp) + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + _ensure_html_header(resp) + + return resp + + +def _get_encoding_from_headers(headers): + # type: (ResponseHeaders) -> Optional[str] + """Determine if we have any encoding information in our headers. + """ + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + if "charset" in params: + return params['charset'] + return None + + +def _determine_base_url(document, page_url): + # type: (HTMLElement, str) -> str + """Determine the HTML document's base URL. + + This looks for a ```` tag in the HTML document. If present, its href + attribute denotes the base URL of anchor tags in the document. If there is + no such tag (or if it does not have a valid href attribute), the HTML + file's URL is used as the base URL. + + :param document: An HTML document representation. The current + implementation expects the result of ``html5lib.parse()``. + :param page_url: The URL of the HTML document. + """ + for base in document.findall(".//base"): + href = base.get("href") + if href is not None: + return href + return page_url + + +def _clean_url_path_part(part): + # type: (str) -> str + """ + Clean a "part" of a URL path (i.e. after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + return urllib_parse.quote(urllib_parse.unquote(part)) + + +def _clean_file_url_path(part): + # type: (str) -> str + """ + Clean the first part of a URL path that corresponds to a local + filesystem path (i.e. the first part after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + # Also, on Windows the path part might contain a drive letter which + # should not be quoted. On Linux where drive letters do not + # exist, the colon should be quoted. We rely on urllib.request + # to do the right thing here. + return urllib_request.pathname2url(urllib_request.url2pathname(part)) + + +# percent-encoded: / +_reserved_chars_re = re.compile('(@|%2F)', re.IGNORECASE) + + +def _clean_url_path(path, is_local_path): + # type: (str, bool) -> str + """ + Clean the path portion of a URL. + """ + if is_local_path: + clean_func = _clean_file_url_path + else: + clean_func = _clean_url_path_part + + # Split on the reserved characters prior to cleaning so that + # revision strings in VCS URLs are properly preserved. + parts = _reserved_chars_re.split(path) + + cleaned_parts = [] + for to_clean, reserved in pairwise(itertools.chain(parts, [''])): + cleaned_parts.append(clean_func(to_clean)) + # Normalize %xx escapes (e.g. %2f -> %2F) + cleaned_parts.append(reserved.upper()) + + return ''.join(cleaned_parts) + + +def _clean_link(url): + # type: (str) -> str + """ + Make sure a link is fully quoted. + For example, if ' ' occurs in the URL, it will be replaced with "%20", + and without double-quoting other characters. + """ + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. + result = urllib_parse.urlparse(url) + # If the netloc is empty, then the URL refers to a local filesystem path. + is_local_path = not result.netloc + path = _clean_url_path(result.path, is_local_path=is_local_path) + return urllib_parse.urlunparse(result._replace(path=path)) + + +def _create_link_from_element( + anchor, # type: HTMLElement + page_url, # type: str + base_url, # type: str +): + # type: (...) -> Optional[Link] + """ + Convert an anchor element in a simple repository page to a Link. + """ + href = anchor.get("href") + if not href: + return None + + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + + yanked_reason = anchor.get('data-yanked') + if yanked_reason: + # This is a unicode string in Python 2 (and 3). + yanked_reason = unescape(yanked_reason) + + link = Link( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + ) + + return link + + +class CacheablePageContent(object): + def __init__(self, page): + # type: (HTMLPage) -> None + assert page.cache_link_parsing + self.page = page + + def __eq__(self, other): + # type: (object) -> bool + return (isinstance(other, type(self)) and + self.page.url == other.page.url) + + def __hash__(self): + # type: () -> int + return hash(self.page.url) + + +def with_cached_html_pages( + fn, # type: Callable[[HTMLPage], Iterable[Link]] +): + # type: (...) -> Callable[[HTMLPage], List[Link]] + """ + Given a function that parses an Iterable[Link] from an HTMLPage, cache the + function's result (keyed by CacheablePageContent), unless the HTMLPage + `page` has `page.cache_link_parsing == False`. + """ + + @_lru_cache(maxsize=None) + def wrapper(cacheable_page): + # type: (CacheablePageContent) -> List[Link] + return list(fn(cacheable_page.page)) + + @functools.wraps(fn) + def wrapper_wrapper(page): + # type: (HTMLPage) -> List[Link] + if page.cache_link_parsing: + return wrapper(CacheablePageContent(page)) + return list(fn(page)) + + return wrapper_wrapper + + +@with_cached_html_pages +def parse_links(page): + # type: (HTMLPage) -> Iterable[Link] + """ + Parse an HTML document, and yield its anchor elements as Link objects. + """ + document = html5lib.parse( + page.content, + transport_encoding=page.encoding, + namespaceHTMLElements=False, + ) + + url = page.url + base_url = _determine_base_url(document, url) + for anchor in document.findall(".//a"): + link = _create_link_from_element( + anchor, + page_url=url, + base_url=base_url, + ) + if link is None: + continue + yield link + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__( + self, + content, # type: bytes + encoding, # type: Optional[str] + url, # type: str + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param encoding: the encoding to decode the given content. + :param url: the URL from which the HTML was downloaded. + :param cache_link_parsing: whether links parsed from this page's url + should be cached. PyPI index urls should + have this set to False, for example. + """ + self.content = content + self.encoding = encoding + self.url = url + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + return redact_auth_from_url(self.url) + + +def _handle_get_page_fail( + link, # type: Link + reason, # type: Union[str, Exception] + meth=None # type: Optional[Callable[..., None]] +): + # type: (...) -> None + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _make_html_page(response, cache_link_parsing=True): + # type: (Response, bool) -> HTMLPage + encoding = _get_encoding_from_headers(response.headers) + return HTMLPage( + response.content, + encoding=encoding, + url=response.url, + cache_link_parsing=cache_link_parsing) + + +def _get_html_page(link, session=None): + # type: (Link, Optional[PipSession]) -> Optional[HTMLPage] + if session is None: + raise TypeError( + "_get_html_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + vcs_scheme = _match_vcs_scheme(url) + if vcs_scheme: + logger.warning('Cannot look at %s URL %s because it does not support ' + 'lookup as web pages.', vcs_scheme, link) + return None + + # Tack index.html onto file:// URLs that point to directories + scheme, _, path, _, _, _ = urllib_parse.urlparse(url) + if (scheme == 'file' and os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + try: + resp = _get_html_response(url, session=session) + except _NotHTTP: + logger.warning( + 'Skipping page %s because it looks like an archive, and cannot ' + 'be checked by a HTTP HEAD request.', link, + ) + except _NotHTML as exc: + logger.warning( + 'Skipping page %s because the %s request got Content-Type: %s.' + 'The only supported Content-Type is text/html', + link, exc.request_desc, exc.content_type, + ) + except NetworkConnectionError as exc: + _handle_get_page_fail(link, exc) + except RetryError as exc: + _handle_get_page_fail(link, exc) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_page_fail(link, reason, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_page_fail(link, "connection error: {}".format(exc)) + except requests.Timeout: + _handle_get_page_fail(link, "timed out") + else: + return _make_html_page(resp, + cache_link_parsing=link.cache_link_parsing) + return None + + +def _remove_duplicate_links(links): + # type: (Iterable[Link]) -> List[Link] + """ + Return a list of links, with duplicates removed and ordering preserved. + """ + # We preserve the ordering when removing duplicates because we can. + return list(OrderedDict.fromkeys(links)) + + +def group_locations(locations, expand_dir=False): + # type: (Sequence[str], bool) -> Tuple[List[str], List[str]] + """ + Divide a list of locations into two groups: "files" (archives) and "urls." + + :return: A pair of lists (files, urls). + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + # type: (str) -> None + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + else: + logger.warning( + "Path '%s' is ignored: it is a directory.", path, + ) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url, + ) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url, + ) + + return files, urls + + +class CollectedLinks(object): + + """ + Encapsulates the return value of a call to LinkCollector.collect_links(). + + The return value includes both URLs to project pages containing package + links, as well as individual package Link objects collected from other + sources. + + This info is stored separately as: + + (1) links from the configured file locations, + (2) links from the configured find_links, and + (3) urls to HTML project pages, as described by the PEP 503 simple + repository API. + """ + + def __init__( + self, + files, # type: List[Link] + find_links, # type: List[Link] + project_urls, # type: List[Link] + ): + # type: (...) -> None + """ + :param files: Links from file locations. + :param find_links: Links from find_links. + :param project_urls: URLs to HTML project pages, as described by + the PEP 503 simple repository API. + """ + self.files = files + self.find_links = find_links + self.project_urls = project_urls + + +class LinkCollector(object): + + """ + Responsible for collecting Link objects from all configured locations, + making network requests as needed. + + The class's main method is its collect_links() method. + """ + + def __init__( + self, + session, # type: PipSession + search_scope, # type: SearchScope + ): + # type: (...) -> None + self.search_scope = search_scope + self.session = session + + @classmethod + def create(cls, session, options, suppress_no_index=False): + # type: (PipSession, Values, bool) -> LinkCollector + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + 'Ignoring indexes: %s', + ','.join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, index_urls=index_urls, + ) + link_collector = LinkCollector( + session=session, search_scope=search_scope, + ) + return link_collector + + @property + def find_links(self): + # type: () -> List[str] + return self.search_scope.find_links + + def fetch_page(self, location): + # type: (Link) -> Optional[HTMLPage] + """ + Fetch an HTML page containing package links. + """ + return _get_html_page(location, session=self.session) + + def collect_links(self, project_name): + # type: (str) -> CollectedLinks + """Find all available links for the given project name. + + :return: All the Link objects (unfiltered), as a CollectedLinks object. + """ + search_scope = self.search_scope + index_locations = search_scope.get_index_urls_locations(project_name) + index_file_loc, index_url_loc = group_locations(index_locations) + fl_file_loc, fl_url_loc = group_locations( + self.find_links, expand_dir=True, + ) + + file_links = [ + Link(url) for url in itertools.chain(index_file_loc, fl_file_loc) + ] + + # We trust every directly linked archive in find_links + find_link_links = [Link(url, '-f') for url in self.find_links] + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links. + # We want to filter out anything that does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + # Mark PyPI indices as "cache_link_parsing == False" -- this + # will avoid caching the result of parsing the page for links. + (Link(url, cache_link_parsing=False) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + ) + if self.session.is_secure_origin(link) + ] + + url_locations = _remove_duplicate_links(url_locations) + lines = [ + '{} location(s) to search for versions of {}:'.format( + len(url_locations), project_name, + ), + ] + for link in url_locations: + lines.append('* {}'.format(link)) + logger.debug('\n'.join(lines)) + + return CollectedLinks( + files=file_links, + find_links=find_link_links, + project_urls=url_locations, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/package_finder.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/package_finder.py new file mode 100644 index 0000000..8411578 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/index/package_finder.py @@ -0,0 +1,1014 @@ +"""Routines related to PyPI, indexes""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import re + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.index.collector import parse_links +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import build_netloc +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import ( + FrozenSet, Iterable, List, Optional, Set, Text, Tuple, Union, + ) + + from pip._vendor.packaging.tags import Tag + from pip._vendor.packaging.version import _BaseVersion + + from pip._internal.index.collector import LinkCollector + from pip._internal.models.search_scope import SearchScope + from pip._internal.req import InstallRequirement + from pip._internal.utils.hashes import Hashes + + BuildTag = Union[Tuple[()], Tuple[int, str]] + CandidateSortingKey = ( + Tuple[int, int, int, _BaseVersion, BuildTag, Optional[int]] + ) + + +__all__ = ['FormatControl', 'BestCandidateResult', 'PackageFinder'] + + +logger = logging.getLogger(__name__) + + +def _check_link_requires_python( + link, # type: Link + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> bool + """ + Return whether the given Python version is compatible with a link's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + """ + try: + is_compatible = check_requires_python( + link.requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier: + logger.debug( + "Ignoring invalid Requires-Python (%r) for link: %s", + link.requires_python, link, + ) + else: + if not is_compatible: + version = '.'.join(map(str, version_info)) + if not ignore_requires_python: + logger.debug( + 'Link requires a different Python (%s not in: %r): %s', + version, link.requires_python, link, + ) + return False + + logger.debug( + 'Ignoring failed Requires-Python check (%s not in: %r) ' + 'for link: %s', + version, link.requires_python, link, + ) + + return True + + +class LinkEvaluator(object): + + """ + Responsible for evaluating links for a particular project. + """ + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + project_name, # type: str + canonical_name, # type: str + formats, # type: FrozenSet[str] + target_python, # type: TargetPython + allow_yanked, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + :param project_name: The user supplied package name. + :param canonical_name: The canonical package name. + :param formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. + :param target_python: The target Python interpreter to use when + evaluating link compatibility. This is used, for example, to + check wheel compatibility, as well as when checking the Python + version, e.g. the Python version embedded in a link filename + (or egg fragment) and against an HTML link's optional PEP 503 + "data-requires-python" attribute. + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param ignore_requires_python: Whether to ignore incompatible + PEP 503 "data-requires-python" values in HTML links. Defaults + to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self._allow_yanked = allow_yanked + self._canonical_name = canonical_name + self._ignore_requires_python = ignore_requires_python + self._formats = formats + self._target_python = target_python + + self.project_name = project_name + + def evaluate_link(self, link): + # type: (Link) -> Tuple[bool, Optional[Text]] + """ + Determine whether a link is a candidate for installation. + + :return: A tuple (is_candidate, result), where `result` is (1) a + version string if `is_candidate` is True, and (2) if + `is_candidate` is False, an optional string to log the reason + the link fails to qualify. + """ + version = None + if link.is_yanked and not self._allow_yanked: + reason = link.yanked_reason or '' + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + return (False, u'yanked for reason: {}'.format(reason)) + + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + return (False, 'not a file') + if ext not in SUPPORTED_EXTENSIONS: + return (False, 'unsupported archive format: {}'.format(ext)) + if "binary" not in self._formats and ext == WHEEL_EXTENSION: + reason = 'No binaries permitted for {}'.format( + self.project_name) + return (False, reason) + if "macosx10" in link.path and ext == '.zip': + return (False, 'macosx10 one') + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + return (False, 'invalid wheel filename') + if canonicalize_name(wheel.name) != self._canonical_name: + reason = 'wrong project name (not {})'.format( + self.project_name) + return (False, reason) + + supported_tags = self._target_python.get_tags() + if not wheel.supported(supported_tags): + # Include the wheel's tags in the reason string to + # simplify troubleshooting compatibility issues. + file_tags = wheel.get_formatted_file_tags() + reason = ( + "none of the wheel's tags match: {}".format( + ', '.join(file_tags) + ) + ) + return (False, reason) + + version = wheel.version + + # This should be up by the self.ok_binary check, but see issue 2700. + if "source" not in self._formats and ext != WHEEL_EXTENSION: + reason = 'No sources permitted for {}'.format(self.project_name) + return (False, reason) + + if not version: + version = _extract_version_from_fragment( + egg_info, self._canonical_name, + ) + if not version: + reason = 'Missing project version for {}'.format(self.project_name) + return (False, reason) + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != self._target_python.py_version: + return (False, 'Python version is incorrect') + + supports_python = _check_link_requires_python( + link, version_info=self._target_python.py_version_info, + ignore_requires_python=self._ignore_requires_python, + ) + if not supports_python: + # Return None for the reason text to suppress calling + # _log_skipped_link(). + return (False, None) + + logger.debug('Found link %s, version: %s', link, version) + + return (True, version) + + +def filter_unallowed_hashes( + candidates, # type: List[InstallationCandidate] + hashes, # type: Hashes + project_name, # type: str +): + # type: (...) -> List[InstallationCandidate] + """ + Filter out candidates whose hashes aren't allowed, and return a new + list of candidates. + + If at least one candidate has an allowed hash, then all candidates with + either an allowed hash or no hash specified are returned. Otherwise, + the given candidates are returned. + + Including the candidates with no hash specified when there is a match + allows a warning to be logged if there is a more preferred candidate + with no hash specified. Returning all candidates in the case of no + matches lets pip report the hash of the candidate that would otherwise + have been installed (e.g. permitting the user to more easily update + their requirements file with the desired hash). + """ + if not hashes: + logger.debug( + 'Given no hashes to check %s links for project %r: ' + 'discarding no candidates', + len(candidates), + project_name, + ) + # Make sure we're not returning back the given value. + return list(candidates) + + matches_or_no_digest = [] + # Collect the non-matches for logging purposes. + non_matches = [] + match_count = 0 + for candidate in candidates: + link = candidate.link + if not link.has_hash: + pass + elif link.is_hash_allowed(hashes=hashes): + match_count += 1 + else: + non_matches.append(candidate) + continue + + matches_or_no_digest.append(candidate) + + if match_count: + filtered = matches_or_no_digest + else: + # Make sure we're not returning back the given value. + filtered = list(candidates) + + if len(filtered) == len(candidates): + discard_message = 'discarding no candidates' + else: + discard_message = 'discarding {} non-matches:\n {}'.format( + len(non_matches), + '\n '.join(str(candidate.link) for candidate in non_matches) + ) + + logger.debug( + 'Checked %s links for project %r against %s hashes ' + '(%s matches, %s no digest): %s', + len(candidates), + project_name, + hashes.digest_count, + match_count, + len(matches_or_no_digest) - match_count, + discard_message + ) + + return filtered + + +class CandidatePreferences(object): + + """ + Encapsulates some of the preferences for filtering and sorting + InstallationCandidate objects. + """ + + def __init__( + self, + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + ): + # type: (...) -> None + """ + :param allow_all_prereleases: Whether to allow all pre-releases. + """ + self.allow_all_prereleases = allow_all_prereleases + self.prefer_binary = prefer_binary + + +class BestCandidateResult(object): + """A collection of candidates, returned by `PackageFinder.find_best_candidate`. + + This class is only intended to be instantiated by CandidateEvaluator's + `compute_best_candidate()` method. + """ + + def __init__( + self, + candidates, # type: List[InstallationCandidate] + applicable_candidates, # type: List[InstallationCandidate] + best_candidate, # type: Optional[InstallationCandidate] + ): + # type: (...) -> None + """ + :param candidates: A sequence of all available candidates found. + :param applicable_candidates: The applicable candidates. + :param best_candidate: The most preferred candidate found, or None + if no applicable candidates were found. + """ + assert set(applicable_candidates) <= set(candidates) + + if best_candidate is None: + assert not applicable_candidates + else: + assert best_candidate in applicable_candidates + + self._applicable_candidates = applicable_candidates + self._candidates = candidates + + self.best_candidate = best_candidate + + def iter_all(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through all candidates. + """ + return iter(self._candidates) + + def iter_applicable(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through the applicable candidates. + """ + return iter(self._applicable_candidates) + + +class CandidateEvaluator(object): + + """ + Responsible for filtering and sorting candidates for installation based + on what tags are valid. + """ + + @classmethod + def create( + cls, + project_name, # type: str + target_python=None, # type: Optional[TargetPython] + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object. + + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + :param hashes: An optional collection of allowed hashes. + """ + if target_python is None: + target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() + + supported_tags = target_python.get_tags() + + return cls( + project_name=project_name, + supported_tags=supported_tags, + specifier=specifier, + prefer_binary=prefer_binary, + allow_all_prereleases=allow_all_prereleases, + hashes=hashes, + ) + + def __init__( + self, + project_name, # type: str + supported_tags, # type: List[Tag] + specifier, # type: specifiers.BaseSpecifier + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> None + """ + :param supported_tags: The PEP 425 tags supported by the target + Python in order of preference (most preferred first). + """ + self._allow_all_prereleases = allow_all_prereleases + self._hashes = hashes + self._prefer_binary = prefer_binary + self._project_name = project_name + self._specifier = specifier + self._supported_tags = supported_tags + + def get_applicable_candidates( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> List[InstallationCandidate] + """ + Return the applicable candidates from a list of candidates. + """ + # Using None infers from the specifier instead. + allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier + versions = { + str(v) for v in specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + (str(c.version) for c in candidates), + prereleases=allow_prereleases, + ) + } + + # Again, converting version to str to deal with debundling. + applicable_candidates = [ + c for c in candidates if str(c.version) in versions + ] + + filtered_applicable_candidates = filter_unallowed_hashes( + candidates=applicable_candidates, + hashes=self._hashes, + project_name=self._project_name, + ) + + return sorted(filtered_applicable_candidates, key=self._sort_key) + + def _sort_key(self, candidate): + # type: (InstallationCandidate) -> CandidateSortingKey + """ + Function to pass as the `key` argument to a call to sorted() to sort + InstallationCandidates by preference. + + Returns a tuple such that tuples sorting as greater using Python's + default comparison operator are more preferred. + + The preference is as follows: + + First and foremost, candidates with allowed (matching) hashes are + always preferred over candidates without matching hashes. This is + because e.g. if the only candidate with an allowed hash is yanked, + we still want to use that candidate. + + Second, excepting hash considerations, candidates that have been + yanked (in the sense of PEP 592) are always less preferred than + candidates that haven't been yanked. Then: + + If not finding wheels, they are sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self._supported_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + valid_tags = self._supported_tags + support_num = len(valid_tags) + build_tag = () # type: BuildTag + binary_preference = 0 + link = candidate.link + if link.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(link.filename) + if not wheel.supported(valid_tags): + raise UnsupportedWheel( + "{} is not a supported wheel for this platform. It " + "can't be sorted.".format(wheel.filename) + ) + if self._prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(valid_tags)) + if wheel.build_tag is not None: + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + has_allowed_hash = int(link.is_hash_allowed(self._hashes)) + yank_value = -1 * int(link.is_yanked) # -1 for yanked. + return ( + has_allowed_hash, yank_value, binary_preference, candidate.version, + build_tag, pri, + ) + + def sort_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> Optional[InstallationCandidate] + """ + Return the best candidate per the instance's sort order, or None if + no candidate is acceptable. + """ + if not candidates: + return None + best_candidate = max(candidates, key=self._sort_key) + return best_candidate + + def compute_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> BestCandidateResult + """ + Compute and return a `BestCandidateResult` instance. + """ + applicable_candidates = self.get_applicable_candidates(candidates) + + best_candidate = self.sort_best_candidate(applicable_candidates) + + return BestCandidateResult( + candidates, + applicable_candidates=applicable_candidates, + best_candidate=best_candidate, + ) + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__( + self, + link_collector, # type: LinkCollector + target_python, # type: TargetPython + allow_yanked, # type: bool + format_control=None, # type: Optional[FormatControl] + candidate_prefs=None, # type: CandidatePreferences + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + This constructor is primarily meant to be used by the create() class + method and from tests. + + :param format_control: A FormatControl object, used to control + the selection of source packages / binary packages when consulting + the index and links. + :param candidate_prefs: Options to use when creating a + CandidateEvaluator object. + """ + if candidate_prefs is None: + candidate_prefs = CandidatePreferences() + + format_control = format_control or FormatControl(set(), set()) + + self._allow_yanked = allow_yanked + self._candidate_prefs = candidate_prefs + self._ignore_requires_python = ignore_requires_python + self._link_collector = link_collector + self._target_python = target_python + + self.format_control = format_control + + # These are boring links that have already been logged somehow. + self._logged_links = set() # type: Set[Link] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + @classmethod + def create( + cls, + link_collector, # type: LinkCollector + selection_prefs, # type: SelectionPreferences + target_python=None, # type: Optional[TargetPython] + ): + # type: (...) -> PackageFinder + """Create a PackageFinder. + + :param selection_prefs: The candidate selection preferences, as a + SelectionPreferences object. + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + """ + if target_python is None: + target_python = TargetPython() + + candidate_prefs = CandidatePreferences( + prefer_binary=selection_prefs.prefer_binary, + allow_all_prereleases=selection_prefs.allow_all_prereleases, + ) + + return cls( + candidate_prefs=candidate_prefs, + link_collector=link_collector, + target_python=target_python, + allow_yanked=selection_prefs.allow_yanked, + format_control=selection_prefs.format_control, + ignore_requires_python=selection_prefs.ignore_requires_python, + ) + + @property + def target_python(self): + # type: () -> TargetPython + return self._target_python + + @property + def search_scope(self): + # type: () -> SearchScope + return self._link_collector.search_scope + + @search_scope.setter + def search_scope(self, search_scope): + # type: (SearchScope) -> None + self._link_collector.search_scope = search_scope + + @property + def find_links(self): + # type: () -> List[str] + return self._link_collector.find_links + + @property + def index_urls(self): + # type: () -> List[str] + return self.search_scope.index_urls + + @property + def trusted_hosts(self): + # type: () -> Iterable[str] + for host_port in self._link_collector.session.pip_trusted_origins: + yield build_netloc(*host_port) + + @property + def allow_all_prereleases(self): + # type: () -> bool + return self._candidate_prefs.allow_all_prereleases + + def set_allow_all_prereleases(self): + # type: () -> None + self._candidate_prefs.allow_all_prereleases = True + + @property + def prefer_binary(self): + # type: () -> bool + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self): + # type: () -> None + self._candidate_prefs.prefer_binary = True + + def make_link_evaluator(self, project_name): + # type: (str) -> LinkEvaluator + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + + return LinkEvaluator( + project_name=project_name, + canonical_name=canonical_name, + formats=formats, + target_python=self._target_python, + allow_yanked=self._allow_yanked, + ignore_requires_python=self._ignore_requires_python, + ) + + def _sort_links(self, links): + # type: (Iterable[Link]) -> List[Link] + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() # type: Set[Link] + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _log_skipped_link(self, link, reason): + # type: (Link, Text) -> None + if link not in self._logged_links: + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + # Also, put the link at the end so the reason is more visible + # and because the link string is usually very long. + logger.debug(u'Skipping link: %s: %s', reason, link) + self._logged_links.add(link) + + def get_install_candidate(self, link_evaluator, link): + # type: (LinkEvaluator, Link) -> Optional[InstallationCandidate] + """ + If the link is a candidate for install, convert it to an + InstallationCandidate and return it. Otherwise, return None. + """ + is_candidate, result = link_evaluator.evaluate_link(link) + if not is_candidate: + if result: + self._log_skipped_link(link, reason=result) + return None + + return InstallationCandidate( + name=link_evaluator.project_name, + link=link, + # Convert the Text result to str since InstallationCandidate + # accepts str. + version=str(result), + ) + + def evaluate_links(self, link_evaluator, links): + # type: (LinkEvaluator, Iterable[Link]) -> List[InstallationCandidate] + """ + Convert links that are candidates to InstallationCandidate objects. + """ + candidates = [] + for link in self._sort_links(links): + candidate = self.get_install_candidate(link_evaluator, link) + if candidate is not None: + candidates.append(candidate) + + return candidates + + def process_project_url(self, project_url, link_evaluator): + # type: (Link, LinkEvaluator) -> List[InstallationCandidate] + logger.debug( + 'Fetching project page and analyzing links: %s', project_url, + ) + html_page = self._link_collector.fetch_page(project_url) + if html_page is None: + return [] + + page_links = list(parse_links(html_page)) + + with indent_log(): + package_links = self.evaluate_links( + link_evaluator, + links=page_links, + ) + + return package_links + + def find_all_candidates(self, project_name): + # type: (str) -> List[InstallationCandidate] + """Find all available InstallationCandidate for project_name + + This checks index_urls and find_links. + All versions found are returned as an InstallationCandidate list. + + See LinkEvaluator.evaluate_link() for details on which files + are accepted. + """ + collected_links = self._link_collector.collect_links(project_name) + + link_evaluator = self.make_link_evaluator(project_name) + + find_links_versions = self.evaluate_links( + link_evaluator, + links=collected_links.find_links, + ) + + page_versions = [] + for project_url in collected_links.project_urls: + package_links = self.process_project_url( + project_url, link_evaluator=link_evaluator, + ) + page_versions.extend(package_links) + + file_versions = self.evaluate_links( + link_evaluator, + links=collected_links.files, + ) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.link.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return file_versions + find_links_versions + page_versions + + def make_candidate_evaluator( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object to use. + """ + candidate_prefs = self._candidate_prefs + return CandidateEvaluator.create( + project_name=project_name, + target_python=self._target_python, + prefer_binary=candidate_prefs.prefer_binary, + allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, + hashes=hashes, + ) + + def find_best_candidate( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> BestCandidateResult + """Find matches for the given project and specifier. + + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + + :return: A `BestCandidateResult` instance. + """ + candidates = self.find_all_candidates(project_name) + candidate_evaluator = self.make_candidate_evaluator( + project_name=project_name, + specifier=specifier, + hashes=hashes, + ) + return candidate_evaluator.compute_best_candidate(candidates) + + def find_requirement(self, req, upgrade): + # type: (InstallRequirement, bool) -> Optional[InstallationCandidate] + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a InstallationCandidate if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + hashes = req.hashes(trust_internet=False) + best_candidate_result = self.find_best_candidate( + req.name, specifier=req.specifier, hashes=hashes, + ) + best_candidate = best_candidate_result.best_candidate + + installed_version = None # type: Optional[_BaseVersion] + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + + def _format_versions(cand_iter): + # type: (Iterable[InstallationCandidate]) -> str + # This repeated parse_version and str() conversion is needed to + # handle different vendoring sources from pip and pkg_resources. + # If we stop using the pkg_resources provided specifier and start + # using our own, we can drop the cast to str(). + return ", ".join(sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + )) or "none" + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + _format_versions(best_candidate_result.iter_all()), + ) + + raise DistributionNotFound( + 'No matching distribution found for {}'.format( + req) + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + _format_versions(best_candidate_result.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + _format_versions(best_candidate_result.iter_applicable()), + ) + return best_candidate + + +def _find_name_version_sep(fragment, canonical_name): + # type: (str, str) -> int + """Find the separator's index based on the package's canonical name. + + :param fragment: A + filename "fragment" (stem) or + egg fragment. + :param canonical_name: The package's canonical name. + + This function is needed since the canonicalized name does not necessarily + have the same length as the egg info's name part. An example:: + + >>> fragment = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(fragment, canonical_name) + 8 + """ + # Project name and version must be separated by one single dash. Find all + # occurrences of dashes; if the string in front of it matches the canonical + # name, this is the one separating the name and version parts. + for i, c in enumerate(fragment): + if c != "-": + continue + if canonicalize_name(fragment[:i]) == canonical_name: + return i + raise ValueError("{} does not match {}".format(fragment, canonical_name)) + + +def _extract_version_from_fragment(fragment, canonical_name): + # type: (str, str) -> Optional[str] + """Parse the version string from a + filename + "fragment" (stem) or egg fragment. + + :param fragment: The string to parse. E.g. foo-2.1 + :param canonical_name: The canonicalized name of the package this + belongs to. + """ + try: + version_start = _find_name_version_sep(fragment, canonical_name) + 1 + except ValueError: + return None + version = fragment[version_start:] + if not version: + return None + return version diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/locations.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000..0c12354 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/locations.py @@ -0,0 +1,194 @@ +"""Locations where we look for configs, install stuff, etc""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import os.path +import platform +import site +import sys +import sysconfig +from distutils import sysconfig as distutils_sysconfig +from distutils.command.install import SCHEME_KEYS # type: ignore +from distutils.command.install import install as distutils_install_command + +from pip._internal.models.scheme import Scheme +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional, Union + + from distutils.cmd import Command as DistutilsCommand + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +def get_major_minor_version(): + # type: () -> str + """ + Return the major-minor version of the current Python as a string, e.g. + "3.7" or "3.10". + """ + return '{}.{}'.format(*sys.version_info) + + +def get_src_prefix(): + # type: () -> str + if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') + else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + + # under macOS + virtualenv sys.prefix is not properly resolved + # it is something like /path/to/python/bin/.. + return os.path.abspath(src_prefix) + + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_path("purelib") # type: Optional[str] + +# This is because of a bug in PyPy's sysconfig module, see +# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths +# for more information. +if platform.python_implementation().lower() == "pypy": + site_packages = distutils_sysconfig.get_python_lib() +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site = site.getusersitepackages() +except AttributeError: + user_site = site.USER_SITE + +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + + +def distutils_scheme( + dist_name, user=False, home=None, root=None, isolated=False, prefix=None +): + # type:(str, bool, str, str, bool, str) -> Dict[str, str] + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + dist_args = {'name': dist_name} # type: Dict[str, Union[str, List[str]]] + if isolated: + dist_args["script_args"] = ["--no-user-cfg"] + + d = Distribution(dist_args) + d.parse_config_files() + obj = None # type: Optional[DistutilsCommand] + obj = d.get_command_obj('install', create=True) + assert obj is not None + i = cast(distutils_install_command, obj) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={} prefix={}".format(user, prefix) + assert not (home and prefix), "home={} prefix={}".format(home, prefix) + i.user = user or i.user + if user or home: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + + scheme = {} + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + i.prefix, + 'include', + 'site', + 'python{}'.format(get_major_minor_version()), + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme + + +def get_scheme( + dist_name, # type: str + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None, # type: Optional[str] +): + # type: (...) -> Scheme + """ + Get the "scheme" corresponding to the input parameters. The distutils + documentation provides the context for the available schemes: + https://docs.python.org/3/install/index.html#alternate-installation + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme and provides the base + directory for the same + :param root: root under which other directories are re-based + :param isolated: equivalent to --no-user-cfg, i.e. do not consider + ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for + scheme paths + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + scheme = distutils_scheme( + dist_name, user, home, root, isolated, prefix + ) + return Scheme( + platlib=scheme["platlib"], + purelib=scheme["purelib"], + headers=scheme["headers"], + scripts=scheme["scripts"], + data=scheme["data"], + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/main.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/main.py new file mode 100644 index 0000000..3208d5b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/main.py @@ -0,0 +1,16 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..661027cc5338ab927b9b269c54b6064d37ffe94f GIT binary patch literal 296 zcmYk1F-`+P3`J+tB#M+fY@1@&1r10Df<#S&XlO=e#>x)a%nV}(5pKW{I09En%Ms|H z!OK#_()ZVYk}Vq-i@DKy-~8aE`sWaitJ1Ji@l2aH_r_PZW;uC1y{a?14X&w{4yAS} z3Wmt_HhJoKgoSb;rYN;4NFZ+KH_PfUwbmzR?T0B7UHjL*-{6j^Ft`pQu2b{~*U9a+ zI;t=cJYl2O&`YKgMx6ITEH;<)NKRmk4aH~Z!^04}6i?bS;2_le-^-9g1{*-?n{!3+k0a!;{-2eap literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b937616e0dc23877292259e7640673496aa9487 GIT binary patch literal 1523 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 RXVS@6s+B{Pmuxm+{TKAPmaPB) literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0fcca30e80430b0f829a80c686b8eb941567d755 GIT binary patch literal 2781 zcmaJ@-)|eo5#HT9l1GYCY)5qh*G(@)+mx+IHcf%Ff@>I1VkZsC1{D%C1k&yKb|sy3 zyrXuPwnbAfm5cbT&jtD*J^Ej|*FNQ6aL~Hn>`Ag@8$Duw%Xvccy&2Vin7SkB+uqr7Rf||*V7Pd;A%O#bPM(dg6B-Cj^df1J9kERwtmHngkScT73g1*R#Tef|qT z+(xrsqSI{56&nl1$AJJ4=Xyd`y6UBTE@AzrjvE#E24BysCy;Y^v&?|!25sW$OuX<|Gk*~*G7A|PVpO;b+d z2isY?9VhWLNq3Vi9m5pyJsoF-bxEFUHR#0ns&-Mzds&f~{c5Q!^8FKHl+v?KKbi}) zbu@be-2#uCF|*>>Av@p;;i>DM5(~ay5!Fh_) zu<-03!!N8hc9?F{tI9$p!;i{zHqnIxv(&@Aq>rJmAL990&7ST%ZJnH&a!03bSd)ES z+#lxIJtDWu;)B6&hc-3{JA)d@=*U&lkggxOq{7~3SPHfOTc*1sq-N3|( zZ!i~r!@l90f)MmLqScRpjYF*T!qJ_SH(UQ$e zM@s=<`c%}aeD6%3RhoIFno`nr*GMCR4ijA-`nDp%|ISxmr`s!TA(wbmlVca`2*x_* zU7+^FNahFNo9ook9p`L<>kBRAd*E7f0u!%ZNdgzVMV=#X`75ttqByQ(JA4P>vJ;?% z#{s-7M)S_2gP%P*xDh`(=)ru=8kpv7>>)@}E|c&cxTm<)-`RhU)1;>Ef zf}f3{3XhTA7lA@MY7nIwZ?V6N-ywBl7dO`_SL|J10P0`=>L6T%s<|u7{Pc`H<(iA| zkYhz0wHEbh{0MS%?w+FEz1HX%@6SK%_lvS$C$)GO_xqIBPU+I8_1NgWS!T42-;Wcs z<?#E*$0{1HiVd}VW)ZKsz#dY!~m&ERgJ7r z`k^5xR4tX+G%*T}Yur!rnXZ~R0O4i`$W>722USQed>K?%k@ZxroFVeDx$95iQU_xd!XKd(nEY+5i7zx(*ST!W-$>|C8}CT|X@_@1MS^e&oAEzLZ@q13_Nt zx*liR{uxrXiH;$HJJ07oyr|_X^2i~h-m;0Yq3TG9`ZfcwWYv{&57?>`NY#>3l_|0W zrBOpAuFiPx(2SCO)dlulmQFOPsH#=}kQRSK-3zVHa*m0xK$*rHIO1UtiB2b4j>0Gs zyz^GH>?KCM?)dvC!2@|;7pgRax-R2y%wXoS+}45gwwE|9(gVbU_VH`$FuZng*>4=U x{V7f{RHP7gH9;xNknv2*v&(z*Pr)nlx;IqO&DvhKC=vL)*+pgP!t$%Z{{U!=r?>zB literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e16d50b17d22f0c3fcdbc2e1076a890c41b3944 GIT binary patch literal 1262 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d442344be5fbd4dda42e32c083875037cbb2a8ed GIT binary patch literal 984 zcmZWn&2H2%5KcDPpQf!q;>y8-(@Jc7=m~^SQ7IrHDx?($_F{SC>BiI>rylS2M|%T_ zXW+`MufU_^%3HvNaneeQn8=@J#^cHOo00qbSw!;v?2pWnDEbwQZAr;Eq=k$?MYCuT zsrW}ci{C`5r;@LcN}%^8nI$TnM&thV&+N5z%wN@jOd{GsOAm@&XkRj2p_j%;uWiM! zC}9bhXYB>a7EpnchS@c^wT263PnldxZRFg*T{M%GV!j0JSYay zTVP&a`O;RobqjvLZ~sZVg3G-0OY>M753ZuJCffz&*-gJ0)>NI*b5d3gTBQW3z+p4Q z!s*&$lPc+DL2UdZJ+cmI;X?w5W-)QqQ}HZ;bk^^fnhiRRn!&Vv`?Y_`I*paqC}g$t zC9;cB7bTMp*b0d`iQ&O)E)kg04hnCbCUQ@$%NU7tUZ2Xs|4+AMA6h0TZ*e9Ukhk&0 zfC{{mpohwjXyGG*O}vRjwCNGXn}o2pNePoppDRBKK>2_`l8Ab-kHQx|WahrHG`7n;U`x@-8-Dlo{qIisOyMuHBt7vcexh3(jn z266XY-|IS&-^HAahv7mcmmoxw2|?*rCg^)YoUf$0JsAo?Lr3o2>CxNc_s8Ph@uz9C zBLo`jQ3$si_y|O7LHM^D6712!K-MTu)3n$6PR6Ob7Xt1RGy^*i6tpK>8QO#)O*4Al PRrVq@1w#wc(J1)~b?^}l literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c248a07186dfe391effc1684de1e43e6bcc60e1 GIT binary patch literal 3477 zcmb7H&2QYs73Yv#E|*%#wyfAr(gLjVQ7>Tc8YtSPso)y0lQgKRs!>uP3j_>Jj&>>W zV>L4?TU%~XKmwc|f}+Rfv;unZwSNw;IrZF|3&j1sq2*oMDv)w9!};QyH}Cy^@6CqG z%btPf@B9A=|GH`z-%@AsvC+AMBELZu8!=0be*I>Bru$~!)NiYA;ccdN*624Ft+7%k zYxZ5;w$oPT^*!Bgq)S=5-_~s>UCvheE7^tq1>J9^7qd(KOU&SxLao?o7PB3rv-B_MXKZy?btww-B2S_)O(s0Z!;E9FF$#sG zhP{2X{W0FnVJHKY%8psBNRd6j?A9DLz^K?dRK{2X}w{ z!QI~kpM18p_3_q6Bycm4Dx%F79}As3C~^Z8H^@{l6N8D&*czC9n>S)RZk!m$tnb85 z+&nS*&Df1wc)PI|FX7#a+c?;AwY0-S5e;{uV#FsOdOn^H@+ch1aT+QveKq8MT125r ziky0(@<++YPoy6e`ED{8<2=49e5u8BwSdaoP4YNMlYC!R9-90iol-`m_}SA$gZzIN zMgAGpz?fPkyJviHYigJF3}`rIqN`YGAG2vgnS0jEE*msfHn75)HA?%GG2_5h&aokt zYR=r!EUi-$GTxoqsx|Xy#ndUCy`|XPH^ruE&z570zQT9vV6!wzt2AT#YZJz3On$G` zCmY-q9LM)Vf5AL{aet3T$`5xH7yg-FlYF3!^yU1_U)E+IIZxQ1s>V>Mk(?y2+U-H2 zhA&%J@Plz8xc}+qPH$&t^M*ex9`HkKNS=@~^|K-#r`%6+fBQ%ci=2Fs7f|akOw#av z%DazEu>^yPHmc4_Zmz7rnaW$Y{tjUO8zo7;sTFAA}-@K|0OKic&e(qPlp0oTMt4+o-Y$ z9+jK(2O21qqeaG#4e=u=@#O2?XOauqg93RtDDpV$-7BJT#&dn|R&i4@X=i z_3-Ejh*iNz6y&E~-Mz{4!yc5UF0A#F?k{>02fR57qy2EeWe;xY1xb!G=V97|b$Kd# zHJk%UwCIkGDksu__#sr2@1im;*vw@fN}JiNZ9D8DdLG*5Q@i1qCd2Q$$?5`py0wTG zNI--zQ8WoIqEWKa2n@j32*Bzc<1yP37ckzjDqHe&cWzQ`rn*|kg31kWh(ra!hv-RS zmf<*TvbLzf*^-FS^s|Q`;i70A{(%OF$WWleo*@L)itU*Lwr|xv(CfXarL58dF>Phn zM4UFt#-1a$Q8(qba%Zg=)IBvBPE!*Rlv^*^%!|$0Qf$rIrFja96B)6`viL^N+4Wr9 zh@BHXeh*RXY+c;MSLlJP?$+e}Pr(QsU3?(YUw=ogBMk9OV(<_lH@@MIQXWd~0}+Qw zOmwaR+nrq5EQ)=iE@-4haQVLf$LYkp;ZN2U=5!3BnHNk(E+f}TDpydI#uqD7rr^F~ z_6LeB$XYvwcpFn4CMeWBHY>AxtFoZ!%bsg(40sM4i`yy5BjGli{A|&20}k#`B2kJX zu%5-^?glkedAs{uM8&+$ie|nb41oMe1}jkf)q04lG)fefqclym(}GunL(v_kQW_Vu=?r@Le4pdzxqFp_B%+CH&7X- z4OXo&&vY%XNo*r#*>>A>nfuhS9E_|m`>FfXwkL0W52Hn7;IrqU!oC;Iu z>w}K~PLVxBob|8oCPFI0q)xjC&7B&ZewfG4rHp}SHAK{ZE(7@&5RbAdr$(D;EhxC!LO2W5NQ&aHnZMi_iM_3ju5;+ouDKjz$Tg^@y}vK}7&C zS^GQNrEojhc-h5sL!U>C7lpoJ2jYmf0|99d(;5X41<;Z_jHv``Ywr6>)h%s7&3 zEZ!o0DL;yzQuQmU<_6R)QeM#Cg4a;AgI&|MU1Ut}y4!Ns^<3w5LDN-}E*1#W;u3xJ zb5#8|k*~YKA;49LZk@!jpgUFxE*u2B{tp&c0HfWkIOMVmOyL?Ji0L(0-_YyMGCx*P z=C65M$Ih1)=q$AqpVJsVMDM&a+O7IxTp0Q(vaU s9a5Aca^=()Pi>j=%vre`wZm>xUYGO@Vk7;`HfvMC?W7GFwJp#3FXcG1kpKVy literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..865de6c25c0afc28ce10d6759d8a7071da01b4ac GIT binary patch literal 1694 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/candidate.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/candidate.py new file mode 100644 index 0000000..9149e0f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/candidate.py @@ -0,0 +1,38 @@ +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._vendor.packaging.version import _BaseVersion + from pip._internal.models.link import Link + + +class InstallationCandidate(KeyBasedCompareMixin): + """Represents a potential "candidate" for installation. + """ + + __slots__ = ["name", "version", "link"] + + def __init__(self, name, version, link): + # type: (str, str, Link) -> None + self.name = name + self.version = parse_version(version) # type: _BaseVersion + self.link = link + + super(InstallationCandidate, self).__init__( + key=(self.name, self.version, self.link), + defining_class=InstallationCandidate + ) + + def __repr__(self): + # type: () -> str + return "".format( + self.name, self.version, self.link, + ) + + def __str__(self): + # type: () -> str + return '{!r} candidate (version {} at {})'.format( + self.name, self.version, self.link, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/direct_url.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/direct_url.py new file mode 100644 index 0000000..87bd9fe --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/direct_url.py @@ -0,0 +1,245 @@ +""" PEP 610 """ +import json +import re + +from pip._vendor import six +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, Optional, Type, TypeVar, Union + ) + + T = TypeVar("T") + + +DIRECT_URL_METADATA_NAME = "direct_url.json" +ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") + +__all__ = [ + "DirectUrl", + "DirectUrlValidationError", + "DirInfo", + "ArchiveInfo", + "VcsInfo", +] + + +class DirectUrlValidationError(Exception): + pass + + +def _get(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> Optional[T] + """Get value from dictionary and verify expected type.""" + if key not in d: + return default + value = d[key] + if six.PY2 and expected_type is str: + expected_type = six.string_types # type: ignore + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + "{!r} has unexpected type for {} (expected {})".format( + value, key, expected_type + ) + ) + return value + + +def _get_required(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> T + value = _get(d, expected_type, key, default) + if value is None: + raise DirectUrlValidationError("{} must have a value".format(key)) + return value + + +def _exactly_one_of(infos): + # type: (Iterable[Optional[InfoType]]) -> InfoType + infos = [info for info in infos if info is not None] + if not infos: + raise DirectUrlValidationError( + "missing one of archive_info, dir_info, vcs_info" + ) + if len(infos) > 1: + raise DirectUrlValidationError( + "more than one of archive_info, dir_info, vcs_info" + ) + assert infos[0] is not None + return infos[0] + + +def _filter_none(**kwargs): + # type: (Any) -> Dict[str, Any] + """Make dict excluding None values.""" + return {k: v for k, v in kwargs.items() if v is not None} + + +class VcsInfo(object): + name = "vcs_info" + + def __init__( + self, + vcs, # type: str + commit_id, # type: str + requested_revision=None, # type: Optional[str] + resolved_revision=None, # type: Optional[str] + resolved_revision_type=None, # type: Optional[str] + ): + self.vcs = vcs + self.requested_revision = requested_revision + self.commit_id = commit_id + self.resolved_revision = resolved_revision + self.resolved_revision_type = resolved_revision_type + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[VcsInfo] + if d is None: + return None + return cls( + vcs=_get_required(d, str, "vcs"), + commit_id=_get_required(d, str, "commit_id"), + requested_revision=_get(d, str, "requested_revision"), + resolved_revision=_get(d, str, "resolved_revision"), + resolved_revision_type=_get(d, str, "resolved_revision_type"), + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none( + vcs=self.vcs, + requested_revision=self.requested_revision, + commit_id=self.commit_id, + resolved_revision=self.resolved_revision, + resolved_revision_type=self.resolved_revision_type, + ) + + +class ArchiveInfo(object): + name = "archive_info" + + def __init__( + self, + hash=None, # type: Optional[str] + ): + self.hash = hash + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[ArchiveInfo] + if d is None: + return None + return cls(hash=_get(d, str, "hash")) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(hash=self.hash) + + +class DirInfo(object): + name = "dir_info" + + def __init__( + self, + editable=False, # type: bool + ): + self.editable = editable + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[DirInfo] + if d is None: + return None + return cls( + editable=_get_required(d, bool, "editable", default=False) + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(editable=self.editable or None) + + +if MYPY_CHECK_RUNNING: + InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] + + +class DirectUrl(object): + + def __init__( + self, + url, # type: str + info, # type: InfoType + subdirectory=None, # type: Optional[str] + ): + self.url = url + self.info = info + self.subdirectory = subdirectory + + def _remove_auth_from_netloc(self, netloc): + # type: (str) -> str + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.split("@", 1) + if ( + isinstance(self.info, VcsInfo) and + self.info.vcs == "git" and + user_pass == "git" + ): + return netloc + if ENV_VAR_RE.match(user_pass): + return netloc + return netloc_no_user_pass + + @property + def redacted_url(self): + # type: () -> str + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is ``git`` + in the case of a git URL. + """ + purl = urllib_parse.urlsplit(self.url) + netloc = self._remove_auth_from_netloc(purl.netloc) + surl = urllib_parse.urlunsplit( + (purl.scheme, netloc, purl.path, purl.query, purl.fragment) + ) + return surl + + def validate(self): + # type: () -> None + self.from_dict(self.to_dict()) + + @classmethod + def from_dict(cls, d): + # type: (Dict[str, Any]) -> DirectUrl + return DirectUrl( + url=_get_required(d, str, "url"), + subdirectory=_get(d, str, "subdirectory"), + info=_exactly_one_of( + [ + ArchiveInfo._from_dict(_get(d, dict, "archive_info")), + DirInfo._from_dict(_get(d, dict, "dir_info")), + VcsInfo._from_dict(_get(d, dict, "vcs_info")), + ] + ), + ) + + def to_dict(self): + # type: () -> Dict[str, Any] + res = _filter_none( + url=self.redacted_url, + subdirectory=self.subdirectory, + ) + res[self.info.name] = self.info._to_dict() + return res + + @classmethod + def from_json(cls, s): + # type: (str) -> DirectUrl + return cls.from_dict(json.loads(s)) + + def to_json(self): + # type: () -> str + return json.dumps(self.to_dict(), sort_keys=True) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/format_control.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..c6275e7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,92 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import CommandError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Set, FrozenSet + + +class FormatControl(object): + """Helper for managing formats from which a package can be installed. + """ + + __slots__ = ["no_binary", "only_binary"] + + def __init__(self, no_binary=None, only_binary=None): + # type: (Optional[Set[str]], Optional[Set[str]]) -> None + if no_binary is None: + no_binary = set() + if only_binary is None: + only_binary = set() + + self.no_binary = no_binary + self.only_binary = only_binary + + def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, self.__class__): + return NotImplemented + + if self.__slots__ != other.__slots__: + return False + + return all( + getattr(self, k) == getattr(other, k) + for k in self.__slots__ + ) + + def __ne__(self, other): + # type: (object) -> bool + return not self.__eq__(other) + + def __repr__(self): + # type: () -> str + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + # type: (str, Set[str], Set[str]) -> None + if value.startswith('-'): + raise CommandError( + "--no-binary / --only-binary option requires 1 argument." + ) + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + # Without a none, we want to discard everything as :all: covers it + if ':none:' not in new: + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name): + # type: (str) -> FrozenSet[str] + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard('source') + elif canonical_name in self.no_binary: + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') + return frozenset(result) + + def disallow_binaries(self): + # type: () -> None + self.handle_mutual_excludes( + ':all:', self.no_binary, self.only_binary, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/index.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..5b4a1fe --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/index.py @@ -0,0 +1,34 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class PackageIndex(object): + """Represents a Package Index and provides easier access to endpoints + """ + + __slots__ = ['url', 'netloc', 'simple_url', 'pypi_url', + 'file_storage_domain'] + + def __init__(self, url, file_storage_domain): + # type: (str, str) -> None + super(PackageIndex, self).__init__() + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path): + # type: (str) -> str + return urllib_parse.urljoin(self.url, path) + + +PyPI = PackageIndex( + 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' +) +TestPyPI = PackageIndex( + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' +) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/link.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..c0d278a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/link.py @@ -0,0 +1,245 @@ +import os +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.misc import ( + redact_auth_from_url, + split_auth_from_netloc, + splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Tuple, Union + from pip._internal.index.collector import HTMLPage + from pip._internal.utils.hashes import Hashes + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + __slots__ = [ + "_parsed_url", + "_url", + "comes_from", + "requires_python", + "yanked_reason", + "cache_link_parsing", + ] + + def __init__( + self, + url, # type: str + comes_from=None, # type: Optional[Union[str, HTMLPage]] + requires_python=None, # type: Optional[str] + yanked_reason=None, # type: Optional[Text] + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param url: url of the resource pointed to (href of the link) + :param comes_from: instance of HTMLPage where the link was found, + or string. + :param requires_python: String containing the `Requires-Python` + metadata field, specified in PEP 345. This may be specified by + a data-requires-python attribute in the HTML link tag, as + described in PEP 503. + :param yanked_reason: the reason the file has been yanked, if the + file has been yanked, or None if the file hasn't been yanked. + This is the value of the "data-yanked" attribute, if present, in + a simple repository HTML link. If the file has been yanked but + no reason was provided, this should be the empty string. See + PEP 592 for more information and the specification. + :param cache_link_parsing: A flag that is used elsewhere to determine + whether resources retrieved from this link + should be cached. PyPI index urls should + generally have this set to False, for + example. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self._parsed_url = urllib_parse.urlsplit(url) + # Store the url as a private attribute to prevent accidentally + # trying to set a new value. + self._url = url + + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + self.yanked_reason = yanked_reason + + super(Link, self).__init__(key=url, defining_class=Link) + + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + if self.requires_python: + rp = ' (requires-python:{})'.format(self.requires_python) + else: + rp = '' + if self.comes_from: + return '{} (from {}){}'.format( + redact_auth_from_url(self._url), self.comes_from, rp) + else: + return redact_auth_from_url(str(self._url)) + + def __repr__(self): + # type: () -> str + return ''.format(self) + + @property + def url(self): + # type: () -> str + return self._url + + @property + def filename(self): + # type: () -> str + path = self.path.rstrip('/') + name = posixpath.basename(path) + if not name: + # Make sure we don't leak auth information if the netloc + # includes a username and password. + netloc, user_pass = split_auth_from_netloc(self.netloc) + return netloc + + name = urllib_parse.unquote(name) + assert name, ( + 'URL {self._url!r} produced no filename'.format(**locals())) + return name + + @property + def file_path(self): + # type: () -> str + return url_to_path(self.url) + + @property + def scheme(self): + # type: () -> str + return self._parsed_url.scheme + + @property + def netloc(self): + # type: () -> str + """ + This can contain auth information. + """ + return self._parsed_url.netloc + + @property + def path(self): + # type: () -> str + return urllib_parse.unquote(self._parsed_url.path) + + def splitext(self): + # type: () -> Tuple[str, str] + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + # type: () -> str + return self.splitext()[1] + + @property + def url_without_fragment(self): + # type: () -> str + scheme, netloc, path, query, fragment = self._parsed_url + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + # type: () -> Optional[str] + match = self._egg_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + # type: () -> Optional[str] + match = self._subdirectory_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + # type: () -> Optional[str] + match = self._hash_re.search(self._url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + # type: () -> Optional[str] + match = self._hash_re.search(self._url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + # type: () -> str + return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_file(self): + # type: () -> bool + return self.scheme == 'file' + + def is_existing_dir(self): + # type: () -> bool + return self.is_file and os.path.isdir(self.file_path) + + @property + def is_wheel(self): + # type: () -> bool + return self.ext == WHEEL_EXTENSION + + @property + def is_vcs(self): + # type: () -> bool + from pip._internal.vcs import vcs + + return self.scheme in vcs.all_schemes + + @property + def is_yanked(self): + # type: () -> bool + return self.yanked_reason is not None + + @property + def has_hash(self): + # type: () -> bool + return self.hash_name is not None + + def is_hash_allowed(self, hashes): + # type: (Optional[Hashes]) -> bool + """ + Return True if the link has a hash and it is allowed. + """ + if hashes is None or not self.has_hash: + return False + # Assert non-None so mypy knows self.hash_name and self.hash are str. + assert self.hash_name is not None + assert self.hash is not None + + return hashes.is_hash_allowed(self.hash_name, hex_digest=self.hash) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/scheme.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/scheme.py new file mode 100644 index 0000000..5040551 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/scheme.py @@ -0,0 +1,31 @@ +""" +For types associated with installation schemes. + +For a general overview of available schemes and their context, see +https://docs.python.org/3/install/index.html#alternate-installation. +""" + + +SCHEME_KEYS = ['platlib', 'purelib', 'headers', 'scripts', 'data'] + + +class Scheme(object): + """A Scheme holds paths which are used as the base directories for + artifacts associated with a Python package. + """ + + __slots__ = SCHEME_KEYS + + def __init__( + self, + platlib, # type: str + purelib, # type: str + headers, # type: str + scripts, # type: str + data, # type: str + ): + self.platlib = platlib + self.purelib = purelib + self.headers = headers + self.scripts = scripts + self.data = data diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/search_scope.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/search_scope.py new file mode 100644 index 0000000..d732504 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/search_scope.py @@ -0,0 +1,135 @@ +import itertools +import logging +import os +import posixpath + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import has_tls +from pip._internal.utils.misc import normalize_path, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +logger = logging.getLogger(__name__) + + +class SearchScope(object): + + """ + Encapsulates the locations that pip is configured to search. + """ + + __slots__ = ["find_links", "index_urls"] + + @classmethod + def create( + cls, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> SearchScope + """ + Create a SearchScope object after normalizing the `find_links`. + """ + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + built_find_links = [] # type: List[str] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + built_find_links.append(link) + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not has_tls(): + for link in itertools.chain(index_urls, built_find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == 'https': + logger.warning( + 'pip is configured with locations that require ' + 'TLS/SSL, however the ssl module in Python is not ' + 'available.' + ) + break + + return cls( + find_links=built_find_links, + index_urls=index_urls, + ) + + def __init__( + self, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> None + self.find_links = find_links + self.index_urls = index_urls + + def get_formatted_locations(self): + # type: () -> str + lines = [] + redacted_index_urls = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + for url in self.index_urls: + + redacted_index_url = redact_auth_from_url(url) + + # Parse the URL + purl = urllib_parse.urlsplit(redacted_index_url) + + # URL is generally invalid if scheme and netloc is missing + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not purl.scheme and not purl.netloc: + logger.warning( + 'The index url "%s" seems invalid, ' + 'please provide a scheme.', redacted_index_url) + + redacted_index_urls.append(redacted_index_url) + + lines.append('Looking in indexes: {}'.format( + ', '.join(redacted_index_urls))) + + if self.find_links: + lines.append( + 'Looking in links: {}'.format(', '.join( + redact_auth_from_url(url) for url in self.find_links)) + ) + return '\n'.join(lines) + + def get_index_urls_locations(self, project_name): + # type: (str) -> List[str] + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + # type: (str) -> str + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py new file mode 100644 index 0000000..5db3ca9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py @@ -0,0 +1,49 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + from pip._internal.models.format_control import FormatControl + + +class SelectionPreferences(object): + """ + Encapsulates the candidate selection preferences for downloading + and installing files. + """ + + __slots__ = ['allow_yanked', 'allow_all_prereleases', 'format_control', + 'prefer_binary', 'ignore_requires_python'] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + allow_yanked, # type: bool + allow_all_prereleases=False, # type: bool + format_control=None, # type: Optional[FormatControl] + prefer_binary=False, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """Create a SelectionPreferences object. + + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param prefer_binary: Whether to prefer an old, but valid, binary + dist over a new source dist. + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self.allow_yanked = allow_yanked + self.allow_all_prereleases = allow_all_prereleases + self.format_control = format_control + self.prefer_binary = prefer_binary + self.ignore_requires_python = ignore_requires_python diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/target_python.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/target_python.py new file mode 100644 index 0000000..6d1ca79 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/target_python.py @@ -0,0 +1,120 @@ +import sys + +from pip._internal.utils.compatibility_tags import ( + get_supported, + version_info_to_nodot, +) +from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import Tag + + +class TargetPython(object): + + """ + Encapsulates the properties of a Python interpreter one is targeting + for a package install, download, etc. + """ + + __slots__ = [ + "_given_py_version_info", + "abi", + "implementation", + "platform", + "py_version", + "py_version_info", + "_valid_tags", + ] + + def __init__( + self, + platform=None, # type: Optional[str] + py_version_info=None, # type: Optional[Tuple[int, ...]] + abi=None, # type: Optional[str] + implementation=None, # type: Optional[str] + ): + # type: (...) -> None + """ + :param platform: A string or None. If None, searches for packages + that are supported by the current system. Otherwise, will find + packages that can be built on the platform passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param py_version_info: An optional tuple of ints representing the + Python version information to use (e.g. `sys.version_info[:3]`). + This can have length 1, 2, or 3 when provided. + :param abi: A string or None. This is passed to compatibility_tags.py's + get_supported() function as is. + :param implementation: A string or None. This is passed to + compatibility_tags.py's get_supported() function as is. + """ + # Store the given py_version_info for when we call get_supported(). + self._given_py_version_info = py_version_info + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + py_version = '.'.join(map(str, py_version_info[:2])) + + self.abi = abi + self.implementation = implementation + self.platform = platform + self.py_version = py_version + self.py_version_info = py_version_info + + # This is used to cache the return value of get_tags(). + self._valid_tags = None # type: Optional[List[Tag]] + + def format_given(self): + # type: () -> str + """ + Format the given, non-None attributes for display. + """ + display_version = None + if self._given_py_version_info is not None: + display_version = '.'.join( + str(part) for part in self._given_py_version_info + ) + + key_values = [ + ('platform', self.platform), + ('version_info', display_version), + ('abi', self.abi), + ('implementation', self.implementation), + ] + return ' '.join( + '{}={!r}'.format(key, value) for key, value in key_values + if value is not None + ) + + def get_tags(self): + # type: () -> List[Tag] + """ + Return the supported PEP 425 tags to check wheel candidates against. + + The tags are returned in order of preference (most preferred first). + """ + if self._valid_tags is None: + # Pass versions=None if no py_version_info was given since + # versions=None uses special default logic. + py_version_info = self._given_py_version_info + if py_version_info is None: + version = None + else: + version = version_info_to_nodot(py_version_info) + + tags = get_supported( + version=version, + platform=self.platform, + abi=self.abi, + impl=self.implementation, + ) + self._valid_tags = tags + + return self._valid_tags diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/wheel.py new file mode 100644 index 0000000..4d4068f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/models/wheel.py @@ -0,0 +1,78 @@ +"""Represents a wheel file and provides access to the various parts of the +name that have meaning. +""" +import re + +from pip._vendor.packaging.tags import Tag + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +class Wheel(object): + """A wheel file""" + + wheel_file_re = re.compile( + r"""^(?P(?P.+?)-(?P.*?)) + ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + # type: (str) -> None + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "{} is not a valid wheel filename.".format(filename) + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = { + Tag(x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def get_formatted_file_tags(self): + # type: () -> List[str] + """Return the wheel's tags as a sorted list of strings.""" + return sorted(str(tag) for tag in self.file_tags) + + def support_index_min(self, tags): + # type: (List[Tag]) -> int + """Return the lowest index that one of the wheel's file_tag combinations + achieves in the given list of supported tags. + + For example, if there are 8 supported tags and one of the file tags + is first in the list, then return 0. + + :param tags: the PEP 425 tags to check the wheel against, in order + with most preferred first. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min(tags.index(tag) for tag in self.file_tags if tag in tags) + + def supported(self, tags): + # type: (List[Tag]) -> bool + """Return whether the wheel is compatible with one of the given tags. + + :param tags: the PEP 425 tags to check the wheel against. + """ + return not self.file_tags.isdisjoint(tags) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__init__.py new file mode 100644 index 0000000..b51bde9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__init__.py @@ -0,0 +1,2 @@ +"""Contains purely network-related utilities. +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ae74095287321f7c3eab6e08286429387561587 GIT binary patch literal 284 zcmYk1F-`+95JkOdh$7_<+eXMc5>!YCAwf-pXlSg#9wlZS+t{PIokyn1CYN+imSK;Y;v)oqH{mA5PKuHhuglQa@<)SPuXI literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b3f9d783ca0f33f72713f936ec3985282957566 GIT binary patch literal 7136 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0f09fb3243765d75e5ade9322eaa2d826bdc02d GIT binary patch literal 9263 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..908a38fc7603f80c52a238cc5af5bcf456d18171 GIT binary patch literal 1437 zcmZux&u<(x6t+D-cCy)|t0)a9mFNhG!%EqmB7~Gy6>8WlP1LfWq-n_r7){1b;_j?x zVtdo>YIA`~T&TpATj9o)1AmOKT;R?L4)8pitpdWMdG_;rWBYyIH%^z9>ImBRgTK;; zO@w~6&ei6Ca}QqiIT(s4&e0IZct~P`txs}i=*Dg}c5-j%$38~Pmv0NF&qWRzVEHBW21uR@Kv1kw zo8e6fSpV2rLl;eiP<5LF6GqZ9axE*X)Znd>pgirKHB~^k02aqm6 z-*Sv=*EY+iT9*m4d?!P7A{!9=3CUIH{`@97P)w>QJ55D;QV5zx+eJ1SLR~Ofu;?sp zL)T`{OD6ejy0iz^ngj?qZ-Ty@H^Y08ujuteTr}JoPphL|okP2yWsAUL`(R#{}2^4xCx1(}k^pt`e_CrE674$1i8YaOCj&J7L+383O zRHq(7`v`bno)HS90VYECTdr|r`po!B1M@?(yrs3|Mgx^Ma2nVJK;>Hy>9>~R8^BX1fwE{J zJM+3aLr`3NrJic zvT``IJ#gWQgv22^@-O+yiGKkk;5YWBv?WgF`F+jvoA-NfHdm50b1*MJ93`>7>YmKaeh&3~9a8Iy1P#2t}uiq!M5*PK16|$ldiK z&qZu@-IlVDu&l@^QGz`!BvXhtQ_!t`_juzmTfcXE{Tp`w;bijFwo zAe2Io*nIi*$A)kdOMMI?2uepni__5NKDW8^l7wb-0Hk2K6f9eczn&)d}bK zH)OG&a50DmPtK^olebDR*iRzk`d?;Gf&rdlYl_p38vagf@I8GxV2r$urLKc$GNs2> zMJoHmqD0%rPDM}XkyAO-PDPko!NxpJkTRVNeQ9A&-F=mD?K76-iDsQ(x-Jd$+;r2c?h*V$|iK7D}^H7N$ZAaZTj= zW2A1xog)8q^!ZpNTC9~(yc=x`>`KZpj;V#rqja3NJvD9t8kL8#hjgxDDIbJb%QT=q zonQGsz@yV@#RiDe$Bd_04Jf~YsmPR6w6ZwHk&~zHQAZn)IsnF{b5MKcSBRphI-`@MlVnx|GbXSKVo#@~_Ers(Z(M z-ZAoYsd7&+?9a&~@)%z}>i*EpgZ6!c+<*pc;7;a!%#%hv*ESDJG557N(54!jL%VhW zSyX#FBI3ZNc9oVQ%H|*3+uYn32s3p(RUCX8Amx!;oO&EZ)SRAtKf&ypxRs~TGcl4l9!;69~!MK4&tT}TXNCp zyv62g5Y$H?#9Bo&SZIfUI{&zqPhGl6wX>g|DrAVmlSTwRMD}0r%(G97N}#W9oYW@twa{p literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/auth.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/auth.py new file mode 100644 index 0000000..c49deaa --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/auth.py @@ -0,0 +1,310 @@ +"""Network Authentication Helpers + +Contains interface (MultiDomainBasicAuth) and associated glue code for +providing credentials in the context of network requests. +""" + +import logging + +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.utils import get_netrc_auth +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import ( + ask, + ask_input, + ask_password, + remove_auth_from_url, + split_auth_netloc_from_url, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Optional, Tuple, List, Any + + from pip._internal.vcs.versioncontrol import AuthInfo + + from pip._vendor.requests.models import Response, Request + + Credentials = Tuple[str, str, str] + +logger = logging.getLogger(__name__) + +try: + import keyring # noqa +except ImportError: + keyring = None +except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + keyring = None + + +def get_keyring_auth(url, username): + # type: (str, str) -> Optional[AuthInfo] + """Return the tuple auth for a given url from keyring.""" + global keyring + if not url or not keyring: + return None + + try: + try: + get_credential = keyring.get_credential + except AttributeError: + pass + else: + logger.debug("Getting credentials from keyring for %s", url) + cred = get_credential(url, username) + if cred is not None: + return cred.username, cred.password + return None + + if username: + logger.debug("Getting password from keyring for %s", url) + password = keyring.get_password(url, username) + if password: + return username, password + + except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + keyring = None + return None + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True, index_urls=None): + # type: (bool, Optional[List[str]]) -> None + self.prompting = prompting + self.index_urls = index_urls + self.passwords = {} # type: Dict[str, AuthInfo] + # When the user is prompted to enter credentials and keyring is + # available, we will offer to save them. If the user accepts, + # this value is set to the credentials they entered. After the + # request authenticates, the caller should call + # ``save_credentials`` to save these. + self._credentials_to_save = None # type: Optional[Credentials] + + def _get_index_url(self, url): + # type: (str) -> Optional[str] + """Return the original index URL matching the requested URL. + + Cached or dynamically generated credentials may work against + the original index URL rather than just the netloc. + + The provided url should have had its username and password + removed already. If the original index url had credentials then + they will be included in the return value. + + Returns None if no matching index was found, or if --no-index + was specified by the user. + """ + if not url or not self.index_urls: + return None + + for u in self.index_urls: + prefix = remove_auth_from_url(u).rstrip("/") + "/" + if url.startswith(prefix): + return u + return None + + def _get_new_credentials(self, original_url, allow_netrc=True, + allow_keyring=True): + # type: (str, bool, bool) -> AuthInfo + """Find and return credentials for the specified URL.""" + # Split the credentials and netloc from the url. + url, netloc, url_user_password = split_auth_netloc_from_url( + original_url, + ) + + # Start with the credentials embedded in the url + username, password = url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in url for %s", netloc) + return url_user_password + + # Find a matching index url for this request + index_url = self._get_index_url(url) + if index_url: + # Split the credentials from the url. + index_info = split_auth_netloc_from_url(index_url) + if index_info: + index_url, _, index_url_user_password = index_info + logger.debug("Found index url %s", index_url) + + # If an index URL was found, try its embedded credentials + if index_url and index_url_user_password[0] is not None: + username, password = index_url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in index url for %s", netloc) + return index_url_user_password + + # Get creds from netrc if we still don't have them + if allow_netrc: + netrc_auth = get_netrc_auth(original_url) + if netrc_auth: + logger.debug("Found credentials in netrc for %s", netloc) + return netrc_auth + + # If we don't have a password and keyring is available, use it. + if allow_keyring: + # The index url is more specific than the netloc, so try it first + kr_auth = ( + get_keyring_auth(index_url, username) or + get_keyring_auth(netloc, username) + ) + if kr_auth: + logger.debug("Found credentials in keyring for %s", netloc) + return kr_auth + + return username, password + + def _get_url_and_credentials(self, original_url): + # type: (str) -> Tuple[str, Optional[str], Optional[str]] + """Return the credentials to use for the provided URL. + + If allowed, netrc and keyring may be used to obtain the + correct credentials. + + Returns (url_without_credentials, username, password). Note + that even if the original URL contains credentials, this + function may return a different username and password. + """ + url, netloc, _ = split_auth_netloc_from_url(original_url) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + if username is None and password is None: + # No stored credentials. Acquire new credentials without prompting + # the user. (e.g. from netrc, keyring, or the URL itself) + username, password = self._get_new_credentials(original_url) + + if username is not None or password is not None: + # Convert the username and password if they're None, so that + # this netloc will show up as "cached" in the conditional above. + # Further, HTTPBasicAuth doesn't accept None, so it makes sense to + # cache the value that is going to be used. + username = username or "" + password = password or "" + + # Store any acquired credentials. + self.passwords[netloc] = (username, password) + + assert ( + # Credentials were found + (username is not None and password is not None) or + # Credentials were not found + (username is None and password is None) + ), "Could not load credentials from url: {}".format(original_url) + + return url, username, password + + def __call__(self, req): + # type: (Request) -> Request + # Get credentials for this request + url, username, password = self._get_url_and_credentials(req.url) + + # Set the url of the request to the url without any credentials + req.url = url + + if username is not None and password is not None: + # Send the basic auth with this request + req = HTTPBasicAuth(username, password)(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + # Factored out to allow for easy patching in tests + def _prompt_for_password(self, netloc): + # type: (str) -> Tuple[Optional[str], Optional[str], bool] + username = ask_input("User for {}: ".format(netloc)) + if not username: + return None, None, False + auth = get_keyring_auth(netloc, username) + if auth and auth[0] is not None and auth[1] is not None: + return auth[0], auth[1], False + password = ask_password("Password: ") + return username, password, True + + # Factored out to allow for easy patching in tests + def _should_save_password_to_keyring(self): + # type: () -> bool + if not keyring: + return False + return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + + def handle_401(self, resp, **kwargs): + # type: (Response, **Any) -> Response + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simply return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username, password, save = self._prompt_for_password(parsed.netloc) + + # Store the new username and password to use for future requests + self._credentials_to_save = None + if username is not None and password is not None: + self.passwords[parsed.netloc] = (username, password) + + # Prompt to save the password to keyring + if save and self._should_save_password_to_keyring(): + self._credentials_to_save = (parsed.netloc, username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + req.register_hook("response", self.warn_on_401) + + # On successful request, save the credentials that were used to + # keyring. (Note that if the user responded "no" above, this member + # is not set and nothing will be saved.) + if self._credentials_to_save: + req.register_hook("response", self.save_credentials) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def warn_on_401(self, resp, **kwargs): + # type: (Response, **Any) -> None + """Response callback to warn about incorrect credentials.""" + if resp.status_code == 401: + logger.warning( + '401 Error, Credentials not correct for %s', resp.request.url, + ) + + def save_credentials(self, resp, **kwargs): + # type: (Response, **Any) -> None + """Response callback to save credentials on success.""" + assert keyring is not None, "should never reach here without keyring" + if not keyring: + return + + creds = self._credentials_to_save + self._credentials_to_save = None + if creds and resp.status_code < 400: + try: + logger.info('Saving credentials to keyring') + keyring.set_password(*creds) + except Exception: + logger.exception('Failed to save credentials') diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/cache.py new file mode 100644 index 0000000..a0d55b5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/cache.py @@ -0,0 +1,79 @@ +"""HTTP cache implementation. +""" + +import os +from contextlib import contextmanager + +from pip._vendor.cachecontrol.cache import BaseCache +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.requests.models import Response + +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Iterator + + +def is_from_cache(response): + # type: (Response) -> bool + return getattr(response, "from_cache", False) + + +@contextmanager +def suppressed_cache_errors(): + # type: () -> Iterator[None] + """If we can't access the cache then we can just skip caching and process + requests as if caching wasn't enabled. + """ + try: + yield + except (OSError, IOError): + pass + + +class SafeFileCache(BaseCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, directory): + # type: (str) -> None + assert directory is not None, "Cache directory must not be None." + super(SafeFileCache, self).__init__() + self.directory = directory + + def _get_cache_path(self, name): + # type: (str) -> str + # From cachecontrol.caches.file_cache.FileCache._fn, brought into our + # class for backwards-compatibility and to avoid using a non-public + # method. + hashed = FileCache.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + # type: (str) -> Optional[bytes] + path = self._get_cache_path(key) + with suppressed_cache_errors(): + with open(path, 'rb') as f: + return f.read() + + def set(self, key, value): + # type: (str, bytes) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + ensure_dir(os.path.dirname(path)) + + with adjacent_tmp_file(path) as f: + f.write(value) + + replace(f.name, path) + + def delete(self, key): + # type: (str) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + os.remove(path) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/download.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/download.py new file mode 100644 index 0000000..44f9985 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/download.py @@ -0,0 +1,182 @@ +"""Download files with progress indicators. +""" +import cgi +import logging +import mimetypes +import os + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE + +from pip._internal.cli.progress_bars import DownloadProgressProvider +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.index import PyPI +from pip._internal.network.cache import is_from_cache +from pip._internal.network.utils import ( + HEADERS, + raise_for_status, + response_chunks, +) +from pip._internal.utils.misc import ( + format_size, + redact_auth_from_url, + splitext, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, Optional + + from pip._vendor.requests.models import Response + + from pip._internal.models.link import Link + from pip._internal.network.session import PipSession + +logger = logging.getLogger(__name__) + + +def _get_http_response_size(resp): + # type: (Response) -> Optional[int] + try: + return int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + return None + + +def _prepare_download( + resp, # type: Response + link, # type: Link + progress_bar # type: str +): + # type: (...) -> Iterable[bytes] + total_length = _get_http_response_size(resp) + + if link.netloc == PyPI.file_storage_domain: + url = link.show_url + else: + url = link.url_without_fragment + + logged_url = redact_auth_from_url(url) + + if total_length: + logged_url = '{} ({})'.format(logged_url, format_size(total_length)) + + if is_from_cache(resp): + logger.info("Using cached %s", logged_url) + else: + logger.info("Downloading %s", logged_url) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif is_from_cache(resp): + show_progress = False + elif not total_length: + show_progress = True + elif total_length > (40 * 1000): + show_progress = True + else: + show_progress = False + + chunks = response_chunks(resp, CONTENT_CHUNK_SIZE) + + if not show_progress: + return chunks + + return DownloadProgressProvider( + progress_bar, max=total_length + )(chunks) + + +def sanitize_content_filename(filename): + # type: (str) -> str + """ + Sanitize the "filename" value from a Content-Disposition header. + """ + return os.path.basename(filename) + + +def parse_content_disposition(content_disposition, default_filename): + # type: (str, str) -> str + """ + Parse the "filename" value from a Content-Disposition header, and + return the default filename if the result is empty. + """ + _type, params = cgi.parse_header(content_disposition) + filename = params.get('filename') + if filename: + # We need to sanitize the filename to prevent directory traversal + # in case the filename contains ".." path parts. + filename = sanitize_content_filename(filename) + return filename or default_filename + + +def _get_http_response_filename(resp, link): + # type: (Response, Link) -> str + """Get an ideal filename from the given HTTP response, falling back to + the link filename if not provided. + """ + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + filename = parse_content_disposition(content_disposition, filename) + ext = splitext(filename)[1] # type: Optional[str] + if not ext: + ext = mimetypes.guess_extension( + resp.headers.get('content-type', '') + ) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + return filename + + +def _http_get_download(session, link): + # type: (PipSession, Link) -> Response + target_url = link.url.split('#', 1)[0] + resp = session.get(target_url, headers=HEADERS, stream=True) + raise_for_status(resp) + return resp + + +class Download(object): + def __init__( + self, + response, # type: Response + filename, # type: str + chunks, # type: Iterable[bytes] + ): + # type: (...) -> None + self.response = response + self.filename = filename + self.chunks = chunks + + +class Downloader(object): + def __init__( + self, + session, # type: PipSession + progress_bar, # type: str + ): + # type: (...) -> None + self._session = session + self._progress_bar = progress_bar + + def __call__(self, link): + # type: (Link) -> Download + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", e.response.status_code, link + ) + raise + + return Download( + resp, + _get_http_response_filename(resp, link), + _prepare_download(resp, link, self._progress_bar), + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py new file mode 100644 index 0000000..a0f9e15 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py @@ -0,0 +1,235 @@ +"""Lazy ZIP over HTTP""" + +__all__ = ['HTTPRangeRequestUnsupported', 'dist_from_wheel_url'] + +from bisect import bisect_left, bisect_right +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from zipfile import BadZipfile, ZipFile + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.six.moves import range + +from pip._internal.network.utils import ( + HEADERS, + raise_for_status, + response_chunks, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, List, Optional, Tuple + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Response + + from pip._internal.network.session import PipSession + + +class HTTPRangeRequestUnsupported(Exception): + pass + + +def dist_from_wheel_url(name, url, session): + # type: (str, str, PipSession) -> Distribution + """Return a pkg_resources.Distribution from the given wheel URL. + + This uses HTTP range requests to only fetch the potion of the wheel + containing metadata, just enough for the object to be constructed. + If such requests are not supported, HTTPRangeRequestUnsupported + is raised. + """ + with LazyZipOverHTTP(url, session) as wheel: + # For read-only ZIP files, ZipFile only needs methods read, + # seek, seekable and tell, not the whole IO protocol. + zip_file = ZipFile(wheel) # type: ignore + # After context manager exit, wheel.name + # is an invalid file by intention. + return pkg_resources_distribution_for_wheel(zip_file, name, wheel.name) + + +class LazyZipOverHTTP(object): + """File-like object mapped to a ZIP file over HTTP. + + This uses HTTP range requests to lazily fetch the file's content, + which is supposed to be fed to ZipFile. If such requests are not + supported by the server, raise HTTPRangeRequestUnsupported + during initialization. + """ + + def __init__(self, url, session, chunk_size=CONTENT_CHUNK_SIZE): + # type: (str, PipSession, int) -> None + head = session.head(url, headers=HEADERS) + raise_for_status(head) + assert head.status_code == 200 + self._session, self._url, self._chunk_size = session, url, chunk_size + self._length = int(head.headers['Content-Length']) + self._file = NamedTemporaryFile() + self.truncate(self._length) + self._left = [] # type: List[int] + self._right = [] # type: List[int] + if 'bytes' not in head.headers.get('Accept-Ranges', 'none'): + raise HTTPRangeRequestUnsupported('range request is not supported') + self._check_zip() + + @property + def mode(self): + # type: () -> str + """Opening mode, which is always rb.""" + return 'rb' + + @property + def name(self): + # type: () -> str + """Path to the underlying file.""" + return self._file.name + + def seekable(self): + # type: () -> bool + """Return whether random access is supported, which is True.""" + return True + + def close(self): + # type: () -> None + """Close the file.""" + self._file.close() + + @property + def closed(self): + # type: () -> bool + """Whether the file is closed.""" + return self._file.closed + + def read(self, size=-1): + # type: (int) -> bytes + """Read up to size bytes from the object and return them. + + As a convenience, if size is unspecified or -1, + all bytes until EOF are returned. Fewer than + size bytes may be returned if EOF is reached. + """ + download_size = max(size, self._chunk_size) + start, length = self.tell(), self._length + stop = length if size < 0 else min(start+download_size, length) + start = max(0, stop-download_size) + self._download(start, stop-1) + return self._file.read(size) + + def readable(self): + # type: () -> bool + """Return whether the file is readable, which is True.""" + return True + + def seek(self, offset, whence=0): + # type: (int, int) -> int + """Change stream position and return the new absolute position. + + Seek to offset relative position indicated by whence: + * 0: Start of stream (the default). pos should be >= 0; + * 1: Current position - pos may be negative; + * 2: End of stream - pos usually negative. + """ + return self._file.seek(offset, whence) + + def tell(self): + # type: () -> int + """Return the current possition.""" + return self._file.tell() + + def truncate(self, size=None): + # type: (Optional[int]) -> int + """Resize the stream to the given size in bytes. + + If size is unspecified resize to the current position. + The current stream position isn't changed. + + Return the new file size. + """ + return self._file.truncate(size) + + def writable(self): + # type: () -> bool + """Return False.""" + return False + + def __enter__(self): + # type: () -> LazyZipOverHTTP + self._file.__enter__() + return self + + def __exit__(self, *exc): + # type: (*Any) -> Optional[bool] + return self._file.__exit__(*exc) + + @contextmanager + def _stay(self): + # type: ()-> Iterator[None] + """Return a context manager keeping the position. + + At the end of the block, seek back to original position. + """ + pos = self.tell() + try: + yield + finally: + self.seek(pos) + + def _check_zip(self): + # type: () -> None + """Check and download until the file is a valid ZIP.""" + end = self._length - 1 + for start in reversed(range(0, end, self._chunk_size)): + self._download(start, end) + with self._stay(): + try: + # For read-only ZIP files, ZipFile only needs + # methods read, seek, seekable and tell. + ZipFile(self) # type: ignore + except BadZipfile: + pass + else: + break + + def _stream_response(self, start, end, base_headers=HEADERS): + # type: (int, int, Dict[str, str]) -> Response + """Return HTTP response to a range request from start to end.""" + headers = base_headers.copy() + headers['Range'] = 'bytes={}-{}'.format(start, end) + # TODO: Get range requests to be correctly cached + headers['Cache-Control'] = 'no-cache' + return self._session.get(self._url, headers=headers, stream=True) + + def _merge(self, start, end, left, right): + # type: (int, int, int, int) -> Iterator[Tuple[int, int]] + """Return an iterator of intervals to be fetched. + + Args: + start (int): Start of needed interval + end (int): End of needed interval + left (int): Index of first overlapping downloaded data + right (int): Index after last overlapping downloaded data + """ + lslice, rslice = self._left[left:right], self._right[left:right] + i = start = min([start]+lslice[:1]) + end = max([end]+rslice[-1:]) + for j, k in zip(lslice, rslice): + if j > i: + yield i, j-1 + i = k + 1 + if i <= end: + yield i, end + self._left[left:right], self._right[left:right] = [start], [end] + + def _download(self, start, end): + # type: (int, int) -> None + """Download bytes from start to end inclusively.""" + with self._stay(): + left = bisect_left(self._right, start) + right = bisect_right(self._left, end) + for start, end in self._merge(start, end, left, right): + response = self._stream_response(start, end) + response.raise_for_status() + self.seek(start) + for chunk in response_chunks(response, self._chunk_size): + self._file.write(chunk) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/session.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/session.py new file mode 100644 index 0000000..39a4a54 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/session.py @@ -0,0 +1,421 @@ +"""PipSession and supporting code, containing all pip-specific +network request configuration and behavior. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import email.utils +import json +import logging +import mimetypes +import os +import platform +import sys +import warnings + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.models import Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +from pip import __version__ +from pip._internal.network.auth import MultiDomainBasicAuth +from pip._internal.network.cache import SafeFileCache +# Import ssl from compat so the initial import occurs in only one place. +from pip._internal.utils.compat import has_tls, ipaddress +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.misc import ( + build_url_from_netloc, + get_installed_version, + parse_netloc, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import ( + Iterator, List, Optional, Tuple, Union, + ) + + from pip._internal.models.link import Link + + SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] + + +logger = logging.getLogger(__name__) + + +# Ignore warning raised when using --trusted-host. +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] # type: List[SecureOrigin] + + +# These are environment variables present when running under various +# CI systems. For each variable, some CI systems that use the variable +# are indicated. The collection was chosen so that for each of a number +# of popular systems, at least one of the environment variables is used. +# This list is used to provide some indication of and lower bound for +# CI traffic to PyPI. Thus, it is okay if the list is not comprehensive. +# For more background, see: https://github.com/pypa/pip/issues/5499 +CI_ENVIRONMENT_VARIABLES = ( + # Azure Pipelines + 'BUILD_BUILDID', + # Jenkins + 'BUILD_ID', + # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI + 'CI', + # Explicit environment variable. + 'PIP_IS_CI', +) + + +def looks_like_ci(): + # type: () -> bool + """ + Return whether it looks like pip is running under CI. + """ + # We don't use the method of checking for a tty (e.g. using isatty()) + # because some CI systems mimic a tty (e.g. Travis CI). Thus that + # method doesn't provide definitive information in either direction. + return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": __version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + if has_tls(): + import _ssl as ssl + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_version = get_installed_version("setuptools") + if setuptools_version is not None: + data["setuptools_version"] = setuptools_version + + # Use None rather than False so as not to give the impression that + # pip knows it is not being run under CI. Rather, it is a null or + # inconclusive result. Also, we include some value rather than no + # value to make it easier to know that the check has been run. + data["ci"] = True if looks_like_ci() else None + + user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") + if user_data is not None: + data["user_data"] = user_data + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureHTTPAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class InsecureCacheControlAdapter(CacheControlAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureCacheControlAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class PipSession(requests.Session): + + timeout = None # type: Optional[int] + + def __init__(self, *args, **kwargs): + """ + :param trusted_hosts: Domains not to emit warnings for when not using + HTTPS. + """ + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + trusted_hosts = kwargs.pop("trusted_hosts", []) # type: List[str] + index_urls = kwargs.pop("index_urls", None) + + super(PipSession, self).__init__(*args, **kwargs) + + # Namespace the attribute with "pip_" just in case to prevent + # possible conflicts with the base class. + self.pip_trusted_origins = [] # type: List[Tuple[str, Optional[int]]] + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth(index_urls=index_urls) + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 503, 520, 527], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching so we'll use it for all http:// URLs. + # If caching is disabled, we will also use it for + # https:// hosts that we've marked as ignoring + # TLS errors for (trusted-hosts). + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + # We want to _only_ cache responses on securely fetched origins or when + # the host is specified as trusted. We do this because + # we can't validate the response of an insecurely/untrusted fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + self._trusted_host_adapter = InsecureCacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + self._trusted_host_adapter = insecure_adapter + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + for host in trusted_hosts: + self.add_trusted_host(host, suppress_logging=True) + + def add_trusted_host(self, host, source=None, suppress_logging=False): + # type: (str, Optional[str], bool) -> None + """ + :param host: It is okay to provide a host that has previously been + added. + :param source: An optional source string, for logging where the host + string came from. + """ + if not suppress_logging: + msg = 'adding trusted host: {!r}'.format(host) + if source is not None: + msg += ' (from {})'.format(source) + logger.info(msg) + + host_port = parse_netloc(host) + if host_port not in self.pip_trusted_origins: + self.pip_trusted_origins.append(host_port) + + self.mount( + build_url_from_netloc(host) + '/', + self._trusted_host_adapter + ) + if not host_port[1]: + # Mount wildcard ports for the same host. + self.mount( + build_url_from_netloc(host) + ':', + self._trusted_host_adapter + ) + + def iter_secure_origins(self): + # type: () -> Iterator[SecureOrigin] + for secure_origin in SECURE_ORIGINS: + yield secure_origin + for host, port in self.pip_trusted_origins: + yield ('*', host, '*' if port is None else port) + + def is_secure_origin(self, location): + # type: (Link) -> bool + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin_protocol, origin_host, origin_port = ( + parsed.scheme, parsed.hostname, parsed.port, + ) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + origin_protocol = origin_protocol.rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in self.iter_secure_origins(): + secure_protocol, secure_host, secure_port = secure_origin + if origin_protocol != secure_protocol and secure_protocol != "*": + continue + + try: + addr = ipaddress.ip_address( + None + if origin_host is None + else six.ensure_text(origin_host) + ) + network = ipaddress.ip_network( + six.ensure_text(secure_host) + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if ( + origin_host and + origin_host.lower() != secure_host.lower() and + secure_host != "*" + ): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port matches. + if ( + origin_port != secure_port and + secure_port != "*" and + secure_port is not None + ): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + origin_host, + origin_host, + ) + + return False + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/utils.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/utils.py new file mode 100644 index 0000000..907b3fe --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/utils.py @@ -0,0 +1,97 @@ +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterator + +# The following comments and HTTP headers were originally added by +# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. +# +# We use Accept-Encoding: identity here because requests defaults to +# accepting compressed responses. This breaks in a variety of ways +# depending on how the server is configured. +# - Some servers will notice that the file isn't a compressible file +# and will leave the file alone and with an empty Content-Encoding +# - Some servers will notice that the file is already compressed and +# will leave the file alone, adding a Content-Encoding: gzip header +# - Some servers won't notice anything at all and will take a file +# that's already been compressed and compress it again, and set +# the Content-Encoding: gzip header +# By setting this to request only the identity encoding we're hoping +# to eliminate the third case. Hopefully there does not exist a server +# which when given a file will notice it is already compressed and that +# you're not asking for a compressed file and will then decompress it +# before sending because if that's the case I don't think it'll ever be +# possible to make this work. +HEADERS = {'Accept-Encoding': 'identity'} # type: Dict[str, str] + + +def raise_for_status(resp): + # type: (Response) -> None + http_error_msg = u'' + if isinstance(resp.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. + try: + reason = resp.reason.decode('utf-8') + except UnicodeDecodeError: + reason = resp.reason.decode('iso-8859-1') + else: + reason = resp.reason + + if 400 <= resp.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + elif 500 <= resp.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + if http_error_msg: + raise NetworkConnectionError(http_error_msg, response=resp) + + +def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE): + # type: (Response, int) -> Iterator[bytes] + """Given a requests Response, provide the data chunks. + """ + try: + # Special case for urllib3. + for chunk in response.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False, + ): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = response.raw.read(chunk_size) + if not chunk: + break + yield chunk diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py new file mode 100644 index 0000000..e611262 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py @@ -0,0 +1,52 @@ +"""xmlrpclib.Transport implementation +""" + +import logging + +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict + from pip._internal.network.session import PipSession + + +logger = logging.getLogger(__name__) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + # type: (str, PipSession, bool) -> None + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + # type: (str, str, Dict[str, str], bool) -> None + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + raise_for_status(response) + self.verbose = verbose + return self.parse_response(response.raw) + except NetworkConnectionError as exc: + assert exc.response + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2afcc1e7570b162904df3bd4327b8e5d2174689c GIT binary patch literal 232 zcmYk0F$%&!5Jfj&AwmvfkxXGFh*((Li1jiiL$W5>U3WKP4&ebjf>*Nj2zJ^yi-msp z|MOol45sNsu&&J;V?Sd2E8($X`w>SewqlxWZo)|0vKH=k_r0Vlgd`2YX_ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dde08bc4f1c90395e40f07fe136de9066fc419a GIT binary patch literal 3641 zcmZ`+&u<(_74GWp>FMbiJGNsxc7Aqtlif^6?4S*Z4Iu)YT{djiM9D5oT0pBkT{CXG ze~hbo9Bg_x;7Gtqi{ONiI5-lQ`~w{MD>(I)6E`GoAOYX2ZrkG(v{kRGURA$&RrS5^ zJqJ@$j)v#=+y4suc}@E(4JIEGgKwhATTIhJ&C@~_>M`>e-bSn^hG#I+nX#EPyoTyG zVk>ESP1UzzJ85|>)o;d5()QY_Z^xZv%9~;uXNS}L%q!;2@G0?kSZc+y$(%Q*Rypz6 zWZs)k7QBUI(OXQ;dFPTPZz);!mXq_|d3?RdFNE#BzN~o{`7*!2=T-lb>eI-;%w<}| z&+|*mnwq^*k1y9_7pt#uSIu2T|02Jt`fqR}Y=@n2YM>wJ-kafcc;=<%T??1OnQ-dFZq|20KICc0(_X|SjfX*R zI~Z{3{EZHLy~|1`4H6#aqhZW3n(YN?mPS1o+2=k@LAmgh?~EeB6Q1Tz0wMW(LSzCH z-6YuNei+GIL>nXeO~0QBKT2gD#IY|VC(*VU~8Avl?I;7(kg zbaAWhb$Ji%)P8jZfhec7;t%S>cx5}-Xt)Zj z=3D?zs~OCxh@zk7z3r9ZUU{`Ac#v~{Vhes>WXTD_uft`z1fex-=IACf*&K72&X(A; zKFuyN;bN|*(~9~b%r(*0(BxkrfHED;C^TT}&to&!4uBaYt%8Z0gs20&cRGbun9JH0 zVK5#ybG>M88Ni@j*oC&s#4fPfg3b)}5fdBKBTPcq5_gNnp@ntLzW$0aZRaj**|60d zJ6LCLwF-xb_j6s|EiAPX`o2-GZHPbRPSMEQq4~L9=!EdX5*_HQT4#_B9_odmu-n-G z-5MRE8}$Lt`T$*Ez>TEa%TkH!$CWw?t&jSy+{{LC=tqMz6P(rz11WiU%Z>7S&fVa! z1UxG~aP!R|cXLn^VnZ1@`VE4V8>At?osUFHp|g=?F%QxzGyrY}R0=LQl#&xK8QXlX z+bIqBSE1U#y@*AK(nUb%D< zNa703%_=NU0{9-(@(zS%1FgEPTg=isOb1#W!v=mG{F!W;>4w8**c>oDL#WopJNTxD zWPSWl=o3rOVacyT5N3avFeMN{y*VmvURN=Rg25bg5x6uBvcoN}HvLSady zBW;L6`z3K4 zM^`3S?kgUa574Q;f$cBhi>|5YpaxPWCQ!;xKUJCNLyi66!aC?BlMP?(> zj6n{>;vi9QARXL8`Y;N87p{SXQJmCi z!bLg(T~nG+v)6FMC{^f)kpwzB*NC#8Y2q#nb{o|(skE*LP&uyl-4NfvifZ4pHBU)D z==V_;0AnjO`DGFcZAuW@1hLI;(W-5NZD^asHDDWTSIu|Xdbe9PeLu{4zF#(}mZ-@w zj$t1qoD>wQ-UX!W6(6x3W@4rC0t;7Al*UqBoVR>x#pEqlg-T`NDCw_ER<*Li$C9#e zyh7Pt0T=gFK~$dqU(@;C5Pk@Xi?RiLJ+7)D+gBBn?~8Nf{iV~N0xc_O$1zMCR9dyE z76yX;ff7_Mhz^M<64NBkkhlrqIZvvZ7lohK{HV$*UVH5%t9b2NOXah7W@1Ju+G~`g zpK>{hCuZvkx{3goevCLr5rG1xy0(d{KEChLzMo{_DCQ4{UR9bgf$R=&jb@OfBB{f! XHPA+@3y-l_+1?D|(G2`CjypT)v{*%{ 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 Bdr<%Y literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5070873f2cc41c7483279f0096f8841dd0adff15 GIT binary patch literal 11577 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..732c5dc1fe503c74192cc93f29fc6e62513e2941 GIT binary patch literal 2035 zcmah~&yUka6rOP=juW!W?otVgD6$ZsZlxr&oZ2FU3Y4OjRkeUBB2|OqnRvZxd(4aj zB+d5F<%+5v+tco`|B`#`iGQJ0yZy%5L3rbM>&Q|$4QrF6N!j4|CmEmsa`e^oR^>lNv*iBqDD_Ka{IlJ%v@=MQ8N zh@c4kNM=$8MXWMEQBfTFL#6#hMnO3CjT-4t`XV-kjt3*FWV)?4aDZ&A({lBGW{Mz5 zs@-?BRvMEZq`{8lMi!&IP%1H8MiGy*q2hs#3=~d>*uu?dkZTo6W2crL$uz$cYZ(?w zk1?_K;K{d7`0cOm-u{|zKHk{4zi|(sS0lh=^=q{a7bG2|pZNFyc_UKQvU{kb$?J#`{EA?Y5WV^oaPRW!C_kD6; zfon{ze;H@O4>G?CzdF`0AFhbG?S7p3PZy)7-IkB%L^B^+GtWarWJ~b8!v)_hcr_i@4ZYcS@&wt)#=>i9UrR{`#%=*hp=9;Z~3Zk;+8SyQ9L9 z#R!v1_MQbDG#&XempYzJ=i@?}f+Gssz`Zvcc4YRvm&5}*+)~-~?x#Hy7qXKF;Z6`q z)63($hj12BXF<|aIIfDm=?!dbdg&3Z_>4i_d|b9}B;Ze|(){LG>$8oMODC835J)cA z(Y=u<5X0O=XRbcEw4sjSynq!H9U%yS`iI>&mI9N`1Er;3z~{GZ!ET zbE$XeGDaP=jJ46Cf9M=CS6fs<%iQ^c@|BeWc5k~gV&sIK0a$F;{dY?cR?zFx~LAK^3 zyEm%^(w~RXADK9d{3@Ff%g!J$Qusm+17s?+EZ<_2q9{~E-$d5y%peeQURR*LIBbF7 zEG;IVY>u+*`1$^a7V^)on00?%HL7xS#6JIunY2N`&OG%VAuenOG%8o3L=8|^#yqtd zAO%+1^D{UuE|)XKM@SMLs%#iXlXEkMFMM0vh@G|Yg(%ygEDjS~NE2>=E-vb0Bz0LA zaxjXtjhMC(*0!{3W`zwUG@Dg2aO6_CRAI;SF|XLvnw8YbA}=-ywNr|+A&@q4VzjLX zC9{R{U1qHw)oID+aDD_`a}|QLEv?wUtPXkTF$W{O|G2I@>!E+Sp{?rrdu4-jp+e5V ze7FPj^=T}W=YVT>k=R}NGL(5$?`Mf|`R!%=3KG*jR^%&(DV6JWcl+8#M-^>E$ac;AfbA1sjRgfw)j$v%8sIF4j fyOAm}O5{yjB+NMoR`aOmwCU07FxqxnHSg?yd5BL) literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05006e359890b399e346867cd06f45e4b9748003 GIT binary patch literal 1376 zcmZuxL5~|X6t*Wbli6&xU3#J|D!x?ONMxoJLI{KqV!LGvC0#8l)#kv+$#@fIXY8r% z={6yIp ze52?LJFt#co2+||hg(^gwZBKhD;fR@^`bvu4>aguds9_HFqNKiGfj2Lol-EZ>4d@Z zwJ(njj?&%zz1=U;!^eZcgTZG#&-xF!Ha2=x8ZHIR!EgJ3mG9kse_zUzU7F`ije+9D zXv4navZaVYE1Y>tbnXFs0J!)XEMC@H`nAQhGJoa6Dt>{_ zf+b!B%itXUa0{a)o`p*vvIQ@_v+xPT`p8Ewph#l-F4D6*2)o}A$R|h;ADQ<2r^e>? z44XZV+^%pmxZfR$Q@y2}3H)#e|qCBL{m2 z1o)GYb1#(~LkZ(fhrl>wrcy$alA0+olAKN%%LzXwx*FGR$5o!sU8IT0Bgm#sxn}PW zwT>IW?y<`>Slr*GLP)dTr^$3I6}hd~VEzRHOM>+K4o_dYMiCAe6w`owas3|Ch6`FZ zkYvDTY^0ih2zeH7JIXb&*Xh4KBRlKNz4}xX%+QP)nqGu1-nvhD&awvCf1_{f-lh$6 zIhinJ1E!RODPyB*qC7YZHUtI*HTDf%jn<`0n`3D=9f8y#u^W5O$IMZ&p48T_3u^tD zOr&7Ede~@Ty zSib~#aIaN zeC*+_=i%s;-vTRW-rqZZ7k6=l)eZ3NUA67H1<)FG6?A(GAjB6C;>z$`$FDokwC2YD zIhi@U6BAWGSTgu1Ss(v3F iP}hk|;(8qteN@P-%Gt*lG`$TX!jT8`fcQ4ZPU|0uos;4K literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bd8fd0b2985aa7c8b35552dec8654373f2989b3 GIT binary patch literal 2658 zcma);UvnHs5x{5m|E*4vBg+nfklMK7BuAmU1gb)CQ7I~x>^KOSvSVC63YOK*=x*)Z zonvOsvd()?rt*Y~M~XM;krzGzpJZNn%2yx>&^^176$4f5)y{19&h$?A{JQ6|)zz55 z_2)-lX0NXj@^=)@UoHqggynzfUt+4%W`Upz>! zf3kD^XX%}LdwaL`Zh|Rq7p$A;_DmO|&PwBb#-Hfi!PRw_2fYvXb-2}UpwXlu&*;)& z^q4lz30=AkRY~JA=SO7e*4MOi?4A(qJ@g-W@_%mW>TcRtvl zIy|v+8y7+oEZ(_c9Y{I{lA>-sf)yt4l$2sJDeB2^art`dVghlQSG?BhTJm%mFE&s0 zVyUGR&y zjH}#u8usKbL|%re|M=7Bp5juC@@ZCQ6H&8lv@P=1`%y0V=y%x`bX)w%jLX6XWi&g| z92f#X9>O9!af{cFM`f{(;#AZ>9R7Hu3eC4>S^g-Sa5b70vr$^qno9s<1enSUAyK3K zc~P=aOJvC>S$;H}9hpmQRL-!IT+K^;3D&GOp&>y;W5=U0U4uTR5xwBZZ-G8XxK1yO z?-^_wgbp@9PW|8vIKT0=)3}ZI46*UGd+as-355)G?h@@Ip3DXK_L+x>Hsr)v{?9o& zco-f>4Q&YXUxSer!H@1h0?InkQ=ZH+Jw-V1**XnCT0!$_CS)d$5*v|(6%xWL6yylJkxCD2T1foBpl1RlBz#-;L9{y{p=uHn9%izJDaHjW#u3VR=(+J8 zilQC_#>Z zP@O_RC45Hz`1vwIP?!s-IFu|l0F(gWh?w`>UH0yi`U8*X;OGe@_-mr$kXfi;R>T5? zh>{27K4GCpJo3y1+`_x$J)(QZG2*w3^-2Rz4FS777CrZuE6vJ%;+>m%LBZU*EG| zKx?gyL2vPgk1?t2X3lF&L4c#n#AI%F67jpzhVn|sRafzcah$-_BCF1L?N~iX)I7(7 z;b2~tEvkB$+y-;9Z7j>bg5_y#0 z#g}0Thaz}ou>)gh-K<(y(%DgJ(;`-4E_05~%d0r^Z5-rdb}$pS#SxEd(?_pT$VvrO zRSs}e(3W|-{?oB&^yTc?IG-}@)_(%?)rZiKRfl%JuYxd!q=A$<)qEZMebmrRsI+HV z>Ulx|UKYb{N0h`Unzz0=V6KZ&4bT5oL56MU z%^Ux9=-HnXdkos0XphvFKHXZ@M-aJ*4Dr)JCqy7fOnixd`b-b%%WiuLM`?-$DNXHW ww}lPMhIRN{WbqW0wm3yUsRWys{39%k3iT0dTDhQq%U5eiLH*-z${Qv*} literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py new file mode 100644 index 0000000..cf52f8d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py @@ -0,0 +1,37 @@ +"""Metadata generation logic for source distributions. +""" + +import os + +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.build_env import BuildEnvironment + from pip._vendor.pep517.wrappers import Pep517HookCaller + + +def generate_metadata(build_env, backend): + # type: (BuildEnvironment, Pep517HookCaller) -> str + """Generate metadata using mechanisms described in PEP 517. + + Returns the generated metadata directory. + """ + metadata_tmpdir = TempDirectory( + kind="modern-metadata", globally_managed=True + ) + + metadata_dir = metadata_tmpdir.path + + with build_env: + # Note that Pep517HookCaller implements a fallback for + # prepare_metadata_for_build_wheel, so we don't have to + # consider the possibility that this hook doesn't exist. + runner = runner_with_spinner_message("Preparing wheel metadata") + with backend.subprocess_runner(runner): + distinfo_dir = backend.prepare_metadata_for_build_wheel( + metadata_dir + ) + + return os.path.join(metadata_dir, distinfo_dir) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py new file mode 100644 index 0000000..14762ae --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py @@ -0,0 +1,77 @@ +"""Metadata generation logic for legacy source distributions. +""" + +import logging +import os + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.build_env import BuildEnvironment + +logger = logging.getLogger(__name__) + + +def _find_egg_info(directory): + # type: (str) -> str + """Find an .egg-info subdirectory in `directory`. + """ + filenames = [ + f for f in os.listdir(directory) if f.endswith(".egg-info") + ] + + if not filenames: + raise InstallationError( + "No .egg-info directory found in {}".format(directory) + ) + + if len(filenames) > 1: + raise InstallationError( + "More than one .egg-info directory found in {}".format( + directory + ) + ) + + return os.path.join(directory, filenames[0]) + + +def generate_metadata( + build_env, # type: BuildEnvironment + setup_py_path, # type: str + source_dir, # type: str + isolated, # type: bool + details, # type: str +): + # type: (...) -> str + """Generate metadata using setup.py-based defacto mechanisms. + + Returns the generated metadata directory. + """ + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + setup_py_path, details, + ) + + egg_info_dir = TempDirectory( + kind="pip-egg-info", globally_managed=True + ).path + + args = make_setuptools_egg_info_args( + setup_py_path, + egg_info_dir=egg_info_dir, + no_user_config=isolated, + ) + + with build_env: + call_subprocess( + args, + cwd=source_dir, + command_desc='python setup.py egg_info', + ) + + # Return the .egg-info directory. + return _find_egg_info(egg_info_dir) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py new file mode 100644 index 0000000..0c28c49 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py @@ -0,0 +1,46 @@ +import logging +import os + +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + from pip._vendor.pep517.wrappers import Pep517HookCaller + +logger = logging.getLogger(__name__) + + +def build_wheel_pep517( + name, # type: str + backend, # type: Pep517HookCaller + metadata_directory, # type: str + build_options, # type: List[str] + tempd, # type: str +): + # type: (...) -> Optional[str] + """Build one InstallRequirement using the PEP 517 build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + assert metadata_directory is not None + if build_options: + # PEP 517 does not support --build-options + logger.error('Cannot build wheel for %s using PEP 517 when ' + '--build-option is present', name) + return None + try: + logger.debug('Destination directory: %s', tempd) + + runner = runner_with_spinner_message( + 'Building wheel for {} (PEP 517)'.format(name) + ) + with backend.subprocess_runner(runner): + wheel_name = backend.build_wheel( + tempd, + metadata_directory=metadata_directory, + ) + except Exception: + logger.error('Failed building wheel for %s', name) + return None + return os.path.join(tempd, wheel_name) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py new file mode 100644 index 0000000..37dc876 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py @@ -0,0 +1,115 @@ +import logging +import os.path + +from pip._internal.cli.spinners import open_spinner +from pip._internal.utils.setuptools_build import ( + make_setuptools_bdist_wheel_args, +) +from pip._internal.utils.subprocess import ( + LOG_DIVIDER, + call_subprocess, + format_command_args, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Text + +logger = logging.getLogger(__name__) + + +def format_command_result( + command_args, # type: List[str] + command_output, # type: Text +): + # type: (...) -> str + """Format command information for logging.""" + command_desc = format_command_args(command_args) + text = 'Command arguments: {}\n'.format(command_desc) + + if not command_output: + text += 'Command output: None' + elif logger.getEffectiveLevel() > logging.DEBUG: + text += 'Command output: [use --verbose to show]' + else: + if not command_output.endswith('\n'): + command_output += '\n' + text += 'Command output:\n{}{}'.format(command_output, LOG_DIVIDER) + + return text + + +def get_legacy_build_wheel_path( + names, # type: List[str] + temp_dir, # type: str + name, # type: str + command_args, # type: List[str] + command_output, # type: Text +): + # type: (...) -> Optional[str] + """Return the path to the wheel in the temporary build directory.""" + # Sort for determinism. + names = sorted(names) + if not names: + msg = ( + 'Legacy build of wheel for {!r} created no files.\n' + ).format(name) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + return None + + if len(names) > 1: + msg = ( + 'Legacy build of wheel for {!r} created more than one file.\n' + 'Filenames (choosing first): {}\n' + ).format(name, names) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + + return os.path.join(temp_dir, names[0]) + + +def build_wheel_legacy( + name, # type: str + setup_py_path, # type: str + source_dir, # type: str + global_options, # type: List[str] + build_options, # type: List[str] + tempd, # type: str +): + # type: (...) -> Optional[str] + """Build one unpacked package using the "legacy" build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + wheel_args = make_setuptools_bdist_wheel_args( + setup_py_path, + global_options=global_options, + build_options=build_options, + destination_dir=tempd, + ) + + spin_message = 'Building wheel for {} (setup.py)'.format(name) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + + try: + output = call_subprocess( + wheel_args, + cwd=source_dir, + spinner=spinner, + ) + except Exception: + spinner.finish("error") + logger.error('Failed building wheel for %s', name) + return None + + names = os.listdir(tempd) + wheel_path = get_legacy_build_wheel_path( + names=names, + temp_dir=tempd, + name=name, + command_args=wheel_args, + command_output=output, + ) + return wheel_path diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/check.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..5714915 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,158 @@ +"""Validation of dependencies of packages +""" + +import logging +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.distributions import ( + make_distribution_for_install_requirement, +) +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from pip._internal.req.req_install import InstallRequirement + from typing import ( + Any, Callable, Dict, Optional, Set, Tuple, List + ) + + # Shorthands + PackageSet = Dict[str, 'PackageDetails'] + Missing = Tuple[str, Any] + Conflicting = Tuple[str, str, Any] + + MissingDict = Dict[str, List[Missing]] + ConflictingDict = Dict[str, List[Conflicting]] + CheckResult = Tuple[MissingDict, ConflictingDict] + ConflictDetails = Tuple[PackageSet, CheckResult] + +PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) + + +def create_package_set_from_installed(**kwargs): + # type: (**Any) -> Tuple[PackageSet, bool] + """Converts a list of distributions into a PackageSet. + """ + # Default to using all packages installed on the system + if kwargs == {}: + kwargs = {"local_only": False, "skip": ()} + + package_set = {} + problems = False + for dist in get_installed_distributions(**kwargs): + name = canonicalize_name(dist.project_name) + try: + package_set[name] = PackageDetails(dist.version, dist.requires()) + except RequirementParseError as e: + # Don't crash on broken metadata + logger.warning("Error parsing requirements for %s: %s", name, e) + problems = True + return package_set, problems + + +def check_package_set(package_set, should_ignore=None): + # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + + missing = {} + conflicting = {} + + for package_name in package_set: + # Info about dependencies of package_name + missing_deps = set() # type: Set[Missing] + conflicting_deps = set() # type: Set[Conflicting] + + if should_ignore and should_ignore(package_name): + continue + + for req in package_set[package_name].requires: + name = canonicalize_name(req.project_name) # type: str + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate() + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version # type: str + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install): + # type: (List[InstallRequirement]) -> ConflictDetails + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set, _ = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ) + ) + + +def _simulate_installation_of(to_install, package_set): + # type: (List[InstallRequirement], PackageSet) -> Set[str] + """Computes the version of packages after installing to_install. + """ + + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + abstract_dist = make_distribution_for_install_requirement(inst_req) + dist = abstract_dist.get_pkg_resources_distribution() + + assert dist is not None + name = canonicalize_name(dist.key) + package_set[name] = PackageDetails(dist.version, dist.requires()) + + installed.add(name) + + return installed + + +def _create_whitelist(would_be_installed, package_set): + # type: (Set[str], PackageSet) -> Set[str] + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].requires: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/freeze.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..ddb9cb2 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,272 @@ +from __future__ import absolute_import + +import collections +import logging +import os + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.direct_url_helpers import ( + direct_url_as_pep440_direct_reference, + dist_get_direct_url, +) +from pip._internal.utils.misc import ( + dist_is_editable, + get_installed_distributions, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Iterator, Optional, List, Container, Set, Dict, Tuple, Iterable, Union + ) + from pip._internal.cache import WheelCache + from pip._vendor.pkg_resources import ( + Distribution, Requirement + ) + + RequirementInfo = Tuple[Optional[Union[str, Requirement]], bool, List[str]] + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, # type: Optional[List[str]] + find_links=None, # type: Optional[List[str]] + local_only=False, # type: bool + user_only=False, # type: bool + paths=None, # type: Optional[List[str]] + isolated=False, # type: bool + wheel_cache=None, # type: Optional[WheelCache] + exclude_editable=False, # type: bool + skip=() # type: Container[str] +): + # type: (...) -> Iterator[str] + find_links = find_links or [] + + for link in find_links: + yield '-f {}'.format(link) + installations = {} # type: Dict[str, FrozenRequirement] + + for dist in get_installed_distributions( + local_only=local_only, + skip=(), + user_only=user_only, + paths=paths + ): + try: + req = FrozenRequirement.from_dist(dist) + except RequirementParseError as exc: + # We include dist rather than dist.project_name because the + # dist string includes more information, like the version and + # location. We also include the exception message to aid + # troubleshooting. + logger.warning( + 'Could not generate requirement for distribution %r: %s', + dist, exc + ) + continue + if exclude_editable and req.editable: + continue + installations[req.canonical_name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() # type: Set[str] + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files = collections.defaultdict(list) # type: Dict[str, List[str]] + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + line.startswith(( + '-r', '--requirement', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url', + '--use-feature'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = install_req_from_editable( + line, + isolated=isolated, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + else: + line_req_canonical_name = canonicalize_name( + line_req.name) + if line_req_canonical_name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but " + "package %r is not installed", + req_file_path, + COMMENT_RE.sub('', line).strip(), + line_req.name + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[ + line_req_canonical_name]).rstrip() + del installations[line_req_canonical_name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in six.iteritems(req_files): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if installation.canonical_name not in skip: + yield str(installation).rstrip() + + +def get_requirement_info(dist): + # type: (Distribution) -> RequirementInfo + """ + Compute and return values (req, editable, comments) for use in + FrozenRequirement.from_dist(). + """ + if not dist_is_editable(dist): + return (None, False, []) + + location = os.path.normcase(os.path.abspath(dist.location)) + + from pip._internal.vcs import vcs, RemoteNotFoundError + vcs_backend = vcs.get_backend_for_dir(location) + + if vcs_backend is None: + req = dist.as_requirement() + logger.debug( + 'No VCS found for editable requirement "%s" in: %r', req, + location, + ) + comments = [ + '# Editable install with no version control ({})'.format(req) + ] + return (location, True, comments) + + try: + req = vcs_backend.get_src_requirement(location, dist.project_name) + except RemoteNotFoundError: + req = dist.as_requirement() + comments = [ + '# Editable {} install with no remote ({})'.format( + type(vcs_backend).__name__, req, + ) + ] + return (location, True, comments) + + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + vcs_backend.name, + ) + return (None, True, []) + + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + else: + if req is not None: + return (req, True, []) + + logger.warning( + 'Could not determine repository location of %s', location + ) + comments = ['## !! Could not determine repository location'] + + return (None, False, comments) + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None + self.name = name + self.canonical_name = canonicalize_name(name) + self.req = req + self.editable = editable + self.comments = comments + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> FrozenRequirement + # TODO `get_requirement_info` is taking care of editable requirements. + # TODO This should be refactored when we will add detection of + # editable that provide .dist-info metadata. + req, editable, comments = get_requirement_info(dist) + if req is None and not editable: + # if PEP 610 metadata is present, attempt to use it + direct_url = dist_get_direct_url(dist) + if direct_url: + req = direct_url_as_pep440_direct_reference( + direct_url, dist.project_name + ) + comments = [] + if req is None: + # name==version requirement + req = dist.as_requirement() + + return cls(dist.project_name, req, editable, comments=comments) + + def __str__(self): + # type: () -> str + req = self.req + if self.editable: + req = '-e {}'.format(req) + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py new file mode 100644 index 0000000..24d6a5d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py @@ -0,0 +1,2 @@ +"""For modules related to installing packages. +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a33627f9bd342dd24a0c9bb10ea8203b804efec GIT binary patch literal 296 zcmYk1u}T9$5Qg{C7y@~RYXja*kxCFr5iD)Q#%39Bhs&DH&bm7h<9k^62)1Y%pXull)lm6a8=nt!0 zZXPIiVW<-j1SN<##AQz6z}I7L=_Q_HZ9enMPSR=T9o8*-Nw1xESy1+qzJq9A9ztA? zh2IUAeTyGGSuE}^zMgu<-!GIl z!FQF-CuJshOrO=1XB1XUzN`yI?(vgC3SPqDS&O*M2-*NfAI2UG)qv3GHDKFx-nb6Z zZqsd?1~t9LZM?=O?#td!q4r*&S9cxM1h4KujNgP!pLj1J{(D4xANlA7aKa}aBlYkj z?J zd=*B}w1_T%owugb_a4=p7d+p1E>RR62^oE$rd_j>GqFq=7A=&@jJHgm8+N$BwW1Pd zf*%#R=~j{+70->o5+yYwaBbYG&=!3!`;jcB9n%HIb1IEra5&3WwRElUK}-kI(jYA_ za%9Dh6=M*_=c(Pcy$Y_@xJvcP1cee%dz6^|(vpiQKQT9JZV{sdD^bggVp2$&X(89f zxApWJk~d+;zkiLNK>8}qRw++&!ATl_C9(~aaVBW|ES#88Vt{@u;e>r5jTcrz%>{$_5^E+>^J11A# zCu;gR)%%+=3NW^Xfw8=17u&11>|PSL?2_O!cri7jpF5zbT@bz-xI?GyuJ7!+Lof9H E0}EQ8-2eap literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e7031fb6284419cc3d1ec75fe9268d19641f001 GIT binary patch literal 3304 zcmZu!&2JmW6`xry$tA@veNohh?M>h0xy=GygMzxetwh3xpsC5loB_J6OLB$I$PBQ-F6NG^3(Z#7f2t zOHtV=>wYn`qKZ?|{Zd$s7Mum$FNd|L?$mYP3L8<=Y3hC@Y(S%5}$H@#}0b5XFugs4AWjZ6MEXK z{vGUrcDh(D_=jFRpe{=i0qo+(b9x>zmM|Es1~H+raKmJvI2!XIqb>>5+*ag0qptpf zNRp7d(~YY91nr@hcn7vz;AdP~^wbB793--odMw|Y$!U1-60ucS_^&_oKjW0~zVd=M zNMhpkA13}#9Rmgv+CTQT;k8XqQ_6yTI6V`T3m2Sx1ZL~!>^6-*?}x#@Vh@w}N4=l+ zc_8R^>iN&W)V!YtX&<&CC<6ob6HuqI!uwN)^+OFLfQhtR5DbLtZm9#RlOp5%#&u@t zT}keM;b2+pMbvd=)petU3`5ngx$g1M3-cMa2D4?WfjcM0s3nS-mEjC!t*=mKW{s?n z6|+iKBIb*Q(V{4k;<%huC-?%7mV|YIF7IQMEnIE^UCI`-8Y#UnF#0tl4-=k?0!DG` z9mIco7h(N80s6-5pXX=wjw)Geg88?iI$j`U=xd$x>xi`ndg8W;MD6S!xo%Pa5Z>T_$fnyoxU zzqvDN>)kG9?Fk;OWozRlvM>SLvfXSoTLT+)Fns0s_t17p4J=m&xplFetzY0T@c@lB zw4VE;cGe!Rz#ccXzSV3~YxySA{89VIFq3VJ*Ys=$bZ=hZtTQntSnK~2%&kwrvSK6a zT)>!?GqVn8e61sVO-wU_Shs%@c70Alja&y8s0d!!SBwWo3tR%7k6X{;yPgj z@`d>fQr!8{G!(Y;@t!yp-KuO1!erkI-9#rJE}K^wZZ?pMy9Yr?70jink|{(yqX)sM ztR7O2Kn8?-Db2$K=IcXFU1-czAu1bjl5>6%9|QwgNzeQwNb7iCvkZ8e-lm+TD`n+|Qg@a6r~oMA<}|M@bMfb!=8vd#?PJOEJrW2twAz#OUD?pO0@-z%Y7{rvzG8|Gv&H@{A zb;mO0T3O_R1*!6oLS2E=RF=F{IYO2h4Ym7B4##3eEq=lPh}|3PCU8t;+T4%dQ@yto zxvhu-i!5<{waU7Nq??|(TALN{1gZ~8q{Zm5z6fMxU){_u+)Q$P7^|Cz5|<}K=IiPq z_l|5Tc|y4lJ66{_ds}_R5>ObZm|?dRTe}K6RaQXY>Y_s816i8}pSB0vR{`*0(hHFOKs2SCA6+bp=WedXyws5szyfRGVKW5P~OyDitfN>MI zURi}J?p=fZ3clKXTb5myBtBfRWd$BUaSpmgn3W9zuo{XWx`|!nTDj(tvUQ!%3>PZQ z!0ZA4uc(niwa_a458>aa A`2YX_ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6274818f13756fb733c30c03965946899db96701 GIT binary patch literal 21288 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 P&iI`1BD~9o+bRDSp5)b( literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py new file mode 100644 index 0000000..a668a61 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py @@ -0,0 +1,52 @@ +"""Legacy editable installation process, i.e. `setup.py develop`. +""" +import logging + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.setuptools_build import make_setuptools_develop_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + + from pip._internal.build_env import BuildEnvironment + + +logger = logging.getLogger(__name__) + + +def install_editable( + install_options, # type: List[str] + global_options, # type: Sequence[str] + prefix, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool + name, # type: str + setup_py_path, # type: str + isolated, # type: bool + build_env, # type: BuildEnvironment + unpacked_source_directory, # type: str +): + # type: (...) -> None + """Install a package in editable mode. Most arguments are pass-through + to setuptools. + """ + logger.info('Running setup.py develop for %s', name) + + args = make_setuptools_develop_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + no_user_config=isolated, + prefix=prefix, + home=home, + use_user_site=use_user_site, + ) + + with indent_log(): + with build_env: + call_subprocess( + args, + cwd=unpacked_source_directory, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py new file mode 100644 index 0000000..87227d5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py @@ -0,0 +1,130 @@ +"""Legacy installation process, i.e. `setup.py install`. +""" + +import logging +import os +import sys +from distutils.util import change_root + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.setuptools_build import make_setuptools_install_args +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + + from pip._internal.build_env import BuildEnvironment + from pip._internal.models.scheme import Scheme + + +logger = logging.getLogger(__name__) + + +class LegacyInstallFailure(Exception): + def __init__(self): + # type: () -> None + self.parent = sys.exc_info() + + +def install( + install_options, # type: List[str] + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + use_user_site, # type: bool + pycompile, # type: bool + scheme, # type: Scheme + setup_py_path, # type: str + isolated, # type: bool + req_name, # type: str + build_env, # type: BuildEnvironment + unpacked_source_directory, # type: str + req_description, # type: str +): + # type: (...) -> bool + + header_dir = scheme.headers + + with TempDirectory(kind="record") as temp_dir: + try: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = make_setuptools_install_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + record_filename=record_filename, + root=root, + prefix=prefix, + header_dir=header_dir, + home=home, + use_user_site=use_user_site, + no_user_config=isolated, + pycompile=pycompile, + ) + + runner = runner_with_spinner_message( + "Running setup.py install for {}".format(req_name) + ) + with indent_log(), build_env: + runner( + cmd=install_args, + cwd=unpacked_source_directory, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + # Signal to the caller that we didn't install the new package + return False + + except Exception: + # Signal to the caller that we didn't install the new package + raise LegacyInstallFailure + + # At this point, we have successfully installed the requirement. + + # We intentionally do not use any encoding to read the file because + # setuptools writes the file using distutils.file_util.write_file, + # which does not specify an encoding. + with open(record_filename) as f: + record_lines = f.read().splitlines() + + def prepend_root(path): + # type: (str) -> str + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + for line in record_lines: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + message = ( + "{} did not indicate that it installed an " + ".egg-info directory. Only setup.py projects " + "generating .egg-info directories are supported." + ).format(req_description) + raise InstallationError(message) + + new_lines = [] + for line in record_lines: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath(prepend_root(filename), egg_info_dir) + ) + new_lines.sort() + ensure_dir(egg_info_dir) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py new file mode 100644 index 0000000..e91b1b8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py @@ -0,0 +1,861 @@ +"""Support for installing and building the "wheel" binary package format. +""" + +from __future__ import absolute_import + +import collections +import compileall +import contextlib +import csv +import importlib +import logging +import os.path +import re +import shutil +import sys +import warnings +from base64 import urlsafe_b64encode +from itertools import chain, starmap +from zipfile import ZipFile + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.distlib.util import get_export_entry +from pip._vendor.six import ( + PY2, + ensure_str, + ensure_text, + itervalues, + reraise, + text_type, +) +from pip._vendor.six.moves import filterfalse, map + +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_major_minor_version +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.models.scheme import SCHEME_KEYS +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import ( + captured_stdout, + ensure_dir, + hash_file, + partition, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import ( + current_umask, + is_within_directory, + set_extracted_file_to_default_mode_plus_executable, + zip_item_is_executable, +) +from pip._internal.utils.wheel import ( + parse_wheel, + pkg_resources_distribution_for_wheel, +) + +# Use the custom cast function at runtime to make cast work, +# and import typing.cast when performing pre-commit and type +# checks +if not MYPY_CHECK_RUNNING: + from pip._internal.utils.typing import cast +else: + from email.message import Message + from typing import ( + Any, + Callable, + Dict, + IO, + Iterable, + Iterator, + List, + NewType, + Optional, + Protocol, + Sequence, + Set, + Tuple, + Union, + cast, + ) + from zipfile import ZipInfo + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.models.scheme import Scheme + from pip._internal.utils.filesystem import NamedTemporaryFileResult + + RecordPath = NewType('RecordPath', text_type) + InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] + + class File(Protocol): + src_record_path = None # type: RecordPath + dest_path = None # type: text_type + changed = None # type: bool + + def save(self): + # type: () -> None + pass + + +logger = logging.getLogger(__name__) + + +def rehash(path, blocksize=1 << 20): + # type: (text_type, int) -> Tuple[str, str] + """Return (encoded_digest, length) for path using hashlib.sha256()""" + h, length = hash_file(path, blocksize) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + # unicode/str python2 issues + return (digest, str(length)) # type: ignore + + +def csv_io_kwargs(mode): + # type: (str) -> Dict[str, Any] + """Return keyword arguments to properly open a CSV file + in the given mode. + """ + if PY2: + return {'mode': '{}b'.format(mode)} + else: + return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} + + +def fix_script(path): + # type: (text_type) -> bool + """Replace #!python with #!/path/to/python + Return True if file was changed. + """ + # XXX RECORD hashes will need to be updated + assert os.path.isfile(path) + + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + + +def wheel_root_is_purelib(metadata): + # type: (Message) -> bool + return metadata.get("Root-Is-Purelib", "").lower() == "true" + + +def get_entrypoints(distribution): + # type: (Distribution) -> Tuple[Dict[str, str], Dict[str, str]] + # get the entry points and then the script names + try: + console = distribution.get_entry_map('console_scripts') + gui = distribution.get_entry_map('gui_scripts') + except KeyError: + # Our dict-based Distribution raises KeyError if entry_points.txt + # doesn't exist. + return {}, {} + + def _split_ep(s): + # type: (pkg_resources.EntryPoint) -> Tuple[str, str] + """get the string representation of EntryPoint, + remove space and split on '=' + """ + split_parts = str(s).replace(" ", "").split("=") + return split_parts[0], split_parts[1] + + # convert the EntryPoint objects into strings with module:function + console = dict(_split_ep(v) for v in console.values()) + gui = dict(_split_ep(v) for v in gui.values()) + return console, gui + + +def message_about_scripts_not_on_PATH(scripts): + # type: (Sequence[str]) -> Optional[str] + """Determine if any scripts are not on PATH and format a warning. + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir = collections.defaultdict(set) # type: Dict[str, Set[str]] + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(i).rstrip(os.sep) for i in + os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) + warn_for = { + parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(parent_dir) not in not_warn_dirs + } # type: Dict[str, Set[str]] + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, dir_scripts in warn_for.items(): + sorted_scripts = sorted(dir_scripts) # type: List[str] + if len(sorted_scripts) == 1: + start_text = "script {} is".format(sorted_scripts[0]) + else: + start_text = "scripts {} are".format( + ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] + ) + + msg_lines.append( + "The {} installed in '{}' which is not on PATH." + .format(start_text, parent_dir) + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Add a note if any directory starts with ~ + warn_for_tilde = any( + i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i + ) + if warn_for_tilde: + tilde_warning_msg = ( + "NOTE: The current PATH contains path(s) starting with `~`, " + "which may not be expanded by all applications." + ) + msg_lines.append(tilde_warning_msg) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def _normalized_outrows(outrows): + # type: (Iterable[InstalledCSVRow]) -> List[Tuple[str, str, str]] + """Normalize the given rows of a RECORD file. + + Items in each row are converted into str. Rows are then sorted to make + the value more predictable for tests. + + Each row is a 3-tuple (path, hash, size) and corresponds to a record of + a RECORD file (see PEP 376 and PEP 427 for details). For the rows + passed to this function, the size can be an integer as an int or string, + or the empty string. + """ + # Normally, there should only be one row per path, in which case the + # second and third elements don't come into play when sorting. + # However, in cases in the wild where a path might happen to occur twice, + # we don't want the sort operation to trigger an error (but still want + # determinism). Since the third element can be an int or string, we + # coerce each element to a string to avoid a TypeError in this case. + # For additional background, see-- + # https://github.com/pypa/pip/issues/5868 + return sorted( + (ensure_str(record_path, encoding='utf-8'), hash_, str(size)) + for record_path, hash_, size in outrows + ) + + +def _record_to_fs_path(record_path): + # type: (RecordPath) -> text_type + return record_path + + +def _fs_to_record_path(path, relative_to=None): + # type: (text_type, Optional[text_type]) -> RecordPath + if relative_to is not None: + # On Windows, do not handle relative paths if they belong to different + # logical disks + if os.path.splitdrive(path)[0].lower() == \ + os.path.splitdrive(relative_to)[0].lower(): + path = os.path.relpath(path, relative_to) + path = path.replace(os.path.sep, '/') + return cast('RecordPath', path) + + +def _parse_record_path(record_column): + # type: (str) -> RecordPath + p = ensure_text(record_column, encoding='utf-8') + return cast('RecordPath', p) + + +def get_csv_rows_for_installed( + old_csv_rows, # type: List[List[str]] + installed, # type: Dict[RecordPath, RecordPath] + changed, # type: Set[RecordPath] + generated, # type: List[str] + lib_dir, # type: str +): + # type: (...) -> List[InstalledCSVRow] + """ + :param installed: A map from archive RECORD path to installation RECORD + path. + """ + installed_rows = [] # type: List[InstalledCSVRow] + for row in old_csv_rows: + if len(row) > 3: + logger.warning('RECORD line has more than three elements: %s', row) + old_record_path = _parse_record_path(row[0]) + new_record_path = installed.pop(old_record_path, old_record_path) + if new_record_path in changed: + digest, length = rehash(_record_to_fs_path(new_record_path)) + else: + digest = row[1] if len(row) > 1 else '' + length = row[2] if len(row) > 2 else '' + installed_rows.append((new_record_path, digest, length)) + for f in generated: + path = _fs_to_record_path(f, lib_dir) + digest, length = rehash(f) + installed_rows.append((path, digest, length)) + for installed_record_path in itervalues(installed): + installed_rows.append((installed_record_path, '', '')) + return installed_rows + + +def get_console_script_specs(console): + # type: (Dict[str, str]) -> List[str] + """ + Given the mapping from entrypoint name to callable, return the relevant + console script specs. + """ + # Don't mutate caller's version + console = console.copy() + + scripts_to_generate = [] + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append('pip = ' + pip_script) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + scripts_to_generate.append( + 'pip{} = {}'.format(sys.version_info[0], pip_script) + ) + + scripts_to_generate.append( + 'pip{} = {}'.format(get_major_minor_version(), pip_script) + ) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append( + 'easy_install = ' + easy_install_script + ) + + scripts_to_generate.append( + 'easy_install-{} = {}'.format( + get_major_minor_version(), easy_install_script + ) + ) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console entry points specified in the wheel + scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) + + return scripts_to_generate + + +class ZipBackedFile(object): + def __init__(self, src_record_path, dest_path, zip_file): + # type: (RecordPath, text_type, ZipFile) -> None + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file + self.changed = False + + def _getinfo(self): + # type: () -> ZipInfo + if not PY2: + return self._zip_file.getinfo(self.src_record_path) + # Python 2 does not expose a way to detect a ZIP's encoding, but the + # wheel specification (PEP 427) explicitly mandates that paths should + # use UTF-8, so we assume it is true. + return self._zip_file.getinfo(self.src_record_path.encode("utf-8")) + + def save(self): + # type: () -> None + # directory creation is lazy and after file filtering + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + parent_dir = os.path.dirname(self.dest_path) + ensure_dir(parent_dir) + + # When we open the output file below, any existing file is truncated + # before we start writing the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(self.dest_path): + os.unlink(self.dest_path) + + zipinfo = self._getinfo() + + with self._zip_file.open(zipinfo) as f: + with open(self.dest_path, "wb") as dest: + shutil.copyfileobj(f, dest) + + if zip_item_is_executable(zipinfo): + set_extracted_file_to_default_mode_plus_executable(self.dest_path) + + +class ScriptFile(object): + def __init__(self, file): + # type: (File) -> None + self._file = file + self.src_record_path = self._file.src_record_path + self.dest_path = self._file.dest_path + self.changed = False + + def save(self): + # type: () -> None + self._file.save() + self.changed = fix_script(self.dest_path) + + +class MissingCallableSuffix(InstallationError): + def __init__(self, entry_point): + # type: (str) -> None + super(MissingCallableSuffix, self).__init__( + "Invalid script entry point: {} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information.".format(entry_point) + ) + + +def _raise_for_invalid_entrypoint(specification): + # type: (str) -> None + entry = get_export_entry(specification) + if entry is not None and entry.suffix is None: + raise MissingCallableSuffix(str(entry)) + + +class PipScriptMaker(ScriptMaker): + def make(self, specification, options=None): + # type: (str, Dict[str, Any]) -> List[str] + _raise_for_invalid_entrypoint(specification) + return super(PipScriptMaker, self).make(specification, options) + + +def _install_wheel( + name, # type: str + wheel_zip, # type: ZipFile + wheel_path, # type: str + scheme, # type: Scheme + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool +): + # type: (...) -> None + """Install a wheel. + + :param name: Name of the project to install + :param wheel_zip: open ZipFile for wheel being installed + :param scheme: Distutils scheme dictating the install directories + :param req_description: String used in place of the requirement, for + logging + :param pycompile: Whether to byte-compile installed Python files + :param warn_script_location: Whether to check that scripts are installed + into a directory on PATH + :raises UnsupportedWheel: + * when the directory holds an unpacked wheel with incompatible + Wheel-Version + * when the .dist-info dir does not match the wheel + """ + info_dir, metadata = parse_wheel(wheel_zip, name) + + if wheel_root_is_purelib(metadata): + lib_dir = scheme.purelib + else: + lib_dir = scheme.platlib + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} # type: Dict[RecordPath, RecordPath] + changed = set() # type: Set[RecordPath] + generated = [] # type: List[str] + + def record_installed(srcfile, destfile, modified=False): + # type: (RecordPath, text_type, bool) -> None + """Map archive RECORD paths to installation RECORD paths.""" + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath + if modified: + changed.add(_fs_to_record_path(destfile)) + + def all_paths(): + # type: () -> Iterable[RecordPath] + names = wheel_zip.namelist() + # If a flag is set, names may be unicode in Python 2. We convert to + # text explicitly so these are valid for lookup in RECORD. + decoded_names = map(ensure_text, names) + for name in decoded_names: + yield cast("RecordPath", name) + + def is_dir_path(path): + # type: (RecordPath) -> bool + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path, target_path): + # type: (text_type, text_type) -> None + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" + " outside the target directory {!r}" + ) + raise InstallationError( + message.format(wheel_path, target_path, dest_dir_path) + ) + + def root_scheme_file_maker(zip_file, dest): + # type: (ZipFile, text_type) -> Callable[[RecordPath], File] + def make_root_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + dest_path = os.path.join(dest, normed_path) + assert_no_path_traversal(dest, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_root_scheme_file + + def data_scheme_file_maker(zip_file, scheme): + # type: (ZipFile, Scheme) -> Callable[[RecordPath], File] + scheme_paths = {} + for key in SCHEME_KEYS: + encoded_key = ensure_text(key) + scheme_paths[encoded_key] = ensure_text( + getattr(scheme, key), encoding=sys.getfilesystemencoding() + ) + + def make_data_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + try: + _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) + except ValueError: + message = ( + "Unexpected file in {}: {!r}. .data directory contents" + " should be named like: '/'." + ).format(wheel_path, record_path) + raise InstallationError(message) + + try: + scheme_path = scheme_paths[scheme_key] + except KeyError: + valid_scheme_keys = ", ".join(sorted(scheme_paths)) + message = ( + "Unknown scheme key used in {}: {} (for file {!r}). .data" + " directory contents should be in subdirectories named" + " with a valid scheme key ({})" + ).format( + wheel_path, scheme_key, record_path, valid_scheme_keys + ) + raise InstallationError(message) + + dest_path = os.path.join(scheme_path, dest_subpath) + assert_no_path_traversal(scheme_path, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_data_scheme_file + + def is_data_scheme_path(path): + # type: (RecordPath) -> bool + return path.split("/", 1)[0].endswith(".data") + + paths = all_paths() + file_paths = filterfalse(is_dir_path, paths) + root_scheme_paths, data_scheme_paths = partition( + is_data_scheme_path, file_paths + ) + + make_root_scheme_file = root_scheme_file_maker( + wheel_zip, + ensure_text(lib_dir, encoding=sys.getfilesystemencoding()), + ) + files = map(make_root_scheme_file, root_scheme_paths) + + def is_script_scheme_path(path): + # type: (RecordPath) -> bool + parts = path.split("/", 2) + return ( + len(parts) > 2 and + parts[0].endswith(".data") and + parts[1] == "scripts" + ) + + other_scheme_paths, script_scheme_paths = partition( + is_script_scheme_path, data_scheme_paths + ) + + make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) + other_scheme_files = map(make_data_scheme_file, other_scheme_paths) + files = chain(files, other_scheme_files) + + # Get the defined entry points + distribution = pkg_resources_distribution_for_wheel( + wheel_zip, name, wheel_path + ) + console, gui = get_entrypoints(distribution) + + def is_entrypoint_wrapper(file): + # type: (File) -> bool + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + path = file.dest_path + name = os.path.basename(path) + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + script_scheme_files = map(make_data_scheme_file, script_scheme_paths) + script_scheme_files = filterfalse( + is_entrypoint_wrapper, script_scheme_files + ) + script_scheme_files = map(ScriptFile, script_scheme_files) + files = chain(files, script_scheme_files) + + for file in files: + file.save() + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths(): + # type: () -> Iterator[text_type] + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug + # issues related to permissions on existing files. + for installed_path in sorted(set(installed.values())): + full_installed_path = os.path.join(lib_dir, installed_path) + if not os.path.isfile(full_installed_path): + continue + if not full_installed_path.endswith('.py'): + continue + yield full_installed_path + + def pyc_output_path(path): + # type: (text_type) -> text_type + """Return the path the pyc file would have been written to. + """ + if PY2: + if sys.flags.optimize: + return path + 'o' + else: + return path + 'c' + else: + return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + for path in pyc_source_file_paths(): + # Python 2's `compileall.compile_file` requires a str in + # error cases, so we must convert to the native type. + path_arg = ensure_str( + path, encoding=sys.getfilesystemencoding() + ) + success = compileall.compile_file( + path_arg, force=True, quiet=True + ) + if success: + pyc_path = pyc_output_path(path) + assert os.path.exists(pyc_path) + pyc_record_path = cast( + "RecordPath", pyc_path.replace(os.path.sep, "/") + ) + record_installed(pyc_record_path, pyc_path) + logger.debug(stdout.getvalue()) + + maker = PipScriptMaker(None, scheme.scripts) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {''} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Generate the console and GUI entry points specified in the wheel + scripts_to_generate = get_console_script_specs(console) + + gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) + + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) + + generated.extend( + maker.make_multiple(gui_scripts_to_generate, {'gui': True}) + ) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + generated_file_mode = 0o666 & ~current_umask() + + @contextlib.contextmanager + def _generate_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + with adjacent_tmp_file(path, **kwargs) as f: + yield f + os.chmod(f.name, generated_file_mode) + replace(f.name, path) + + dest_info_dir = os.path.join(lib_dir, info_dir) + + # Record pip as the installer + installer_path = os.path.join(dest_info_dir, 'INSTALLER') + with _generate_file(installer_path) as installer_file: + installer_file.write(b'pip\n') + generated.append(installer_path) + + # Record the PEP 610 direct URL reference + if direct_url is not None: + direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME) + with _generate_file(direct_url_path) as direct_url_file: + direct_url_file.write(direct_url.to_json().encode("utf-8")) + generated.append(direct_url_path) + + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, 'REQUESTED') + with open(requested_path, "w"): + pass + generated.append(requested_path) + + record_text = distribution.get_metadata('RECORD') + record_rows = list(csv.reader(record_text.splitlines())) + + rows = get_csv_rows_for_installed( + record_rows, + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir) + + # Record details of all files installed + record_path = os.path.join(dest_info_dir, 'RECORD') + + with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: + # The type mypy infers for record_file is different for Python 3 + # (typing.IO[Any]) and Python 2 (typing.BinaryIO). We explicitly + # cast to typing.IO[str] as a workaround. + writer = csv.writer(cast('IO[str]', record_file)) + writer.writerows(_normalized_outrows(rows)) + + +@contextlib.contextmanager +def req_error_context(req_description): + # type: (str) -> Iterator[None] + try: + yield + except InstallationError as e: + message = "For req: {}. {}".format(req_description, e.args[0]) + reraise( + InstallationError, InstallationError(message), sys.exc_info()[2] + ) + + +def install_wheel( + name, # type: str + wheel_path, # type: str + scheme, # type: Scheme + req_description, # type: str + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool +): + # type: (...) -> None + with ZipFile(wheel_path, allowZip64=True) as z: + with req_error_context(req_description): + _install_wheel( + name=name, + wheel_zip=z, + wheel_path=wheel_path, + scheme=scheme, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=requested, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/prepare.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..a5455fc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,562 @@ +"""Prepares a distribution for installation +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import logging +import mimetypes +import os +import shutil + +from pip._vendor.six import PY2 + +from pip._internal.distributions import ( + make_distribution_for_install_requirement, +) +from pip._internal.distributions.installed import InstalledDistribution +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, + HashMismatch, + HashUnpinned, + InstallationError, + NetworkConnectionError, + PreviousBuildDirError, + VcsHashUnsupported, +) +from pip._internal.utils.filesystem import copy2_fixed +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, + hide_url, + path_to_display, + rmtree, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import unpack_file +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Callable, List, Optional, Tuple, + ) + + from mypy_extensions import TypedDict + + from pip._internal.distributions import AbstractDistribution + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.network.download import Downloader + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.utils.hashes import Hashes + + if PY2: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'ignore': Callable[[str, List[str]], List[str]], + 'symlinks': bool, + }, + total=False, + ) + else: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'copy_function': Callable[[str, str], None], + 'ignore': Callable[[str, List[str]], List[str]], + 'ignore_dangling_symlinks': bool, + 'symlinks': bool, + }, + total=False, + ) + +logger = logging.getLogger(__name__) + + +def _get_prepared_distribution( + req, # type: InstallRequirement + req_tracker, # type: RequirementTracker + finder, # type: PackageFinder + build_isolation # type: bool +): + # type: (...) -> AbstractDistribution + """Prepare a distribution for installation. + """ + abstract_dist = make_distribution_for_install_requirement(req) + with req_tracker.track(req): + abstract_dist.prepare_distribution_metadata(finder, build_isolation) + return abstract_dist + + +def unpack_vcs_link(link, location): + # type: (Link, str) -> None + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend is not None + vcs_backend.unpack(location, url=hide_url(link.url)) + + +class File(object): + def __init__(self, path, content_type): + # type: (str, str) -> None + self.path = path + self.content_type = content_type + + +def get_http_url( + link, # type: Link + downloader, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> File + temp_dir = TempDirectory(kind="unpack", globally_managed=True) + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir( + link, download_dir, hashes + ) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url( + link, downloader, temp_dir.path, hashes + ) + + return File(from_path, content_type) + + +def _copy2_ignoring_special_files(src, dest): + # type: (str, str) -> None + """Copying special files is not supported, but as a convenience to users + we skip errors copying them. This supports tools that may create e.g. + socket files in the project source directory. + """ + try: + copy2_fixed(src, dest) + except shutil.SpecialFileError as e: + # SpecialFileError may be raised due to either the source or + # destination. If the destination was the cause then we would actually + # care, but since the destination directory is deleted prior to + # copy we ignore all of them assuming it is caused by the source. + logger.warning( + "Ignoring special file error '%s' encountered copying %s to %s.", + str(e), + path_to_display(src), + path_to_display(dest), + ) + + +def _copy_source_tree(source, target): + # type: (str, str) -> None + target_abspath = os.path.abspath(target) + target_basename = os.path.basename(target_abspath) + target_dirname = os.path.dirname(target_abspath) + + def ignore(d, names): + # type: (str, List[str]) -> List[str] + skipped = [] # type: List[str] + if d == source: + # Pulling in those directories can potentially be very slow, + # exclude the following directories if they appear in the top + # level dir (and only it). + # See discussion at https://github.com/pypa/pip/pull/6770 + skipped += ['.tox', '.nox'] + if os.path.abspath(d) == target_dirname: + # Prevent an infinite recursion if the target is in source. + # This can happen when TMPDIR is set to ${PWD}/... + # and we copy PWD to TMPDIR. + skipped += [target_basename] + return skipped + + kwargs = dict(ignore=ignore, symlinks=True) # type: CopytreeKwargs + + if not PY2: + # Python 2 does not support copy_function, so we only ignore + # errors on special file copy in Python 3. + kwargs['copy_function'] = _copy2_ignoring_special_files + + shutil.copytree(source, target, **kwargs) + + +def get_file_url( + link, # type: Link + download_dir=None, # type: Optional[str] + hashes=None # type: Optional[Hashes] +): + # type: (...) -> File + """Get file and optionally check its hash. + """ + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir( + link, download_dir, hashes + ) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link.file_path + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(from_path) + + content_type = mimetypes.guess_type(from_path)[0] + + return File(from_path, content_type) + + +def unpack_url( + link, # type: Link + location, # type: str + downloader, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> Optional[File] + """Unpack link into location, downloading if required. + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if link.is_vcs: + unpack_vcs_link(link, location) + return None + + # If it's a url to a local directory + if link.is_existing_dir(): + if os.path.isdir(location): + rmtree(location) + _copy_source_tree(link.file_path, location) + return None + + # file urls + if link.is_file: + file = get_file_url(link, download_dir, hashes=hashes) + + # http urls + else: + file = get_http_url( + link, + downloader, + download_dir, + hashes=hashes, + ) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies, except wheels + if not link.is_wheel: + unpack_file(file.path, location, file.content_type) + + return file + + +def _download_http_url( + link, # type: Link + downloader, # type: Downloader + temp_dir, # type: str + hashes, # type: Optional[Hashes] +): + # type: (...) -> Tuple[str, str] + """Download link url into temp_dir using provided session""" + download = downloader(link) + + file_path = os.path.join(temp_dir, download.filename) + with open(file_path, 'wb') as content_file: + for chunk in download.chunks: + content_file.write(chunk) + + if hashes: + hashes.check_against_path(file_path) + + return file_path, download.response.headers.get('content-type', '') + + +def _check_download_dir(link, download_dir, hashes): + # type: (Link, str, Optional[Hashes]) -> Optional[str] + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + + if not os.path.exists(download_path): + return None + + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + + +class RequirementPreparer(object): + """Prepares a Requirement + """ + + def __init__( + self, + build_dir, # type: str + download_dir, # type: Optional[str] + src_dir, # type: str + wheel_download_dir, # type: Optional[str] + build_isolation, # type: bool + req_tracker, # type: RequirementTracker + downloader, # type: Downloader + finder, # type: PackageFinder + require_hashes, # type: bool + use_user_site, # type: bool + ): + # type: (...) -> None + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + self.downloader = downloader + self.finder = finder + + # Where still-packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = download_dir + + # Where still-packed .whl files should be written to. If None, they are + # written to the download_dir parameter. Separate to download_dir to + # permit only keeping wheel archives for pip wheel. + self.wheel_download_dir = wheel_download_dir + + # NOTE + # download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + + # Is build isolation allowed? + self.build_isolation = build_isolation + + # Should hash-checking be required? + self.require_hashes = require_hashes + + # Should install in user site-packages? + self.use_user_site = use_user_site + + @property + def _download_should_save(self): + # type: () -> bool + if not self.download_dir: + return False + + if os.path.exists(self.download_dir): + return True + + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '{}'" + .format(self.download_dir)) + + def _log_preparing_link(self, req): + # type: (InstallRequirement) -> None + """Log the way the link prepared.""" + if req.link.is_file: + path = req.link.file_path + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req.req or req) + + def _ensure_link_req_src_dir(self, req, download_dir, parallel_builds): + # type: (InstallRequirement, Optional[str], bool) -> None + """Ensure source_dir of a linked InstallRequirement.""" + # Since source_dir is only set for editable requirements. + if req.link.is_wheel: + # We don't need to unpack wheels, so no need for a source + # directory. + return + assert req.source_dir is None + # We always delete unpacked sdists after pip runs. + req.ensure_has_source_dir( + self.build_dir, + autodelete=True, + parallel_builds=parallel_builds, + ) + + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '{}' due to a" + "pre-existing build directory ({}). This is likely " + "due to a previous installation that failed . pip is " + "being responsible and not assuming it can delete this. " + "Please delete it and try again.".format(req, req.source_dir) + ) + + def _get_linked_req_hashes(self, req): + # type: (InstallRequirement) -> Hashes + # By the time this is called, the requirement's link should have + # been checked so we can tell what kind of requirements req is + # and raise some more informative errors than otherwise. + # (For example, we can raise VcsHashUnsupported for a VCS URL + # rather than HashMissing.) + if not self.require_hashes: + return req.hashes(trust_internet=True) + + # We could check these first 2 conditions inside unpack_url + # and save repetition of conditions, but then we would + # report less-useful error messages for unhashable + # requirements, complaining that there's no hash provided. + if req.link.is_vcs: + raise VcsHashUnsupported() + if req.link.is_existing_dir(): + raise DirectoryUrlHashUnsupported() + + # Unpinned packages are asking for trouble when a new version + # is uploaded. This isn't a security check, but it saves users + # a surprising hash mismatch in the future. + # file:/// URLs aren't pinnable, so don't complain about them + # not being pinned. + if req.original_link is None and not req.is_pinned: + raise HashUnpinned() + + # If known-good hashes are missing for this requirement, + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + return req.hashes(trust_internet=False) or MissingHashes() + + def prepare_linked_requirement(self, req, parallel_builds=False): + # type: (InstallRequirement, bool) -> AbstractDistribution + """Prepare a requirement to be obtained from req.link.""" + assert req.link + link = req.link + self._log_preparing_link(req) + if link.is_wheel and self.wheel_download_dir: + # Download wheels to a dedicated dir when doing `pip wheel`. + download_dir = self.wheel_download_dir + else: + download_dir = self.download_dir + + with indent_log(): + self._ensure_link_req_src_dir(req, download_dir, parallel_builds) + try: + local_file = unpack_url( + link, req.source_dir, self.downloader, download_dir, + hashes=self._get_linked_req_hashes(req) + ) + except NetworkConnectionError as exc: + raise InstallationError( + 'Could not install requirement {} because of HTTP ' + 'error {} for URL {}'.format(req, exc, link) + ) + + # For use in later processing, preserve the file path on the + # requirement. + if local_file: + req.local_file_path = local_file.path + + abstract_dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + + if download_dir: + if link.is_existing_dir(): + logger.info('Link is a directory, ignoring download_dir') + elif local_file: + download_location = os.path.join( + download_dir, link.filename + ) + if not os.path.exists(download_location): + shutil.copy(local_file.path, download_location) + download_path = display_path(download_location) + logger.info('Saved %s', download_path) + + if self._download_should_save: + # Make a .zip of the source_dir we already created. + if link.is_vcs: + req.archive(self.download_dir) + return abstract_dist + + def prepare_editable_requirement( + self, + req, # type: InstallRequirement + ): + # type: (...) -> AbstractDistribution + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if self.require_hashes: + raise InstallationError( + 'The editable requirement {} cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.'.format(req) + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(not self._download_should_save) + + abstract_dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + + if self._download_should_save: + req.archive(self.download_dir) + req.check_if_exists(self.use_user_site) + + return abstract_dist + + def prepare_installed_requirement( + self, + req, # type: InstallRequirement + skip_reason # type: str + ): + # type: (...) -> AbstractDistribution + """Prepare an already-installed requirement + """ + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + "is set to {}".format(req.satisfied_by) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if self.require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.' + ) + abstract_dist = InstalledDistribution(req) + + return abstract_dist diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/pyproject.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..6b4faf7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,196 @@ +from __future__ import absolute_import + +import io +import os +import sys +from collections import namedtuple + +from pip._vendor import six, toml +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional, List + + +def _is_list_of_str(obj): + # type: (Any) -> bool + return ( + isinstance(obj, list) and + all(isinstance(item, six.string_types) for item in obj) + ) + + +def make_pyproject_path(unpacked_source_directory): + # type: (str) -> str + path = os.path.join(unpacked_source_directory, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(path, six.text_type): + path = path.encode(sys.getfilesystemencoding()) + + return path + + +BuildSystemDetails = namedtuple('BuildSystemDetails', [ + 'requires', 'backend', 'check', 'backend_path' +]) + + +def load_pyproject_toml( + use_pep517, # type: Optional[bool] + pyproject_toml, # type: str + setup_py, # type: str + req_name # type: str +): + # type: (...) -> Optional[BuildSystemDetails] + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + directory paths to import the backend from (backend-path), + relative to the project root. + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if has_pyproject: + with io.open(pyproject_toml, encoding="utf-8") as f: + pp_toml = toml.load(f) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 being non-None and falsey because that means + # the user explicitly requested --no-use-pep517. The value 0 as + # opposed to False can occur when the value is provided via an + # environment variable or config file option (due to the quirk of + # strtobool() returning an integer in pip's configuration code). + if has_pyproject and not has_setup: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format( + build_system["build-backend"] + ) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file. + elif use_pep517 is None: + use_pep517 = has_pyproject + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend that most closely emulates the + # traditional direct setup.py execution, and require wheel and + # a version of setuptools that supports that backend. + + build_system = { + "requires": ["setuptools>=40.8.0", "wheel"], + "build-backend": "setuptools.build_meta:__legacy__", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + error_template = ( + "{package} has a pyproject.toml file that does not comply " + "with PEP 518: {reason}" + ) + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise InstallationError( + error_template.format(package=req_name, reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + )) + ) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InstallationError(error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + )) + + # Each requirement must be valid as per PEP 508 + for requirement in requires: + try: + Requirement(requirement) + except InvalidRequirement: + raise InstallationError( + error_template.format( + package=req_name, + reason=( + "'build-system.requires' contains an invalid " + "requirement: {!r}".format(requirement) + ), + ) + ) + + backend = build_system.get("build-backend") + backend_path = build_system.get("backend-path", []) + check = [] # type: List[str] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend, or wheel + # (which is needed by the backend) in their requirements. So we + # make a note to check that those requirements are present once + # we have set up the environment. + # This is quite a lot of work to check for a very specific case. But + # the problem is, that case is potentially quite common - projects that + # adopted PEP 518 early for the ability to specify requirements to + # execute setup.py, but never considered needing to mention the build + # tools themselves. The original PEP 518 code had a similar check (but + # implemented in a different way). + backend = "setuptools.build_meta:__legacy__" + check = ["setuptools>=40.8.0", "wheel"] + + return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..8568d3f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import collections +import logging + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .req_file import parse_requirements +from .req_install import InstallRequirement +from .req_set import RequirementSet + +if MYPY_CHECK_RUNNING: + from typing import Iterator, List, Optional, Sequence, Tuple + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +class InstallationResult(object): + def __init__(self, name): + # type: (str) -> None + self.name = name + + def __repr__(self): + # type: () -> str + return "InstallationResult(name={!r})".format(self.name) + + +def _validate_requirements( + requirements, # type: List[InstallRequirement] +): + # type: (...) -> Iterator[Tuple[str, InstallRequirement]] + for req in requirements: + assert req.name, "invalid to-be-installed requirement: {}".format(req) + yield req.name, req + + +def install_given_reqs( + requirements, # type: List[InstallRequirement] + install_options, # type: List[str] + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + warn_script_location, # type: bool + use_user_site, # type: bool + pycompile, # type: bool +): + # type: (...) -> List[InstallationResult] + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join(to_install.keys()), + ) + + installed = [] + + with indent_log(): + for req_name, requirement in to_install.items(): + if requirement.should_reinstall: + logger.info('Attempting uninstall: %s', req_name) + with indent_log(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + else: + uninstalled_pathset = None + + try: + requirement.install( + install_options, + global_options, + root=root, + home=home, + prefix=prefix, + warn_script_location=warn_script_location, + use_user_site=use_user_site, + pycompile=pycompile, + ) + except Exception: + # if install did not succeed, rollback previous uninstall + if uninstalled_pathset and not requirement.install_succeeded: + uninstalled_pathset.rollback() + raise + else: + if uninstalled_pathset and requirement.install_succeeded: + uninstalled_pathset.commit() + + installed.append(InstallationResult(req_name)) + + return installed diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4a5db5a69b9ffd1581f2ebf93b6f570b033fb00 GIT binary patch literal 2533 zcmaJ?OOG5i5Vm`IW_o&h_LU_GdBB86$Ov{Ph!cn)P!b?K7L)`~TA)^M+cVpney#0Z zvYQ?bY{UU6z?Iu9H!hs{5u7;siiE^JKp+8Co|$C=h;F&;Dp%R%&sF8H({TxWU*G;c zyyOw`6EgFk4b02X^!yOx(2Xx6Rl|ytLzYDB(?U2kcwPLb~WLrc3@(y6i8fEB;D)%s)oSkTCZf>aX(J z_#uA$koqTHBdpE5dxUxXs8Yi);z{)6BX-ddt6n&!k87Vz?>uq*OeJ z(;^pY17vQTF`lU)$%nwLynXB4TfybmuU>vLxc=V8#A0VAG>w z8P+fWO~^4|(M7=v5d>#}`+pZFdK(xGFo{`LX6B>GMMO?x<%Oh{Qv%3&7^hO-o$^?w_a~ z56rqD(swFO{+Sta7 z8P}`UxN%4h>7j)+XXL8p0r~PrN zxBGO|`GBy7O>A;d1BAYKmdH;o0#^9nf|l8=G3x9;0v2t^Gpnjqc2%z$RkLzf^S(pL z2P8Xh6XorJ2l!VV@dIdg zPzm9Z!vUmacPrdQ6PACRC3(nDp0RG3VG09->&&vbXBOE%H{B7 z$B;vDkF!Cp?d?3yw0)cJNr81l*I~U>>Lt0AmkEQ&ojR>sNB-;Pk?iW7h@00zFHdEG zETf2Xz)(8^fY}5HbpyOhW2GBmQSgkJJnk)sQ@G*c%rG|#Nxd*k^35;_ro4Crw8W!G zx=79;!6Xuo1JN!(c;wD>VD{!arvB*mT=MB-M~C1nC_=R*x%x-m3e3N!p~+W)kmUu- zrZ%;x_lI4#Er%}A2KA_evOmw;wq==F3p9@YUtQO;T+nflS{E&|X73DE!a=D@0hLf- z=@L%>(M`M&h!A}mUW)ajmtwtCaU$2Jlat#y^n|(-rw;%C literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..468f785f150fcb54ee9bb72c1155757201e4b32b GIT binary patch literal 11306 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 a$Bj8--nciErz*cTPo(d^F;8*ofBp|QpYxgk literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96dd213dde9913e7f22c6a98ccd1032c66b6e773 GIT binary patch literal 13249 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/constructors.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..7a4641e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,486 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import logging +import os +import re + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements + +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.pyproject import make_pyproject_path +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS +from pip._internal.utils.misc import is_installable_dir, splitext +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Optional, Set, Tuple, Union, + ) + from pip._internal.req.req_file import ParsedRequirement + + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def is_archive_file(name): + # type: (str) -> bool + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False + + +def _strip_extras(path): + # type: (str) -> Tuple[str, Optional[str]] + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def convert_extras(extras): + # type: (Optional[str]) -> Set[str] + if not extras: + return set() + return Requirement("placeholder" + extras.lower()).extras + + +def parse_editable(editable_req): + # type: (str) -> Tuple[Optional[str], str, Set[str]] + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + msg = ( + 'File "setup.py" not found. Directory cannot be installed ' + 'in editable mode: {}'.format(os.path.abspath(url_no_extras)) + ) + pyproject_path = make_pyproject_path(url_no_extras) + if os.path.isfile(pyproject_path): + msg += ( + '\n(A "pyproject.toml" file was found, but editable ' + 'mode currently requires a setup.py based build.)' + ) + raise InstallationError(msg) + + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, set() + + for version_control in vcs: + if url.lower().startswith('{}:'.format(version_control)): + url = '{}+{}'.format(version_control, url) + break + + if '+' not in url: + raise InstallationError( + '{} is not a valid editable requirement. ' + 'It should either be a path to a local project or a VCS URL ' + '(beginning with svn+, git+, hg+, or bzr+).'.format(editable_req) + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + backends = ", ".join([bends.name + '+URL' for bends in vcs.backends]) + error_message = "For --editable={}, " \ + "only {} are currently supported".format( + editable_req, backends) + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '{}', please specify one " + "with #egg=your_package_name".format(editable_req) + ) + return package_name, url, set() + + +def deduce_helpful_msg(req): + # type: (str) -> str + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + msg = "" + if os.path.exists(req): + msg = " It does exist." + # Try to parse and check if it is a requirements file. + try: + with open(req, 'r') as fp: + # parse first line only + next(parse_requirements(fp.read())) + msg += ( + "The argument you provided " + "({}) appears to be a" + " requirements file. If that is the" + " case, use the '-r' flag to install" + " the packages specified within it." + ).format(req) + except RequirementParseError: + logger.debug( + "Cannot parse '%s' as requirements file", req, exc_info=True + ) + else: + msg += " File '{}' does not exist.".format(req) + return msg + + +class RequirementParts(object): + def __init__( + self, + requirement, # type: Optional[Requirement] + link, # type: Optional[Link] + markers, # type: Optional[Marker] + extras, # type: Set[str] + ): + self.requirement = requirement + self.link = link + self.markers = markers + self.extras = extras + + +def parse_req_from_editable(editable_req): + # type: (str) -> RequirementParts + name, url, extras_override = parse_editable(editable_req) + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(name)) + else: + req = None + + link = Link(url) + + return RequirementParts(req, link, None, extras_override) + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, # type: str + comes_from=None, # type: Optional[Union[InstallRequirement, str]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + constraint=False, # type: bool + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + + parts = parse_req_from_editable(editable_req) + + return InstallRequirement( + parts.requirement, + comes_from=comes_from, + user_supplied=user_supplied, + editable=True, + link=parts.link, + constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + extras=parts.extras, + ) + + +def _looks_like_path(name): + # type: (str) -> bool + """Checks whether the string "looks like" a path on the filesystem. + + This does not check whether the target actually exists, only judge from the + appearance. + + Returns true if any of the following conditions is true: + * a path separator is found (either os.path.sep or os.path.altsep); + * a dot is found (which represents the current directory). + """ + if os.path.sep in name: + return True + if os.path.altsep is not None and os.path.altsep in name: + return True + if name.startswith("."): + return True + return False + + +def _get_url_from_path(path, name): + # type: (str, str) -> Optional[str] + """ + First, it checks whether a provided path is an installable directory + (e.g. it has a setup.py). If it is, returns the path. + + If false, check if the path is an archive file (such as a .whl). + The function checks if the path is a file. If false, if the path has + an @, it will treat it as a PEP 440 URL requirement and return the path. + """ + if _looks_like_path(name) and os.path.isdir(path): + if is_installable_dir(path): + return path_to_url(path) + raise InstallationError( + "Directory {name!r} is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found.".format(**locals()) + ) + if not is_archive_file(path): + return None + if os.path.isfile(path): + return path_to_url(path) + urlreq_parts = name.split('@', 1) + if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): + # If the path contains '@' and the part before it does not look + # like a path, try to treat it as a PEP 440 URL req instead. + return None + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + return path_to_url(path) + + +def parse_req_from_line(name, line_source): + # type: (str, Optional[str]) -> RequirementParts + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers_as_string = name.split(marker_sep, 1) + markers_as_string = markers_as_string.strip() + if not markers_as_string: + markers = None + else: + markers = Marker(markers_as_string) + else: + markers = None + name = name.strip() + req_as_string = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras_as_string = None + + if is_url(name): + link = Link(name) + else: + p, extras_as_string = _strip_extras(path) + url = _get_url_from_path(p, name) + if url is not None: + link = Link(url) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req_as_string = "{wheel.name}=={wheel.version}".format(**locals()) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req_as_string = link.egg_fragment + + # a requirement specifier + else: + req_as_string = name + + extras = convert_extras(extras_as_string) + + def with_source(text): + # type: (str) -> str + if not line_source: + return text + return '{} (from {})'.format(text, line_source) + + if req_as_string is not None: + try: + req = Requirement(req_as_string) + except InvalidRequirement: + if os.path.sep in req_as_string: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req_as_string) + elif ('=' in req_as_string and + not any(op in req_as_string for op in operators)): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = '' + msg = with_source( + 'Invalid requirement: {!r}'.format(req_as_string) + ) + if add_msg: + msg += '\nHint: {}'.format(add_msg) + raise InstallationError(msg) + else: + # Deprecate extras after specifiers: "name>=1.0[extras]" + # This currently works by accident because _strip_extras() parses + # any extras in the end of the string and those are saved in + # RequirementParts + for spec in req.specifier: + spec_str = str(spec) + if spec_str.endswith(']'): + msg = "Extras after version '{}'.".format(spec_str) + replace = "moving the extras before version specifiers" + deprecated(msg, replacement=replace, gone_in="21.0") + else: + req = None + + return RequirementParts(req, link, markers, extras) + + +def install_req_from_line( + name, # type: str + comes_from=None, # type: Optional[Union[str, InstallRequirement]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + constraint=False, # type: bool + line_source=None, # type: Optional[str] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + + :param line_source: An optional string describing where the line is from, + for logging purposes in case of an error. + """ + parts = parse_req_from_line(name, line_source) + + return InstallRequirement( + parts.requirement, comes_from, link=parts.link, markers=parts.markers, + use_pep517=use_pep517, isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + constraint=constraint, + extras=parts.extras, + user_supplied=user_supplied, + ) + + +def install_req_from_req_string( + req_string, # type: str + comes_from=None, # type: Optional[InstallRequirement] + isolated=False, # type: bool + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + try: + req = Requirement(req_string) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(req_string)) + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if (req.url and comes_from and comes_from.link and + comes_from.link.netloc in domains_not_allowed): + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + "{} depends on {} ".format(comes_from.name, req) + ) + + return InstallRequirement( + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, + ) + + +def install_req_from_parsed_requirement( + parsed_req, # type: ParsedRequirement + isolated=False, # type: bool + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + if parsed_req.is_editable: + req = install_req_from_editable( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + constraint=parsed_req.constraint, + isolated=isolated, + user_supplied=user_supplied, + ) + + else: + req = install_req_from_line( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + isolated=isolated, + options=parsed_req.options, + constraint=parsed_req.constraint, + line_source=parsed_req.line_source, + user_supplied=user_supplied, + ) + return req diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_file.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..1050582 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,592 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.exceptions import ( + InstallationError, + RequirementsFileParseError, +) +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import ( + Any, Callable, Dict, Iterator, List, NoReturn, Optional, Text, Tuple, + ) + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.network.session import PipSession + + ReqFileLines = Iterator[Tuple[int, Text]] + + LineParser = Callable[[Text], Tuple[str, Values]] + + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s+)#.*$') + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') + +SUPPORTED_OPTIONS = [ + cmdoptions.index_url, + cmdoptions.extra_index_url, + cmdoptions.no_index, + cmdoptions.constraints, + cmdoptions.requirements, + cmdoptions.editable, + cmdoptions.find_links, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.prefer_binary, + cmdoptions.require_hashes, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.use_new_feature, +] # type: List[Callable[..., optparse.Option]] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] # type: List[Callable[..., optparse.Option]] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] + + +class ParsedRequirement(object): + def __init__( + self, + requirement, # type:str + is_editable, # type: bool + comes_from, # type: str + constraint, # type: bool + options=None, # type: Optional[Dict[str, Any]] + line_source=None, # type: Optional[str] + ): + # type: (...) -> None + self.requirement = requirement + self.is_editable = is_editable + self.comes_from = comes_from + self.options = options + self.constraint = constraint + self.line_source = line_source + + +class ParsedLine(object): + def __init__( + self, + filename, # type: str + lineno, # type: int + comes_from, # type: Optional[str] + args, # type: str + opts, # type: Values + constraint, # type: bool + ): + # type: (...) -> None + self.filename = filename + self.lineno = lineno + self.comes_from = comes_from + self.opts = opts + self.constraint = constraint + + if args: + self.is_requirement = True + self.is_editable = False + self.requirement = args + elif opts.editables: + self.is_requirement = True + self.is_editable = True + # We don't support multiple -e on one line + self.requirement = opts.editables[0] + else: + self.is_requirement = False + + +def parse_requirements( + filename, # type: str + session, # type: PipSession + finder=None, # type: Optional[PackageFinder] + comes_from=None, # type: Optional[str] + options=None, # type: Optional[optparse.Values] + constraint=False, # type: bool +): + # type: (...) -> Iterator[ParsedRequirement] + """Parse a requirements file and yield ParsedRequirement instances. + + :param filename: Path or url of requirements file. + :param session: PipSession instance. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param constraint: If true, parsing a constraint file rather than + requirements file. + """ + line_parser = get_line_parser(finder) + parser = RequirementsFileParser(session, line_parser, comes_from) + + for parsed_line in parser.parse(filename, constraint): + parsed_req = handle_line( + parsed_line, + options=options, + finder=finder, + session=session + ) + if parsed_req is not None: + yield parsed_req + + +def preprocess(content): + # type: (Text) -> ReqFileLines + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + """ + lines_enum = enumerate(content.splitlines(), start=1) # type: ReqFileLines + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def handle_requirement_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] +): + # type: (...) -> ParsedRequirement + + # preserve for the nested code path + line_comes_from = '{} {} (line {})'.format( + '-c' if line.constraint else '-r', line.filename, line.lineno, + ) + + assert line.is_requirement + + if line.is_editable: + # For editable requirements, we don't support per-requirement + # options, so just return the parsed requirement. + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + ) + else: + if options: + # Disable wheels if the user has specified build options + cmdoptions.check_install_build_global(options, line.opts) + + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in line.opts.__dict__ and line.opts.__dict__[dest]: + req_options[dest] = line.opts.__dict__[dest] + + line_source = 'line {} of {}'.format(line.lineno, line.filename) + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + options=req_options, + line_source=line_source, + ) + + +def handle_option_line( + opts, # type: Values + filename, # type: str + lineno, # type: int + finder=None, # type: Optional[PackageFinder] + options=None, # type: Optional[optparse.Values] + session=None, # type: Optional[PipSession] +): + # type: (...) -> None + + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled + if f not in options.features_enabled + ) + + # set finder options + if finder: + find_links = finder.find_links + index_urls = finder.index_urls + if opts.index_url: + index_urls = [opts.index_url] + if opts.no_index is True: + index_urls = [] + if opts.extra_index_urls: + index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + find_links.append(value) + + search_scope = SearchScope( + find_links=find_links, + index_urls=index_urls, + ) + finder.search_scope = search_scope + + if opts.pre: + finder.set_allow_all_prereleases() + + if opts.prefer_binary: + finder.set_prefer_binary() + + if session: + for host in opts.trusted_hosts or []: + source = 'line {} of {}'.format(lineno, filename) + session.add_trusted_host(host, source=source) + + +def handle_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] + finder=None, # type: Optional[PackageFinder] + session=None, # type: Optional[PipSession] +): + # type: (...) -> Optional[ParsedRequirement] + """Handle a single parsed requirements line; This can result in + creating/yielding requirements, or updating the finder. + + :param line: The parsed line to be processed. + :param options: CLI options. + :param finder: The finder - updated by non-requirement lines. + :param session: The session - updated by non-requirement lines. + + Returns a ParsedRequirement object if the line is a requirement line, + otherwise returns None. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + """ + + if line.is_requirement: + parsed_req = handle_requirement_line(line, options) + return parsed_req + else: + handle_option_line( + line.opts, + line.filename, + line.lineno, + finder, + options, + session, + ) + return None + + +class RequirementsFileParser(object): + def __init__( + self, + session, # type: PipSession + line_parser, # type: LineParser + comes_from, # type: Optional[str] + ): + # type: (...) -> None + self._session = session + self._line_parser = line_parser + self._comes_from = comes_from + + def parse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + """Parse a given file, yielding parsed lines. + """ + for line in self._parse_and_recurse(filename, constraint): + yield line + + def _parse_and_recurse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + for line in self._parse_file(filename, constraint): + if ( + not line.is_requirement and + (line.opts.requirements or line.opts.constraints) + ): + # parse a nested requirements file + if line.opts.requirements: + req_path = line.opts.requirements[0] + nested_constraint = False + else: + req_path = line.opts.constraints[0] + nested_constraint = True + + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join( + os.path.dirname(filename), req_path, + ) + + for inner_line in self._parse_and_recurse( + req_path, nested_constraint, + ): + yield inner_line + else: + yield line + + def _parse_file(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + _, content = get_file_content( + filename, self._session, comes_from=self._comes_from + ) + + lines_enum = preprocess(content) + + for line_number, line in lines_enum: + try: + args_str, opts = self._line_parser(line) + except OptionParsingError as e: + # add offending line + msg = 'Invalid requirement: {}\n{}'.format(line, e.msg) + raise RequirementsFileParseError(msg) + + yield ParsedLine( + filename, + line_number, + self._comes_from, + args_str, + opts, + constraint, + ) + + +def get_line_parser(finder): + # type: (Optional[PackageFinder]) -> LineParser + def parse_line(line): + # type: (Text) -> Tuple[str, Values] + # Build new parser for each line since it accumulates appendable + # options. + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + defaults.format_control = finder.format_control + + args_str, options_str = break_args_options(line) + # Prior to 2.7.3, shlex cannot deal with unicode entries + if sys.version_info < (2, 7, 3): + # https://github.com/python/mypy/issues/1174 + options_str = options_str.encode('utf8') # type: ignore + + # https://github.com/python/mypy/issues/1174 + opts, _ = parser.parse_args( + shlex.split(options_str), defaults) # type: ignore + + return args_str, opts + + return parse_line + + +def break_args_options(line): + # type: (Text) -> Tuple[str, Text] + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) # type: ignore + + +class OptionParsingError(Exception): + def __init__(self, msg): + # type: (str) -> None + self.msg = msg + + +def build_parser(): + # type: () -> optparse.OptionParser + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + # type: (Any, str) -> NoReturn + raise OptionParsingError(msg) + # NOTE: mypy disallows assigning to a method + # https://github.com/python/mypy/issues/2427 + parser.exit = parser_exit # type: ignore + + return parser + + +def join_lines(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] # type: List[Text] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + assert primary_line_number is not None + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + assert primary_line_number is not None + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def expand_env_variables(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discussion on the `github pull + request #3514 `_. + + Valid characters in variable names follow the `POSIX standard + `_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line + + +def get_file_content(url, session, comes_from=None): + # type: (str, PipSession, Optional[str]) -> Tuple[str, Text] + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + Respects # -*- coding: declarations on the retrieved files. + + :param url: File path or url. + :param session: PipSession instance. + :param comes_from: Origin description of requirements. + """ + scheme = get_url_scheme(url) + + if scheme in ['http', 'https']: + # FIXME: catch some errors + resp = session.get(url) + raise_for_status(resp) + return resp.url, resp.text + + elif scheme == 'file': + if comes_from and comes_from.startswith('http'): + raise InstallationError( + 'Requirements file {} references URL {}, ' + 'which is local'.format(comes_from, url) + ) + + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib_parse.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: {}'.format(exc) + ) + return url, content + + +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_install.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..f25cec9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,905 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import uuid +import zipfile + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_scheme +from pip._internal.models.link import Link +from pip._internal.operations.build.metadata import generate_metadata +from pip._internal.operations.build.metadata_legacy import \ + generate_metadata as generate_metadata_legacy +from pip._internal.operations.install.editable_legacy import \ + install_editable as install_editable_legacy +from pip._internal.operations.install.legacy import LegacyInstallFailure +from pip._internal.operations.install.legacy import install as install_legacy +from pip._internal.operations.install.wheel import install_wheel +from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + dist_in_site_packages, + dist_in_usersite, + get_distribution, + get_installed_version, + hide_url, + redact_auth_from_url, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, Optional, Sequence, Union, + ) + from pip._internal.build_env import BuildEnvironment + from pip._vendor.pkg_resources import Distribution + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.packaging.markers import Marker + + +logger = logging.getLogger(__name__) + + +def _get_dist(metadata_directory): + # type: (str) -> Distribution + """Return a pkg_resources.Distribution for the provided + metadata directory. + """ + dist_dir = metadata_directory.rstrip(os.sep) + + # Build a PathMetadata object, from path to metadata. :wink: + base_dir, dist_dir_name = os.path.split(dist_dir) + metadata = pkg_resources.PathMetadata(base_dir, dist_dir) + + # Determine the correct Distribution object type. + if dist_dir.endswith(".egg-info"): + dist_cls = pkg_resources.Distribution + dist_name = os.path.splitext(dist_dir_name)[0] + else: + assert dist_dir.endswith(".dist-info") + dist_cls = pkg_resources.DistInfoDistribution + dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0] + + return dist_cls( + base_dir, + project_name=dist_name, + metadata=metadata, + ) + + +class InstallRequirement(object): + """ + Represents something that may be installed later on, may have information + about where to fetch the relevant requirement and also contains logic for + installing the said requirement. + """ + + def __init__( + self, + req, # type: Optional[Requirement] + comes_from, # type: Optional[Union[str, InstallRequirement]] + editable=False, # type: bool + link=None, # type: Optional[Link] + markers=None, # type: Optional[Marker] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + install_options=None, # type: Optional[List[str]] + global_options=None, # type: Optional[List[str]] + hash_options=None, # type: Optional[Dict[str, List[str]]] + constraint=False, # type: bool + extras=(), # type: Iterable[str] + user_supplied=False, # type: bool + ): + # type: (...) -> None + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.editable = editable + self.legacy_install_reason = None # type: Optional[int] + + # source_dir is the local directory where the linked requirement is + # located, or unpacked. In case unpacking is needed, creating and + # populating source_dir is done by the RequirementPreparer. Note this + # is not necessarily the directory where pyproject.toml or setup.py is + # located - that one is obtained via unpacked_source_directory. + self.source_dir = None # type: Optional[str] + if self.editable: + assert link + if link.is_file: + self.source_dir = os.path.normpath( + os.path.abspath(link.file_path) + ) + + if link is None and req and req.url: + # PEP 508 URL requirement + link = Link(req.url) + self.link = self.original_link = link + self.original_link_is_in_wheel_cache = False + + # Path to any downloaded or already-existing package. + self.local_file_path = None # type: Optional[str] + if self.link and self.link.is_file: + self.local_file_path = self.link.file_path + + if extras: + self.extras = extras + elif req: + self.extras = { + pkg_resources.safe_extra(extra) for extra in req.extras + } + else: + self.extras = set() + if markers is None and req: + markers = req.marker + self.markers = markers + + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None # type: Optional[Distribution] + # Whether the installation process should try to uninstall an existing + # distribution before installing this requirement. + self.should_reinstall = False + # Temporary build location + self._temp_build_dir = None # type: Optional[TempDirectory] + # Set to True after successful installation + self.install_succeeded = None # type: Optional[bool] + # Supplied options + self.install_options = install_options if install_options else [] + self.global_options = global_options if global_options else [] + self.hash_options = hash_options if hash_options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = user_supplied + + # Set by the legacy resolver when the requirement has been downloaded + # TODO: This introduces a strong coupling between the resolver and the + # requirement (the coupling was previously between the resolver + # and the requirement set). This should be refactored to allow + # the requirement to decide for itself when it has been + # successfully downloaded - but that is more tricky to get right, + # se we are making the change in stages. + self.successfully_downloaded = False + + self.isolated = isolated + self.build_env = NoOpBuildEnvironment() # type: BuildEnvironment + + # For PEP 517, the directory where we request the project metadata + # gets stored. We need this to pass to build_wheel, so the backend + # can ensure that the wheel matches the metadata (see the PEP for + # details). + self.metadata_directory = None # type: Optional[str] + + # The static build requirements (from pyproject.toml) + self.pyproject_requires = None # type: Optional[List[str]] + + # Build requirements that we will check are available + self.requirements_to_check = [] # type: List[str] + + # The PEP 517 backend we should use to build the project + self.pep517_backend = None # type: Optional[Pep517HookCaller] + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = use_pep517 + + def __str__(self): + # type: () -> str + if self.req: + s = str(self.req) + if self.link: + s += ' from {}'.format(redact_auth_from_url(self.link.url)) + elif self.link: + s = redact_auth_from_url(self.link.url) + else: + s = '' + if self.satisfied_by is not None: + s += ' in {}'.format(display_path(self.satisfied_by.location)) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from # type: Optional[str] + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from {})'.format(comes_from) + return s + + def __repr__(self): + # type: () -> str + return '<{} object: {} editable={!r}>'.format( + self.__class__.__name__, str(self), self.editable) + + def format_debug(self): + # type: () -> str + """An un-tested helper for getting state, for debugging. + """ + attributes = vars(self) + names = sorted(attributes) + + state = ( + "{}={!r}".format(attr, attributes[attr]) for attr in sorted(names) + ) + return '<{name} object: {{{state}}}>'.format( + name=self.__class__.__name__, + state=", ".join(state), + ) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + # type: () -> Optional[str] + if self.req is None: + return None + return six.ensure_str(pkg_resources.safe_name(self.req.name)) + + @property + def specifier(self): + # type: () -> SpecifierSet + return self.req.specifier + + @property + def is_pinned(self): + # type: () -> bool + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in {'==', '==='}) + + @property + def installed_version(self): + # type: () -> Optional[str] + return get_installed_version(self.name) + + def match_markers(self, extras_requested=None): + # type: (Optional[Iterable[str]]) -> bool + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + @property + def has_hash_options(self): + # type: () -> bool + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.hash_options) + + def hashes(self, trust_internet=True): + # type: (bool) -> Hashes + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.hash_options.copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + def from_path(self): + # type: () -> Optional[str] + """Format a nice indicator to show where this "comes from" + """ + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def ensure_build_location(self, build_dir, autodelete, parallel_builds): + # type: (str, bool, bool) -> str + assert build_dir is not None + if self._temp_build_dir is not None: + assert self._temp_build_dir.path + return self._temp_build_dir.path + if self.req is None: + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir = TempDirectory( + kind=tempdir_kinds.REQ_BUILD, globally_managed=True + ) + + return self._temp_build_dir.path + + # When parallel builds are enabled, add a UUID to the build directory + # name so multiple builds do not interfere with each other. + dir_name = canonicalize_name(self.name) + if parallel_builds: + dir_name = "{}_{}".format(dir_name, uuid.uuid4().hex) + + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + os.makedirs(build_dir) + actual_build_dir = os.path.join(build_dir, dir_name) + # `None` indicates that we respect the globally-configured deletion + # settings, which is what we actually want when auto-deleting. + delete_arg = None if autodelete else False + return TempDirectory( + path=actual_build_dir, + delete=delete_arg, + kind=tempdir_kinds.REQ_BUILD, + globally_managed=True, + ).path + + def _set_requirement(self): + # type: () -> None + """Set requirement after generating metadata. + """ + assert self.req is None + assert self.metadata is not None + assert self.source_dir is not None + + # Construct a Requirement object from the generated metadata + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + + def warn_on_mismatching_name(self): + # type: () -> None + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) == metadata_name: + # Everything is fine. + return + + # If we're here, there's a mismatch. Log a warning about it. + logger.warning( + 'Generating metadata for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def check_if_exists(self, use_user_site): + # type: (bool) -> None + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.should_reinstall appropriately. + """ + if self.req is None: + return + existing_dist = get_distribution(self.req.name) + if not existing_dist: + return + + existing_version = existing_dist.parsed_version + if not self.req.specifier.contains(existing_version, prereleases=True): + self.satisfied_by = None + if use_user_site: + if dist_in_usersite(existing_dist): + self.should_reinstall = True + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to {} in {}".format( + existing_dist.project_name, existing_dist.location) + ) + else: + self.should_reinstall = True + else: + if self.editable: + self.should_reinstall = True + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + else: + self.satisfied_by = existing_dist + + # Things valid for wheels + @property + def is_wheel(self): + # type: () -> bool + if not self.link: + return False + return self.link.is_wheel + + # Things valid for sdists + @property + def unpacked_source_directory(self): + # type: () -> str + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + setup_py = os.path.join(self.unpacked_source_directory, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + @property + def pyproject_toml_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + return make_pyproject_path(self.unpacked_source_directory) + + def load_pyproject_toml(self): + # type: () -> None + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pyproject_toml_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml_path, + self.setup_py_path, + str(self) + ) + + if pyproject_toml_data is None: + self.use_pep517 = False + return + + self.use_pep517 = True + requires, backend, check, backend_path = pyproject_toml_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller( + self.unpacked_source_directory, backend, backend_path=backend_path, + ) + + def _generate_metadata(self): + # type: () -> str + """Invokes metadata generator functions, with the required arguments. + """ + if not self.use_pep517: + assert self.unpacked_source_directory + + return generate_metadata_legacy( + build_env=self.build_env, + setup_py_path=self.setup_py_path, + source_dir=self.unpacked_source_directory, + isolated=self.isolated, + details=self.name or "from {}".format(self.link) + ) + + assert self.pep517_backend is not None + + return generate_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + ) + + def prepare_metadata(self): + # type: () -> None + """Ensure that project metadata is available. + + Under PEP 517, call the backend hook to prepare the metadata. + Under legacy processing, call setup.py egg-info. + """ + assert self.source_dir + + with indent_log(): + self.metadata_directory = self._generate_metadata() + + # Act on the newly generated metadata, based on the name and version. + if not self.name: + self._set_requirement() + else: + self.warn_on_mismatching_name() + + self.assert_source_matches_version() + + @property + def metadata(self): + # type: () -> Any + if not hasattr(self, '_metadata'): + self._metadata = get_metadata(self.get_dist()) + + return self._metadata + + def get_dist(self): + # type: () -> Distribution + return _get_dist(self.metadata_directory) + + def assert_source_matches_version(self): + # type: () -> None + assert self.source_dir + version = self.metadata['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir( + self, + parent_dir, + autodelete=False, + parallel_builds=False, + ): + # type: (str, bool, bool) -> None + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.ensure_build_location( + parent_dir, + autodelete=autodelete, + parallel_builds=parallel_builds, + ) + + # For editable installations + def update_editable(self, obtain=True): + # type: (bool) -> None + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, \ + "bad url: {self.link.url!r}".format(**locals()) + vc_type, url = self.link.url.split('+', 1) + vcs_backend = vcs.get_backend(vc_type) + if vcs_backend: + if not self.link.is_vcs: + reason = ( + "This form of VCS requirement is being deprecated: {}." + ).format( + self.link.url + ) + replacement = None + if self.link.url.startswith("git+git@"): + replacement = ( + "git+https://git@example.com/..., " + "git+ssh://git@example.com/..., " + "or the insecure git+git://git@example.com/..." + ) + deprecated(reason, replacement, gone_in="21.0", issue=7554) + hidden_url = hide_url(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir, url=hidden_url) + else: + vcs_backend.export(self.source_dir, url=hidden_url) + else: + assert 0, ( + 'Unexpected version control type (in {}): {}'.format( + self.link, vc_type)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False): + # type: (bool, bool) -> Optional[UninstallPathSet] + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + assert self.req + dist = get_distribution(self.req.name) + if not dist: + logger.warning("Skipping %s as it is not installed.", self.name) + return None + logger.info('Found existing installation: %s', dist) + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _get_archive_name(self, path, parentdir, rootdir): + # type: (str, str, str) -> str + + def _clean_zip_name(name, prefix): + # type: (str, str) -> str + assert name.startswith(prefix + os.path.sep), ( + "name {name!r} doesn't start with prefix {prefix!r}" + .format(**locals()) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + path = os.path.join(parentdir, path) + name = _clean_zip_name(path, rootdir) + return self.name + '/' + name + + def archive(self, build_dir): + # type: (str) -> None + """Saves archive to provided build_dir. + + Used for saving downloaded VCS requirements as part of `pip download`. + """ + assert self.source_dir + + create_archive = True + archive_name = '{}-{}.zip'.format(self.name, self.metadata["version"]) + archive_path = os.path.join(build_dir, archive_name) + + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file {} exists. (i)gnore, (w)ipe, ' + '(b)ackup, (a)bort '.format( + display_path(archive_path)), + ('i', 'w', 'b', 'a')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == 'a': + sys.exit(-1) + + if not create_archive: + return + + zip_output = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True, + ) + with zip_output: + dir = os.path.normcase( + os.path.abspath(self.unpacked_source_directory) + ) + for dirpath, dirnames, filenames in os.walk(dir): + for dirname in dirnames: + dir_arcname = self._get_archive_name( + dirname, parentdir=dirpath, rootdir=dir, + ) + zipdir = zipfile.ZipInfo(dir_arcname + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip_output.writestr(zipdir, '') + for filename in filenames: + file_arcname = self._get_archive_name( + filename, parentdir=dirpath, rootdir=dir, + ) + filename = os.path.join(dirpath, filename) + zip_output.write(filename, file_arcname) + + logger.info('Saved %s', display_path(archive_path)) + + def install( + self, + install_options, # type: List[str] + global_options=None, # type: Optional[Sequence[str]] + root=None, # type: Optional[str] + home=None, # type: Optional[str] + prefix=None, # type: Optional[str] + warn_script_location=True, # type: bool + use_user_site=False, # type: bool + pycompile=True # type: bool + ): + # type: (...) -> None + scheme = get_scheme( + self.name, + user=use_user_site, + home=home, + root=root, + isolated=self.isolated, + prefix=prefix, + ) + + global_options = global_options if global_options is not None else [] + if self.editable: + install_editable_legacy( + install_options, + global_options, + prefix=prefix, + home=home, + use_user_site=use_user_site, + name=self.name, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + ) + self.install_succeeded = True + return + + if self.is_wheel: + assert self.local_file_path + direct_url = None + if self.original_link: + direct_url = direct_url_from_link( + self.original_link, + self.source_dir, + self.original_link_is_in_wheel_cache, + ) + install_wheel( + self.name, + self.local_file_path, + scheme=scheme, + req_description=str(self.req), + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=self.user_supplied, + ) + self.install_succeeded = True + return + + # TODO: Why don't we do this for editable installs? + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options = list(global_options) + self.global_options + install_options = list(install_options) + self.install_options + + try: + success = install_legacy( + install_options=install_options, + global_options=global_options, + root=root, + home=home, + prefix=prefix, + use_user_site=use_user_site, + pycompile=pycompile, + scheme=scheme, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + req_name=self.name, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + req_description=str(self.req), + ) + except LegacyInstallFailure as exc: + self.install_succeeded = False + six.reraise(*exc.parent) + except Exception: + self.install_succeeded = True + raise + + self.install_succeeded = success + + if success and self.legacy_install_reason == 8368: + deprecated( + reason=( + "{} was installed using the legacy 'setup.py install' " + "method, because a wheel could not be built for it.". + format(self.name) + ), + replacement="to fix the wheel build issue reported above", + gone_in="21.0", + issue=8368, + ) + + +def check_invalid_constraint_type(req): + # type: (InstallRequirement) -> str + + # Check for unsupported forms + problem = "" + if not req.name: + problem = "Unnamed requirements are not allowed as constraints" + elif req.link: + problem = "Links are not allowed as constraints" + elif req.extras: + problem = "Constraints cannot have extras" + + if problem: + deprecated( + reason=( + "Constraints are only allowed to take the form of a package " + "name and a version specifier. Other forms were originally " + "permitted as an accident of the implementation, but were " + "undocumented. The new implementation of the resolver no " + "longer supports these forms." + ), + replacement=( + "replacing the constraint with a requirement." + ), + # No plan yet for when the new resolver becomes default + gone_in=None, + issue=8210 + ) + + return problem diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_set.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..ab4b6f8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,203 @@ +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InstallationError +from pip._internal.models.wheel import Wheel +from pip._internal.utils import compatibility_tags +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List, Optional, Tuple + from pip._internal.req.req_install import InstallRequirement + + +logger = logging.getLogger(__name__) + + +class RequirementSet(object): + + def __init__(self, check_supported_wheels=True): + # type: (bool) -> None + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 + self.check_supported_wheels = check_supported_wheels + + self.unnamed_requirements = [] # type: List[InstallRequirement] + + def __str__(self): + # type: () -> str + requirements = sorted( + (req for req in self.requirements.values() if not req.comes_from), + key=lambda req: canonicalize_name(req.name), + ) + return ' '.join(str(req.req) for req in requirements) + + def __repr__(self): + # type: () -> str + requirements = sorted( + self.requirements.values(), + key=lambda req: canonicalize_name(req.name), + ) + + format_string = '<{classname} object; {count} requirement(s): {reqs}>' + return format_string.format( + classname=self.__class__.__name__, + count=len(requirements), + reqs=', '.join(str(req.req) for req in requirements), + ) + + def add_unnamed_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert not install_req.name + self.unnamed_requirements.append(install_req) + + def add_named_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert install_req.name + + project_name = canonicalize_name(install_req.name) + self.requirements[project_name] = install_req + + def add_requirement( + self, + install_req, # type: InstallRequirement + parent_req_name=None, # type: Optional[str] + extras_requested=None # type: Optional[Iterable[str]] + ): + # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]] # noqa: E501 + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + # If the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + install_req.name, install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + tags = compatibility_tags.get_supported() + if (self.check_supported_wheels and not wheel.supported(tags)): + raise InstallationError( + "{} is not a supported wheel on this platform.".format( + wheel.filename) + ) + + # This next bit is really a sanity check. + assert not install_req.user_supplied or parent_req_name is None, ( + "a user supplied req shouldn't have a parent" + ) + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not install_req.name: + self.add_unnamed_requirement(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement( + install_req.name) # type: Optional[InstallRequirement] + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: {} (already in {}, name={!r})" + .format(install_req, existing_req, install_req.name) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + self.add_named_requirement(install_req) + # We'd want to rescan this requirement later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = ( + install_req.link and + not ( + existing_req.link and + install_req.link.path == existing_req.link.path + ) + ) + if does_not_satisfy_constraint: + raise InstallationError( + "Could not satisfy constraints for '{}': " + "installation from path or url cannot be " + "constrained to a version".format(install_req.name) + ) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + # If we're now installing a user supplied requirement, + # mark the existing object as such. + if install_req.user_supplied: + existing_req.user_supplied = True + existing_req.extras = tuple(sorted( + set(existing_req.extras) | set(install_req.extras) + )) + logger.debug( + "Setting %s extras to: %s", + existing_req, existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def has_requirement(self, name): + # type: (str) -> bool + project_name = canonicalize_name(name) + + return ( + project_name in self.requirements and + not self.requirements[project_name].constraint + ) + + def get_requirement(self, name): + # type: (str) -> InstallRequirement + project_name = canonicalize_name(name) + + if project_name in self.requirements: + return self.requirements[project_name] + + raise KeyError("No project with the name {name!r}".format(**locals())) + + @property + def all_requirements(self): + # type: () -> List[InstallRequirement] + return self.unnamed_requirements + list(self.requirements.values()) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000..13fb245 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,150 @@ +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._vendor import contextlib2 + +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import TracebackType + from typing import Dict, Iterator, Optional, Set, Type, Union + from pip._internal.req.req_install import InstallRequirement + from pip._internal.models.link import Link + +logger = logging.getLogger(__name__) + + +@contextlib.contextmanager +def update_env_context_manager(**changes): + # type: (str) -> Iterator[None] + target = os.environ + + # Save values from the target and change them. + non_existent_marker = object() + saved_values = {} # type: Dict[str, Union[object, str]] + for name, new_value in changes.items(): + try: + saved_values[name] = target[name] + except KeyError: + saved_values[name] = non_existent_marker + target[name] = new_value + + try: + yield + finally: + # Restore original values in the target. + for name, original_value in saved_values.items(): + if original_value is non_existent_marker: + del target[name] + else: + assert isinstance(original_value, str) # for mypy + target[name] = original_value + + +@contextlib.contextmanager +def get_requirement_tracker(): + # type: () -> Iterator[RequirementTracker] + root = os.environ.get('PIP_REQ_TRACKER') + with contextlib2.ExitStack() as ctx: + if root is None: + root = ctx.enter_context( + TempDirectory(kind='req-tracker') + ).path + ctx.enter_context(update_env_context_manager(PIP_REQ_TRACKER=root)) + logger.debug("Initialized build tracking at %s", root) + + with RequirementTracker(root) as tracker: + yield tracker + + +class RequirementTracker(object): + + def __init__(self, root): + # type: (str) -> None + self._root = root + self._entries = set() # type: Set[InstallRequirement] + logger.debug("Created build tracker: %s", self._root) + + def __enter__(self): + # type: () -> RequirementTracker + logger.debug("Entered build tracker: %s", self._root) + return self + + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None + self.cleanup() + + def _entry_path(self, link): + # type: (Link) -> str + hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req): + # type: (InstallRequirement) -> None + """Add an InstallRequirement to build tracking. + """ + + assert req.link + # Get the file to write information about this requirement. + entry_path = self._entry_path(req.link) + + # Try reading from the file. If it exists and can be read from, a build + # is already in progress, so a LookupError is raised. + try: + with open(entry_path) as fp: + contents = fp.read() + except IOError as e: + # if the error is anything other than "file does not exist", raise. + if e.errno != errno.ENOENT: + raise + else: + message = '{} is already being built: {}'.format( + req.link, contents) + raise LookupError(message) + + # If we're here, req should really not be building already. + assert req not in self._entries + + # Start tracking this requirement. + with open(entry_path, 'w') as fp: + fp.write(str(req)) + self._entries.add(req) + + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + # type: (InstallRequirement) -> None + """Remove an InstallRequirement from build tracking. + """ + + assert req.link + # Delete the created file and the corresponding entries. + os.unlink(self._entry_path(req.link)) + self._entries.remove(req) + + logger.debug('Removed %s from build tracker %r', req, self._root) + + def cleanup(self): + # type: () -> None + for req in set(self._entries): + self.remove(req) + + logger.debug("Removed build tracker: %r", self._root) + + @contextlib.contextmanager + def track(self, req): + # type: (InstallRequirement) -> Iterator[None] + self.add(req) + yield + self.remove(req) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..69719d3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,648 @@ +from __future__ import absolute_import + +import csv +import functools +import logging +import os +import sys +import sysconfig + +from pip._vendor import pkg_resources + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import bin_py, bin_user +from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + FakeFile, + ask, + dist_in_usersite, + dist_is_local, + egg_link_path, + is_local, + normalize_path, + renames, + rmtree, +) +from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple, + ) + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +def _script_names(dist, script_name, is_gui): + # type: (Distribution, str, bool) -> List[str] + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + exe_name = os.path.join(bin_dir, script_name) + paths_to_remove = [exe_name] + if WINDOWS: + paths_to_remove.append(exe_name + '.exe') + paths_to_remove.append(exe_name + '.exe.manifest') + if is_gui: + paths_to_remove.append(exe_name + '-script.pyw') + else: + paths_to_remove.append(exe_name + '-script.py') + return paths_to_remove + + +def _unique(fn): + # type: (Callable[..., Iterator[Any]]) -> Callable[..., Iterator[Any]] + @functools.wraps(fn) + def unique(*args, **kw): + # type: (Any, Any) -> Iterator[Any] + seen = set() # type: Set[Any] + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +@_unique +def uninstallation_paths(dist): + # type: (Distribution) -> Iterator[str] + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + """ + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + path = os.path.join(dn, base + '.pyo') + yield path + + +def compact(paths): + # type: (Iterable[str]) -> Set[str] + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + + sep = os.path.sep + short_paths = set() # type: Set[str] + for path in sorted(paths, key=len): + should_skip = any( + path.startswith(shortpath.rstrip("*")) and + path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_skip: + short_paths.add(path) + return short_paths + + +def compress_for_rename(paths): + # type: (Iterable[str]) -> Set[str] + """Returns a set containing the paths that need to be renamed. + + This set may include directories when the original sequence of paths + included every file on disk. + """ + case_map = dict((os.path.normcase(p), p) for p in paths) + remaining = set(case_map) + unchecked = sorted(set(os.path.split(p)[0] + for p in case_map.values()), key=len) + wildcards = set() # type: Set[str] + + def norm_join(*a): + # type: (str) -> str + return os.path.normcase(os.path.join(*a)) + + for root in unchecked: + if any(os.path.normcase(root).startswith(w) + for w in wildcards): + # This directory has already been handled. + continue + + all_files = set() # type: Set[str] + all_subdirs = set() # type: Set[str] + for dirname, subdirs, files in os.walk(root): + all_subdirs.update(norm_join(root, dirname, d) + for d in subdirs) + all_files.update(norm_join(root, dirname, f) + for f in files) + # If all the files we found are in our remaining set of files to + # remove, then remove them from the latter set and add a wildcard + # for the directory. + if not (all_files - remaining): + remaining.difference_update(all_files) + wildcards.add(root + os.sep) + + return set(map(case_map.__getitem__, remaining)) | wildcards + + +def compress_for_output_listing(paths): + # type: (Iterable[str]) -> Tuple[Set[str], Set[str]] + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = set(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + # probably this one https://github.com/python/mypy/issues/390 + _normcased_files = set(map(os.path.normcase, files)) # type: ignore + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if (os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | { + os.path.join(folder, "*") for folder in folders + } + + return will_remove, will_skip + + +class StashedUninstallPathSet(object): + """A set of file rename operations to stash files while + tentatively uninstalling them.""" + def __init__(self): + # type: () -> None + # Mapping from source file root to [Adjacent]TempDirectory + # for files under that directory. + self._save_dirs = {} # type: Dict[str, TempDirectory] + # (old path, new path) tuples for each move that may need + # to be undone. + self._moves = [] # type: List[Tuple[str, str]] + + def _get_directory_stash(self, path): + # type: (str) -> str + """Stashes a directory. + + Directories are stashed adjacent to their original location if + possible, or else moved/copied into the user's temp dir.""" + + try: + save_dir = AdjacentTempDirectory(path) # type: TempDirectory + except OSError: + save_dir = TempDirectory(kind="uninstall") + self._save_dirs[os.path.normcase(path)] = save_dir + + return save_dir.path + + def _get_file_stash(self, path): + # type: (str) -> str + """Stashes a file. + + If no root has been provided, one will be created for the directory + in the user's temp directory.""" + path = os.path.normcase(path) + head, old_head = os.path.dirname(path), None + save_dir = None + + while head != old_head: + try: + save_dir = self._save_dirs[head] + break + except KeyError: + pass + head, old_head = os.path.dirname(head), head + else: + # Did not find any suitable root + head = os.path.dirname(path) + save_dir = TempDirectory(kind='uninstall') + self._save_dirs[head] = save_dir + + relpath = os.path.relpath(path, head) + if relpath and relpath != os.path.curdir: + return os.path.join(save_dir.path, relpath) + return save_dir.path + + def stash(self, path): + # type: (str) -> str + """Stashes the directory or file and returns its new location. + Handle symlinks as files to avoid modifying the symlink targets. + """ + path_is_dir = os.path.isdir(path) and not os.path.islink(path) + if path_is_dir: + new_path = self._get_directory_stash(path) + else: + new_path = self._get_file_stash(path) + + self._moves.append((path, new_path)) + if (path_is_dir and os.path.isdir(new_path)): + # If we're moving a directory, we need to + # remove the destination first or else it will be + # moved to inside the existing directory. + # We just created new_path ourselves, so it will + # be removable. + os.rmdir(new_path) + renames(path, new_path) + return new_path + + def commit(self): + # type: () -> None + """Commits the uninstall by removing stashed files.""" + for _, save_dir in self._save_dirs.items(): + save_dir.cleanup() + self._moves = [] + self._save_dirs = {} + + def rollback(self): + # type: () -> None + """Undoes the uninstall by moving stashed files back.""" + for p in self._moves: + logger.info("Moving to %s\n from %s", *p) + + for new_path, path in self._moves: + try: + logger.debug('Replacing %s from %s', new_path, path) + if os.path.isfile(new_path) or os.path.islink(new_path): + os.unlink(new_path) + elif os.path.isdir(new_path): + rmtree(new_path) + renames(path, new_path) + except OSError as ex: + logger.error("Failed to restore %s", new_path) + logger.debug("Exception: %s", ex) + + self.commit() + + @property + def can_rollback(self): + # type: () -> bool + return bool(self._moves) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + # type: (Distribution) -> None + self.paths = set() # type: Set[str] + self._refuse = set() # type: Set[str] + self.pth = {} # type: Dict[str, UninstallPthEntries] + self.dist = dist + self._moved_paths = StashedUninstallPathSet() + + def _permitted(self, path): + # type: (str) -> bool + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + # type: (str) -> None + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + # type: (str, str) -> None + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def remove(self, auto_confirm=False, verbose=False): + # type: (bool, bool) -> None + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + + dist_name_version = ( + self.dist.project_name + "-" + self.dist.version + ) + logger.info('Uninstalling %s:', dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + moved = self._moved_paths + + for_rename = compress_for_rename(self.paths) + + for path in sorted(compact(for_rename)): + moved.stash(path) + logger.debug('Removing file or directory %s', path) + + for pth in self.pth.values(): + pth.remove() + + logger.info('Successfully uninstalled %s', dist_name_version) + + def _allowed_to_proceed(self, verbose): + # type: (bool) -> bool + """Display which files would be deleted and prompt for confirmation + """ + + def _display(msg, paths): + # type: (str, Iterable[str]) -> None + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self.paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = set(self.paths) + will_skip = set() + + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) + if verbose: + _display('Will actually move:', compress_for_rename(self.paths)) + + return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + + def rollback(self): + # type: () -> None + """Rollback the changes previously made by remove().""" + if not self._moved_paths.can_rollback: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return + logger.info('Rolling back uninstall of %s', self.dist.project_name) + self._moved_paths.rollback() + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + # type: () -> None + """Remove temporary save dir: rollback will no longer be possible.""" + self._moved_paths.commit() + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> UninstallPathSet + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + return cls(dist) + + if dist_path in {p for p in {sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib")} + if p}: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.project_name, + ) + ) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link {} does not match installed location of {} ' + '(at {})'.format( + link_pointer, dist.project_name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location, + ) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + _scripts_to_remove = [] + console_scripts = dist.get_entry_map(group='console_scripts') + for name in console_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, False)) + # find gui_scripts + gui_scripts = dist.get_entry_map(group='gui_scripts') + for name in gui_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, True)) + + for s in _scripts_to_remove: + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + # type: (str) -> None + self.file = pth_file + self.entries = set() # type: Set[str] + self._saved_lines = None # type: Optional[List[bytes]] + + def add(self, entry): + # type: (str) -> None + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + # os.path.splitdrive is used instead of os.path.isabs because isabs + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or + # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + # type: () -> None + logger.debug('Removing pth entries from %s:', self.file) + + # If the file doesn't exist, log a warning and return + if not os.path.isfile(self.file): + logger.warning( + "Cannot remove entries from nonexistent file %s", self.file + ) + return + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + # type: () -> bool + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..832e9d25b0ab81bc2044ef94a8f966e52a2d0b07 GIT binary patch literal 232 zcmYk0F$%&k7==@C5TOTgNdLh}5OHvJ5$Eum{fc+r`%Z)ursKBL(ODj!4% literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc5a25eb4d3e9494b2c9ecd7ffd0106c808726e8 GIT binary patch literal 1064 zcmZ`&O>fgM7`Br%&AN6>15MflCr&-IIdDQk2vMeO0;*}#4vUa1H-6WKIdN*cg>huN z@Dun6<0s|HiN62`c%5#j6T_12_sg%J=W)V2ofg6I_3Rh_<`MGUIm;tJ=LJsl4i`ZL zl_aMLr39$nL*J8L?k9d8Bms4KUpDeE32Qr$%{)pXto309!m$V8skcRv)-DkZ5nd7z z!s>;ev_@uXM_^Oeba^1j1)+y%07%qqo zbhZeW=N61*rB?4DweiC3K{1bIHgfJn6;Jw4W0P6vl{`J;V=!@vbOkt^{U4!pggL!}OV`t@xVbB)%z&-A zFP3{QzBvYq*y`8j4ir;bfZo8|4U)&VklbBA>^7>1v4ZEoSk+=IS7Iie-DYe$<8qPF z>)5w$zJ$ZE)((tr;?lus+^-SQD5xXDZl~Ht4f}sJ?4x|r^zC_>6=UsmsKRTvz!j1GrWkPXGV_ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/base.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/base.py new file mode 100644 index 0000000..2fa118b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/base.py @@ -0,0 +1,20 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, List + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_set import RequirementSet + + InstallRequirementProvider = Callable[ + [str, InstallRequirement], InstallRequirement + ] + + +class BaseResolver(object): + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + raise NotImplementedError() + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + raise NotImplementedError() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..874ee8c84b08c03d45c3181508cb56678b5a436f GIT binary patch literal 239 zcmYk0u?oU46h%{T5TPI9(7wS*5OHvJ5$EtsA8l-!#3U8_A1;1^zvSvCxaneogI>7j za_?In=J|w0UE4RNd?frUi)9t@pO z5FD|HQPA0vli%46fqdZBnFEOc-&&A9fs*@F8>46C9J~xvSQIVRJ=^1x6f$y0VSkHv;Y7A literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b0f65f8ac86b6d47122187c5913e174c3a4af86 GIT binary patch literal 11816 zcmai4U2q%Mb>2S!O8^8RQlkFlHEqd)LxPH>B#xrG){m7aO&Had;~Gt}USb#Il8asF z?m`j}&@%~@TJFTHJMDC)(~O6lbf)7zbf&L;>r)@{(x=XJU)z_w^`S3yW2@ge`vZVt zT5@pr?(Y4+=X~co=dLw7TbA(mSKI$%-Mk}7|4uiPe;RIH$1i*!OOh?MBwMx>Pj1P4 zRay$Js;73fmL}6X%`0?^ts>_OUa70Mbj}yOnQpmN?p9irZnahI&bDT|wN{PamAtub zy;bMD?j7mQx8^xN<1KWLwvKYX>>cYawiY>G@s4*-v`%oo>YePKYMttyZk^`)S?^5u zZ0l_ITT$M{$lYZ&32_k4G$wbXr~^+NZ>){EWqt@GUrtqWYb?!DB# z*t*F1BVMEXa_eQz&wF3$F1MCBzu>KOo2@42k9w=!ORYb~6ix_#C@X`gy5x2}FuvQOJ*9!vHa z=jg85`i8B%B{j}j11$b6r|0;#6|1Y1r!T1LGMOW2+hhDOja+JWEk+;)T;VfaDREIZmXxOk)dYto0ch8$OHr(^ZK$j1C| zTWD`uu8&Na7RT+ljtCot`1B1YjJ}UNrgGh*rUrNXFv8k7c6{`f8%Dxi=gnISqT4i< zc;;JHxcP<E-J`kDXkyLaC;Z+`2Io4;qi`S#k{ zowaXb-72?|)xCw)jkWKf^H~2L3xwsxDy9$@?)7^f&26@~y@7VapfB1^NF#q|({a3; z7KVYlqu1AIG1}4OVpFxdR(sppaBkB)X$8=Wzm4m{#9G}IPR|njdOqE^iMJ?x>e;7v zMeqQUDQ>q_l9lwYge!sTP5i=NAc>@rv@7qZ!+Io-)Da-`BV|W>NAh3Qq)6FP2X|#@ zryxtibCI@Ju$7UtRU8%eRatsRdRMa5ox=MCTYW3dU6L+IBTcmUiMCgY^k`-z?<)K9 zPI07&$7rK$7r2e$NZluRM%}1l7x$|9Q*~GVDF7TGQ?mXUAj7J1lSIpaVvx$RYHYX< z9N$P}&`9^5G^HJMdsgIlgal>iRLY(>ldyh4J;Xu*}l^(b7xnv+EzDf zlm+UlaoxDQ647Q8q(GGQlcPtm! zb+BNvsz6iNf<&*-IMpWDC*yNo3}4N<{qgf+Cxd(R4l(I;fV+N_3e{EP$2(a)D%+S9 z^UxS^gc9N)as5s*-nTNWjf=pk4M)VKhnDa$&bXS|B-6LLPONPOt{-`NeLyZ;599mJM6l*N7&(p)j!-XQA6X^e~dHtFRHiwGsHXwR$UP_u0CxwgYGNzO{nOit`A@)J+}(HDNl;bQY`0?TX_+SoPd> zx`hR}-2D0~f$>VZ=HV)A{3_HYa)fVrt7H4W>cKF#2gwtRuGt&Jr_BVY8QKp_>l%g~ z(%7YWT~_d~JuRzsSwpV)g{GZQG+9&V9#0hgbFH8$C?o4nm8aqgYPG5AF7?rerI>lv|p8;sdDz%4ioJ&$J45(JnofT18v8XK*dqWxIl_ZddJDTxaZ> zJ%?-AuG>d&t=RMS0{x$U#7Vbu;#WmGl_+) zI*@EY#JU5KO|A55AL5ke>0L^n2xeFzY2mO@s2JcC@DH4Y;4kP$%pJBfj7nFScbAaN z@&{SZiJrcuVrf3B5zAU}J1aC8$Np?C3RXfi8+HaO^eQemuv%iRU8Utr|0qwj=@YMrqcE_B7eUi*FwPIFAGf1nWBGTDW32AMlBb^(;x|sEmhV)1@A1&-s{iC*o zwO7`DO#^Ejiwj^FAZ}m5U96iBR5vosc)?7$q)C=KayCHIpr(URkSdBgm99hIriezY z!v?T%h61wC4)?dP^g&Z5mh;1LZ{lqQ61ZyY`sy9wb1c8;&*9cuTyfJ zs@0!q?KZu;MadhK&=|x`N^T&D=MF5!r*b@Fnr+VtL(^o;5tpg(H;{z%dl88=SCeas zF4q-ZDJn%7|4B+Y`d5ojhet9kX=d9qMPqa{Q^Ajh67C@Zje#0}1R^t%Z%IEqv!ji) zEg3{X8ELyRL{HsQN7_De_iGyAvwh!7u zH0S;eP@$Q@#~?x_5TP>=t`fwsxK|mKk`f?7%1-J1()}CBm1xlY8>6E5#i$5DCn}>_ z{{SkJ&I$@|MPo&s(s&!5O~OK;*~WD;Od)vOiUZ@+MkO-qU^!iIf&d-^ISCo;8W$}e zz_IDLV(flFm>RiF28lH6;GypY5J}YT`&1X8K^0R$w%L59BE>+`H146xypg0_0x=i~ z4cJ&881KCY)NkhK6E@TK-g_^l7c`&m`E3X0_3jkJjZI8z~plMmQ64~75Ho20BKoF%v0`^n5^F6TB9mR1mp79&k;ne<2lQ=GZsfMD@a;}O0Vibn8{aR zTy!2qaG~QW9;5pQj)~KqrX9=X^JU3{CK&4?gi_azkHqx*~pP(NnqMB;6J zI-&PyB#bNISDPjvFA$-)gd&M@l4WHLjZ10oe~l-JmR4bIpB7ae8mY_GqK0c7W_MAp zA-BYacUVv5Sv1q?Z9<>ykMi_mCB2Fa&>uLzt%w?h5cp2T-5Fa;?+V*W_%hoDc}wnH z8Od8H$Jkw56JJ9Mx5cYS?}@J>ZKw$d5hM!s89mpr#a+DmRN>X)#W*mjVYN1~T1ulOMf9i0C$5)+a^GYZB< ze}$4^5s6f)V}FO|#&bWc_%pL75$ApW1R0)v*_N@Z@*eE(76U2PLIqmTQ)x>RR{*nx zkxT%ZJliYMU3eF7OSS^J_)g+QQY%|}`0?;4-pp*3;iH0=eWVaH`!52TD}(oCunU>| z(b8LBT2XcIC)pEUfk!&kQ$x3oK^bqdLkTY_U5a$gjARjsdxhIBbkCkZFM>g^D$h&( zjMT?M3*>mFEfB5}oMyi2L$t&jW291-Z#=w|e6N6V0CS6J8i;dXSnW3AFpv!NMj$U7 zgv2a7AaRH7AchxCjO}^{Y};s~c*68BpaV1GfphR|(*j~n$_~&mg4E?BTO&*#&yGYT zTOYK`*qY782AR)iZa7i9XmO5#GvpX}d4j_kwkUdpB+| zf~m|~%f^f`@RH5PF0)hRfd>B%R=S#@BoObgz;*kfB}9hmHaJh6e+L&4Z&0 zZxk_5RDeOEf;>KKya*_8M`ns-AQ@dMH>-JD`HF|U;A6UQRX9;!_!O*&U|6ukR*2qc zAs%jG1FuuYkQ0M8a7CFIAbtYGMscw$Qs` z%R30afvu_|1l$m$D@G;o)FJ`s2nMvFtqrT!d{SJFjJ-Cze-AnWF}Gb>e5kmW2o_00 zMgmC$CN$eJ29{5JFpV4_1d=zfQ+{(SP3zG4jkGWdC33cdsOwB#J}xFt8#BpXzK&@N zhY}k}JOeYFbQaGhSL`cEtN0R?Ax}Y^N75)w*z$F%SzD)wMBWcKCtP}?cEgQ>8@^~B zlo^xcsRA}0o}D&7nI*9CGEe9dRZAhH#UNYwlTHEFewrVBX9Bh$iQmB zvcbb$#Ad_(?-K_g+XqLbFNGJ8Q(*^}?ZhG~Td;;(R0|us3y0z(6~aMfWigEOy%}2_ zDO=J$>|R=~jJ5@yRfMoLv|Xk5)CaZLC|DhkuQw2_2j^!v1fE-fKYyEqf(#rVIxsj8 z=8-P}&`gktUmXgnP_HI8(zOO))?^IP6dR0{36qg2D8XEc1UY<-!JR;vB6CTQ;ecT= z)^Rp~ct#R&Opq=15tu`x07JDtq2A(qO(VG!A9cu)wUdG(o7GC7t@nbgbj)x_)yoc5Rr!#%miwrLy^E+6EZv*=}4kxDFVR?Bf zd0QQXFv)+){4w@ScnB0ocm$0V(8JVn^~vUm;2|QP9tKKn<_&e-5RpTSH*6ZW`vQ{? z-9TXUQ=>JoyYR^o4%CG3DY;Gw1t%EJ zNtV(*i|6S0hFdT(L@|gp3apA*s=$~*C?3xuNW9@9_Q<*(ml5p=F-O#hpPPC`(u3oq ziHHrRLY`LQNL40!ItVN^vlqaC16sM0B?6Q)s43OTFcb5D6c`cu|GZex0WtH$tB;Q% zWvccRln{()_%W3HJu;;5Vm`AJ`%J92RE80Q-6d%c0KnD+BwM`3B>^X7Q;2RS)EdA+ zwgw?Jltd^Eq`CuwjzHR0nXL%b{X5)MK)==A&ylNgsw0)RX18iK+%>XF;uEwvNA1Xx zUybT}M+krznkd#$7!L0tEJdM}8w6lN7THmZH4@t4wW*E|k{aV8sr<@HN?AzM(`6IynzCuO$qyY#DWrL#7b5S zq3Mu$rb#68vBpQ9g-O9Bj+YwLc-BhK9w{Ce#j~lO$QnI3DAtQ6Y1mW^6O7??5^r+5 zB`~3?R+Gydw9}qy1&DhY&k)Q5Q+lc>+82si{*8i=K^0*Fgb}{b3X`_<(DtXodu4P( z9;J$3cnv*2fdYh8q_U`QfF9ZM6WNrXsC&wjM3@oY+n08<2*UY9-74T$Bnv7_8`6i; z2hs=f2g(O(N3&I1d#oe;2MhbB{~}9Ilsz4-&cN>+;aU!TQl}@dsZVk*6Kz#LmP7Qk zH7h=eYI}2b$=3JkbidC+A0g6Ace`>l53+{q2|80Pj*18?&hL~^XThGKupynB-v37` zpOnk$y-wxzk+yeqr0-Jzv%WKfNcPM~x69<|-v3Xue+;8If$Jh&c?FKYBiRKFSh0(C z{rN9Tc+3lNdD=k`NgBHX&{J`U0vlVKFD12nr+fA%P7&fWv8d zhiEiAgfL1to=miTtc>@-%Adz3a^QFh3Z3R>@s`}8LKH8>{*7(Dc!?i}{6z|mYY+oHPfv8LGZBYS45Pumit)V2+7bqnvC5ZwPPAPpHl^C3K7x9|6N6%^8bLTuL9s3wfw|{8s!C zRpnj%W6E8o}r?SaCout&Y3(D}=p<*zlaRjCsehjJ*BuCEa8FL-?cQmF`P` zqAGL|jtHHi+`qtpq7MJ#CVVvxv*bsV2{GjFl_s!+T+kF1CJGSq#z$TO4$HwPJw#X1 z%}P!s>69Y3oH@x{cVLDRDNIU}2y>*RX}p6I2#g4x&7e)38cY{l+Y;+8ghk+h6UXk+ zI2J>02OV`mypwOhjEz1H2f%tKS#2A8mH)w);+12(q74K)lET^urs)B=8T;&DR|sYU za-Wb#FEJ*;k4H)EWU`VlE%+RUR`L~~6^<%%w362vMHbRTomA*;f4O{eX!&|M?-OSa^vD6e|i$86q*hd?o+fmuB1(pf+Si&=M2fo!{Xsx{)z_6A^8f3 zv@%}?5@}`Bi*uTg5_X6kguBN{l4X;Z%QWgzl~E$`ow?_D8(OA z?njhZln|3kybB5p&`-sEg|247l; zCkJc z$ZgJqf={?wCl4<}*vm&^&$N*rtdUDEXj|h8(`_Kk>V?hh3l`UNqk&0IBYSr7>B9%_+6z)X|o0MTthCnd?@{efX6WyUMU-}TJX*iIeR9ll|tbtM3Zi|Nf#YWo-^i>?I@!T<(y=T0xXUJV%K^39+Kq;;sBMf?L^Fz4VR343zLY{JoR- z8!G%FCG^=1i}ZwJF{U^AwL~9ZBWw-nP*AJubifE-p{krI;hO%}<+?`E)-&`be@f-* LO1UU~UcU6djM8+? literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py new file mode 100644 index 0000000..c9b4c66 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -0,0 +1,485 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +import logging +import sys +from collections import defaultdict +from itertools import chain + +from pip._vendor.packaging import specifiers + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + HashError, + HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, normalize_version_info +from pip._internal.utils.packaging import ( + check_requires_python, + get_requires_python, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import DefaultDict, List, Optional, Set, Tuple + from pip._vendor import pkg_resources + + from pip._internal.cache import WheelCache + from pip._internal.distributions import AbstractDistribution + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] + +logger = logging.getLogger(__name__) + + +def _check_dist_requires_python( + dist, # type: pkg_resources.Distribution + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> None + """ + Check whether the given Python version is compatible with a distribution's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + + :raises UnsupportedPythonVersion: When the given Python version isn't + compatible. + """ + requires_python = get_requires_python(dist) + try: + is_compatible = check_requires_python( + requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier as exc: + logger.warning( + "Package %r has an invalid Requires-Python: %s", + dist.project_name, exc, + ) + return + + if is_compatible: + return + + version = '.'.join(map(str, version_info)) + if ignore_requires_python: + logger.debug( + 'Ignoring failed Requires-Python check for package %r: ' + '%s not in %r', + dist.project_name, version, requires_python, + ) + return + + raise UnsupportedPythonVersion( + 'Package {!r} requires a different Python: {} not in {!r}'.format( + dist.project_name, version, requires_python, + )) + + +class Resolver(BaseResolver): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> None + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + self._py_version_info = py_version_info + + self.preparer = preparer + self.finder = finder + self.wheel_cache = wheel_cache + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + self._make_install_req = make_install_req + + self._discovered_dependencies = \ + defaultdict(list) # type: DiscoveredDependencies + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + requirement_set = RequirementSet( + check_supported_wheels=check_supported_wheels + ) + for req in root_reqs: + if req.constraint: + check_invalid_constraint_type(req) + requirement_set.add_requirement(req) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # _populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] # type: List[InstallRequirement] + hash_errors = HashErrors() + for req in chain(requirement_set.all_requirements, discovered_reqs): + try: + discovered_reqs.extend(self._resolve_one(requirement_set, req)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + return requirement_set + + def _is_upgrade_allowed(self, req): + # type: (InstallRequirement) -> bool + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.user_supplied or req.constraint + + def _set_req_to_reinstall(self, req): + # type: (InstallRequirement) -> None + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or dist_in_usersite(req.satisfied_by): + req.should_reinstall = True + req.satisfied_by = None + + def _check_skip_installed(self, req_to_install): + # type: (InstallRequirement) -> Optional[str] + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return 'already satisfied, skipping upgrade' + return 'already satisfied' + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return 'already up-to-date' + except DistributionNotFound: + # No distribution found, so we squash the error. It will + # be raised later when we re-try later to do the install. + # Why don't we just raise here? + pass + + self._set_req_to_reinstall(req_to_install) + return None + + def _find_requirement_link(self, req): + # type: (InstallRequirement) -> Optional[Link] + upgrade = self._is_upgrade_allowed(req) + best_candidate = self.finder.find_requirement(req, upgrade) + if not best_candidate: + return None + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or '' + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + u'The candidate selected for download or install is a ' + 'yanked version: {candidate}\n' + 'Reason for being yanked: {reason}' + ).format(candidate=best_candidate, reason=reason) + logger.warning(msg) + + return link + + def _populate_link(self, req): + # type: (InstallRequirement) -> None + """Ensure that if a link can be found for this, that it is found. + + Note that req.link may still be None - if the requirement is already + installed and not needed to be upgraded based on the return value of + _is_upgrade_allowed(). + + If preparer.require_hashes is True, don't use the wheel cache, because + cached wheels, always built locally, have different hashes than the + files downloaded from the index server and thus throw false hash + mismatches. Furthermore, cached wheels at present have undeterministic + contents due to file modification times. + """ + if req.link is None: + req.link = self._find_requirement_link(req) + + if self.wheel_cache is None or self.preparer.require_hashes: + return + cache_entry = self.wheel_cache.get_cache_entry( + link=req.link, + package_name=req.name, + supported_tags=get_supported(), + ) + if cache_entry is not None: + logger.debug('Using cached wheel link: %s', cache_entry.link) + if req.link is req.original_link and cache_entry.persistent: + req.original_link_is_in_wheel_cache = True + req.link = cache_entry.link + + def _get_abstract_dist_for(self, req): + # type: (InstallRequirement) -> AbstractDistribution + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + if req.editable: + return self.preparer.prepare_editable_requirement(req) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement( + req, skip_reason + ) + + # We eagerly populate the link, since that's our "legacy" behavior. + self._populate_link(req) + abstract_dist = self.preparer.prepare_linked_requirement(req) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + 'Requirement already satisfied (use --upgrade to upgrade):' + ' %s', req, + ) + + return abstract_dist + + def _resolve_one( + self, + requirement_set, # type: RequirementSet + req_to_install, # type: InstallRequirement + ): + # type: (...) -> List[InstallRequirement] + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + abstract_dist = self._get_abstract_dist_for(req_to_install) + + # Parse and return dependencies + dist = abstract_dist.get_pkg_resources_distribution() + # This will raise UnsupportedPythonVersion if the given Python + # version isn't compatible with the distribution's Requires-Python. + _check_dist_requires_python( + dist, version_info=self._py_version_info, + ignore_requires_python=self.ignore_requires_python, + ) + + more_reqs = [] # type: List[InstallRequirement] + + def add_req(subreq, extras_requested): + sub_install_req = self._make_install_req( + str(subreq), + req_to_install, + ) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = requirement_set.add_requirement( + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append( + add_to_parent + ) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + # 'unnamed' requirements can only come from being directly + # provided by the user. + assert req_to_install.user_supplied + requirement_set.add_requirement( + req_to_install, parent_req_name=None, + ) + + if not self.ignore_dependencies: + if req_to_install.extras: + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + "%s does not provide the extra '%s'", + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq, extras_requested=available_requested) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + req_to_install.successfully_downloaded = True + + return more_reqs + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() # type: Set[InstallRequirement] + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3514f7a869ea4dcfb8a18bcabcafc6fa7288fc09 GIT binary patch literal 243 zcmYjLu?oU45KX~Bgno!adV`Z7;^6Ee&f#p2HnvS-k{0_X`U(D$tDoSeiwPa{!F%uC z;|>nfbjAX1^#`Lrqx>TYVH4;kK?=5K^JI5tHr79RU*X`$I>;+jSZY-uY<1qXqv$DZP?2n*5+k(uE!UA4OC8d}GXUxDlkS+Yoa literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b579f668182e2c5dd3e0f2bea9fb0c64510e2065 GIT binary patch literal 3154 zcmb_e&2tks6qnZP_1fMzPC|hIrKTUWZi&rIdtrvjFb!WBQp1qK4BKJ08%s%+?1v+* zQ(}@!lRu${_6EoNOS<;db8qxO-_ttICJ;(zB9ET*h)TI>XMrcF@w?GLmh!FTfXhuc17+J0rmE2NfySA1y!*Wz{ zD|%iGtC8b6dTxbdQO&Kv_ckxF(lp^EUYjP%#x!xqna#?(#I4^Utir0hgjM;(PQjf7 z-eD!+OPZfzV~eC&`wOfidsIzy{Wyt(t{(;i?!|t@n^aBy@Z`Z0Z|?4`xqIHjN1e`m z=T6g9<=Y||@OY7@%AQZT@Rvfa?EAemNMb)!#UJ~<5WjZ3?|sQ1b0Ogm;LU{~Uc+7I zW10G4_>iyn1HmI6r>Z>X$1Gre%DV#CxBkBD*LU;oyAB;hda?y4XAD@n}!_RXZ*NBdzh;q7&Q28J2_yvIe5E%!E4 zE>o{3l4rb|wlj8y#~bZ1SVDF+iN9@KYs(f)0bTYWOoQTLzbr@UM!vvx#XImq|f%Rhhrx)8# zU(+kig0f^HQqGi>eWc81Nf5(&X=t>v`14fw(yfnD#LHH+TQX7ywW^v^I|U2UMr#a{ znskziX_$9$A>S|_H=xU_Ksa$JBd);Y)T$3A3d~*wQ>OzNK{7(gL=-S7XEoCqfrQL({>?7FbW4TI=A({O+$PF zYpM*c&dTAay754I(oX}q9B}3>ZC(QLOtUdKpVxdu?!%oU&cl`hPb4lP!Q9V?4)UoL zo_Ztmyf8`D`n@keX5WfN-imh#pO`82VZOoOLF^LI%$ac^Z^luRK@e6 zg!MxXbI0@6`+kUV1I~sTfMQNJMFYL?DU#2Se2(P6GkS*Z5$AxQ10~)LHZ^F)wBe`M zyRdYq6@a?|UH%N@UD*$npz|Lpp$Kzb12DH>uIm8iC7734Wfx%3wON%pP#tA9#%eHD z*f^`hSY;ECWRn8x;KRL-RdVY7uqqls+>pbHIW{@8m)dxsVJ*}~u%1;y9**Bzp*obu z>QN=I3U8I~a=IoCbPfOg@T2~RItPj=$Sy6;uH|%|KG2yLIWfgm*p;&&#Vd9;r}v{n zdiNzxPOVJ(qRYJyE^G}{!*J%*<|N+2HRYTS8{ zQ+@70_1hTd2*hP^8OfJOzCzMK@->ogkX%7BgQSH7tM_0(Gck7}TQH8{>^>*>-*EU-tCBy#N3J literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72ca0a9dfeb5f19726bbb8758f23a4b0aa5b020b GIT binary patch literal 17845 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e1f16aefa446653a10c8e18ba94c35a7eb90da4 GIT binary patch literal 10862 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 pUuv-Y+Dex8pZ|-v;wowp!leKJ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f4b7247b6136485b388f9f8f0d1ec427e36e84d GIT binary patch literal 4045 zcma)9TaOz_74DloJ+|kP$pn&RfhJrw)@JOL0I@(6Hkz;(A%-ZE2yIKKx7}6t^i1DU zRi42kM?y-ZT^>LJ2?^ek$NfM1)UQ14AK(F`h3}NR$L=AEXsb_Ebyb}@m+zcY4mUPB z2A;p2{44y&R}JIuG+2Ec4Bo*je+R-0W+p~z`ld;JE3s1Bw{_o6oV4Y)OoO*Xf;lH? zr)&ON>iTZF?ysjEzms4v|NZu*awwx*sSm!coFY_g96A^DInC_mDrC@+{(| z4bElj(X_yP>v4wZ&rLeqD!UJ{CKf#9nJU-!!;Hl&R2)aHy$ca?F^fpd)yKl%9lY{B z2seC_8NS6#W<582o7v1k>ySCnw%8hT(YDz->!4j@UDiYEvJJM0cAag(6x(I@L0r`8 zMs4t|AL`SA`*`I|khwWG0%LB0nsXb}nmeHOyanno$gis@?>1D)qK0F zY_GnG>-J@)0KwW?YRIfhJwpH znG!}8-BYgjR$v0>Ss>7L3x zhGf8+yy`R0xF?VDX~JsVym(C91b+isdKo_hi&J0Fit2V44P`*I>;5rE{F$y zEYIFh*a`E5QN$%w&$WOb?d??t{Ky+8;pDbA;!%j0XlNN`7oyZ=^765FQ{{UyRIwb- z_VO&5-Sko(W@J{Jy281^i>#RCYH6$fpfO*zIM6H}oo^D$y?DGwVsrLx-x6N|Ko-Ge zmQEI?NMPbReWOSd-vBAE1;F_vhCu=Vp^oR&XK>9Hrg zHKIWhXgJs*I}*&oWJ)9baU2V&8vKe7N&pG8g>-=+S}%;jXvE{>6e6B3PF~+$WO!+2^wL1 z+f(6*-W)~wG*g;o72dL8ZOSC0I12@grB$V>kCY&n!BnY3eiY5P5*rjFi~E4zizRY@ zWoAJI7gqr@LO6Y!RxQKH)o}sWj__UTlD)Z&2s(fB|A?M?FL>Kvfu_B%LEqM4Tdq}@ z7vHTRNt?+&&^|$60w7^_!bDx zr6bGE#eHG6u)>Ndkgqcpk7uXEgg6>jvrN_tA}WjZLw#DTk9N^PIkcW3Dd@w=Gzri8 zPIdgUYL}g5Ap6}oLny%pNcorL+MIB@DiHJu59c^yA)R~v+NyP$%Nph(8R^wB{sf)5 zDcuPSM9J)hIY(K)VH{h^erCM1p5jt=?7V1EQOC$u?YZ${ZEn9rx80^#~BNj>XokG1bEH_T}5hR^j5-7!W%YvYLL|pkxh%Mhx+ttbI`qLZoYuiUamN83jwq!eKBXjXjUV- zyCQSBexc$eVZ{%@^pALJ7=3M3u1l;No+7a>JoP$>1`qX&r`63If|6Iv`-b*~-Mnm( z2WaylUP*ADVEb>jpOS-(sVSVf`6ug%Q^y6_7XKZr!CR#%E@@D9?og(~{ieA47h*@L zp>0{F`^EWNtI81SE@BFI@MUdXGC=TM!*>4)uCt=Cx-zTy5lwHL#p+03)sdX#Wr4OY zpY&%EivrHDT(fOKu-E^WUu?2-2KQaf=A>&mwp-4U@%;_$C-?n2I;4 zHze`{BIHx?QzA=bUcgga!Ne`R8kMf;Zn<||$L-+f=(&UbHQy`ZVi2I9vs?@o*Lu{2 zL4B(fQpZqpSye@Fw;$lXtN%z!K`1F-)7PN+qz6(uqYzbvqDpKLxj|%`2r2g}txNaL!isk(`D)dBX3y#&C~tMQ&EB>D E01w7NJ^%m! literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b44c8c4e483cf3c7e091fcacae1814ad825167e1 GIT binary patch literal 5081 zcmb7I&2JmW72i*C`Js>lqe4y-w+#_k_Q3>-#hNpcmRjyo zGb_QOrAy@kz2wkifdZ9|{+IUJlh3`SfSuo)<*q1dS-A_&?0oN=_wjr0(VLsAYIy#! z^Iz{@A86XYsWbUl=&Yg0zoB9p(*tdwcXgfGMqmugu8FqEtiT%BU3*aKmUNAq;tA&M zpgeH8j#^s^DuZgbs@9gVw$`l;>fO4oeWNjlRbFYV!tGMyYQ4XwHG2GnRxJ^7&Lg;D7DyucrEH}nP^gIB(L_Wd(={oAkC|L8t`+UY#%eDg-9 z^+r;A%y;*E!3R8yliFq^240+Pt8WZ>&)@X9*x)f8^C*3z^Tnm?sN) z^G{KIg-d#wowU;FW&?Wd9p+3sz7p|=%<%xgc4dV9nz3`V@Y>#g9m!e0!z@YCVp zLCj_B4n@@Gy||rrS9rMJ4*ciT-HyUvw(hs3AM=%=*W2;7xNHyoVcYe?n2XR0+Jeg{ z*o*xrOq=_h_KGuE$=2{7aa`>1W7l1z>#2JiqmK)C(#jV0NbK1|ZBl2&oFz1v-q#Mb zzTP)bo6PvZ0GG|q=*G)l;7O_U;^^+noMoSgqb4+t>xv|_=RuJmGG0*jdY{Gd5O%bg-xrq*|wRdEe_lB(+tBDNPm3zNF*?(TWPSSLTE zFD}y>2Zjq{jt`U`2oi+&n5vspol&M*q_m=jipZ9P9FFc-PTA0_Hr|$+>tXoZW1_c) zBELm73kFjV?Am+`f`G#U7-aK+!zx>Vp)Mv%IbgpR9^S>*4`K35+tcKgKfwHt^}px} zA@$h6jCo9GX%SjBjn3%ylWku%@bemhAMV_1gi(yP%)CU?PAr;8Y!@D7H>qrjXv9Ow z+lGh&jvJH8PQE=bQ%6r-|GR6iMRS!bksHKZ1pwXs6oWja>^+OfZ7j z%7zB{LpW2T^@~K8Q}<7*tAQ`$UNjg!_zoNXgQAV@7qVd5DYRCD2;rc7fJS!4kNQ19 z9x~AcQ8+U0HQv0#GqM{9hZ;>w=@N)SbQLFNz{AAqM}C+|%N}|no+eU3~cfHDbcp2{UgrULG~}CF&;#S8GGT1rN9oGnTuZroU4VO~*&fQdhvc?~A|IkUbFauS zAPu-ni&@OZn_MfLtI9NxXHe3G3_~Sb$S_or1=p(5t7Q3cipcVN6~B*r6!4Gk|L&Z; z2sF9Ggdc7-vRy0bw#I5zrh?LnLI$t zWU9t(~8pkkd)A)A|aXOqrsDqqMIa>A7fqPA%l zx-goW>AeO-fP*Sc44k+B`zi%4jF5$jx(b~%ogS=VEY;5>^&0BV&#pUI3H*nWVLf!r6f>6ms27l1aG?58*|Wd~|t z8F|Ye3~`HkY3{&Gcsi@|L{J!ksIUWPp=ex1U#wB}TdIm!M`834$~drF(t#BOxu90QaA(O2_`R$T>i)+8J+b%3~IX6LpvzLuSee+@@YTu$AqAU{!$d)HyiL;t!bq~nCB;^owqi!tXp9A$a J7jG~A`X>ydb@l)N literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af88e2d75c4c378529be51aed1a0bd68b6d14967 GIT binary patch literal 7142 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 str + if not extras: + return project + canonical_extras = sorted(canonicalize_name(e) for e in extras) + return "{}[{}]".format(project, ",".join(canonical_extras)) + + +class Requirement(object): + @property + def name(self): + # type: () -> str + raise NotImplementedError("Subclass should override") + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return False + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + raise NotImplementedError("Subclass should override") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") + + +class Candidate(object): + @property + def name(self): + # type: () -> str + raise NotImplementedError("Override in subclass") + + @property + def version(self): + # type: () -> _BaseVersion + raise NotImplementedError("Override in subclass") + + @property + def is_installed(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def is_editable(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def source_link(self): + # type: () -> Optional[Link] + raise NotImplementedError("Override in subclass") + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + raise NotImplementedError("Override in subclass") + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + raise NotImplementedError("Override in subclass") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py new file mode 100644 index 0000000..46cc7e7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -0,0 +1,600 @@ +import logging +import sys + +from pip._vendor.contextlib2 import suppress +from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version + +from pip._internal.exceptions import HashError, MetadataInconsistent +from pip._internal.network.lazy_wheel import ( + HTTPRangeRequestUnsupported, + dist_from_wheel_url, +) +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_is_editable, normalize_version_info +from pip._internal.utils.packaging import get_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Candidate, format_name + +if MYPY_CHECK_RUNNING: + from typing import Any, FrozenSet, Iterable, Optional, Tuple, Union + + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + + from pip._internal.distributions import AbstractDistribution + from pip._internal.models.link import Link + + from .base import Requirement + from .factory import Factory + + BaseCandidate = Union[ + "AlreadyInstalledCandidate", + "EditableCandidate", + "LinkCandidate", + ] + + +logger = logging.getLogger(__name__) + + +def make_install_req_from_link(link, template): + # type: (Link, InstallRequirement) -> InstallRequirement + assert not template.editable, "template is editable" + if template.req: + line = str(template.req) + else: + line = link.url + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + ireq.original_link = template.original_link + ireq.link = link + return ireq + + +def make_install_req_from_editable(link, template): + # type: (Link, InstallRequirement) -> InstallRequirement + assert template.editable, "template not editable" + return install_req_from_editable( + link.url, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + + +def make_install_req_from_dist(dist, template): + # type: (Distribution, InstallRequirement) -> InstallRequirement + project_name = canonicalize_name(dist.project_name) + if template.req: + line = str(template.req) + elif template.link: + line = "{} @ {}".format(project_name, template.link.url) + else: + line = "{}=={}".format(project_name, dist.parsed_version) + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + ireq.satisfied_by = dist + return ireq + + +class _InstallRequirementBackedCandidate(Candidate): + """A candidate backed by an ``InstallRequirement``. + + This represents a package request with the target not being already + in the environment, and needs to be fetched and installed. The backing + ``InstallRequirement`` is responsible for most of the leg work; this + class exposes appropriate information to the resolver. + + :param link: The link passed to the ``InstallRequirement``. The backing + ``InstallRequirement`` will use this link to fetch the distribution. + :param source_link: The link this candidate "originates" from. This is + different from ``link`` when the link is found in the wheel cache. + ``link`` would point to the wheel cache, while this points to the + found remote link (e.g. from pypi.org). + """ + is_installed = False + + def __init__( + self, + link, # type: Link + source_link, # type: Link + ireq, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + self._link = link + self._source_link = source_link + self._factory = factory + self._ireq = ireq + self._name = name + self._version = version + self._dist = None # type: Optional[Distribution] + self._prepared = False + + def __repr__(self): + # type: () -> str + return "{class_name}({link!r})".format( + class_name=self.__class__.__name__, + link=str(self._link), + ) + + def __hash__(self): + # type: () -> int + return hash((self.__class__, self._link)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self._link == other._link + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def source_link(self): + # type: () -> Optional[Link] + return self._source_link + + @property + def name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + if self._name is None: + self._name = canonicalize_name(self.dist.project_name) + return self._name + + @property + def version(self): + # type: () -> _BaseVersion + if self._version is None: + self._version = self.dist.parsed_version + return self._version + + def format_for_error(self): + # type: () -> str + return "{} {} (from {})".format( + self.name, + self.version, + self._link.file_path if self._link.is_file else self._link + ) + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + raise NotImplementedError("Override in subclass") + + def _check_metadata_consistency(self): + # type: () -> None + """Check for consistency of project name and version of dist.""" + # TODO: (Longer term) Rather than abort, reject this candidate + # and backtrack. This would need resolvelib support. + dist = self._dist # type: Distribution + name = canonicalize_name(dist.project_name) + if self._name is not None and self._name != name: + raise MetadataInconsistent(self._ireq, "name", dist.project_name) + version = dist.parsed_version + if self._version is not None and self._version != version: + raise MetadataInconsistent(self._ireq, "version", dist.version) + + def _prepare(self): + # type: () -> None + if self._prepared: + return + try: + abstract_dist = self._prepare_abstract_distribution() + except HashError as e: + e.req = self._ireq + raise + + self._dist = abstract_dist.get_pkg_resources_distribution() + assert self._dist is not None, "Distribution already installed" + self._check_metadata_consistency() + self._prepared = True + + def _fetch_metadata(self): + # type: () -> None + """Fetch metadata, using lazy wheel if possible.""" + preparer = self._factory.preparer + use_lazy_wheel = self._factory.use_lazy_wheel + remote_wheel = self._link.is_wheel and not self._link.is_file + if use_lazy_wheel and remote_wheel and not preparer.require_hashes: + assert self._name is not None + logger.info('Collecting %s', self._ireq.req or self._ireq) + # If HTTPRangeRequestUnsupported is raised, fallback silently. + with indent_log(), suppress(HTTPRangeRequestUnsupported): + logger.info( + 'Obtaining dependency information from %s %s', + self._name, self._version, + ) + url = self._link.url.split('#', 1)[0] + session = preparer.downloader._session + self._dist = dist_from_wheel_url(self._name, url, session) + self._check_metadata_consistency() + if self._dist is None: + self._prepare() + + @property + def dist(self): + # type: () -> Distribution + if self._dist is None: + self._fetch_metadata() + return self._dist + + def _get_requires_python_specifier(self): + # type: () -> Optional[SpecifierSet] + requires_python = get_requires_python(self.dist) + if requires_python is None: + return None + try: + spec = SpecifierSet(requires_python) + except InvalidSpecifier as e: + logger.warning( + "Package %r has an invalid Requires-Python: %s", self.name, e, + ) + return None + return spec + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + if not with_requires: + return + for r in self.dist.requires(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + python_dep = self._factory.make_requires_python_requirement( + self._get_requires_python_specifier(), + ) + if python_dep: + yield python_dep + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + self._prepare() + return self._ireq + + +class LinkCandidate(_InstallRequirementBackedCandidate): + is_editable = False + + def __init__( + self, + link, # type: Link + template, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + source_link = link + cache_entry = factory.get_wheel_cache_entry(link, name) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + link = cache_entry.link + ireq = make_install_req_from_link(link, template) + + if (cache_entry is not None and + cache_entry.persistent and + template.link is template.original_link): + ireq.original_link_is_in_wheel_cache = True + + super(LinkCandidate, self).__init__( + link=link, + source_link=source_link, + ireq=ireq, + factory=factory, + name=name, + version=version, + ) + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + return self._factory.preparer.prepare_linked_requirement( + self._ireq, parallel_builds=True, + ) + + +class EditableCandidate(_InstallRequirementBackedCandidate): + is_editable = True + + def __init__( + self, + link, # type: Link + template, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + super(EditableCandidate, self).__init__( + link=link, + source_link=link, + ireq=make_install_req_from_editable(link, template), + factory=factory, + name=name, + version=version, + ) + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + return self._factory.preparer.prepare_editable_requirement(self._ireq) + + +class AlreadyInstalledCandidate(Candidate): + is_installed = True + source_link = None + + def __init__( + self, + dist, # type: Distribution + template, # type: InstallRequirement + factory, # type: Factory + ): + # type: (...) -> None + self.dist = dist + self._ireq = make_install_req_from_dist(dist, template) + self._factory = factory + + # This is just logging some messages, so we can do it eagerly. + # The returned dist would be exactly the same as self.dist because we + # set satisfied_by in make_install_req_from_dist. + # TODO: Supply reason based on force_reinstall and upgrade_strategy. + skip_reason = "already satisfied" + factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) + + def __repr__(self): + # type: () -> str + return "{class_name}({distribution!r})".format( + class_name=self.__class__.__name__, + distribution=self.dist, + ) + + def __hash__(self): + # type: () -> int + return hash((self.__class__, self.name, self.version)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.name == other.name and self.version == other.version + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def name(self): + # type: () -> str + return canonicalize_name(self.dist.project_name) + + @property + def version(self): + # type: () -> _BaseVersion + return self.dist.parsed_version + + @property + def is_editable(self): + # type: () -> bool + return dist_is_editable(self.dist) + + def format_for_error(self): + # type: () -> str + return "{} {} (Installed)".format(self.name, self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + if not with_requires: + return + for r in self.dist.requires(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None + + +class ExtrasCandidate(Candidate): + """A candidate that has 'extras', indicating additional dependencies. + + Requirements can be for a project with dependencies, something like + foo[extra]. The extras don't affect the project/version being installed + directly, but indicate that we need additional dependencies. We model that + by having an artificial ExtrasCandidate that wraps the "base" candidate. + + The ExtrasCandidate differs from the base in the following ways: + + 1. It has a unique name, of the form foo[extra]. This causes the resolver + to treat it as a separate node in the dependency graph. + 2. When we're getting the candidate's dependencies, + a) We specify that we want the extra dependencies as well. + b) We add a dependency on the base candidate. + See below for why this is needed. + 3. We return None for the underlying InstallRequirement, as the base + candidate will provide it, and we don't want to end up with duplicates. + + The dependency on the base candidate is needed so that the resolver can't + decide that it should recommend foo[extra1] version 1.0 and foo[extra2] + version 2.0. Having those candidates depend on foo=1.0 and foo=2.0 + respectively forces the resolver to recognise that this is a conflict. + """ + def __init__( + self, + base, # type: BaseCandidate + extras, # type: FrozenSet[str] + ): + # type: (...) -> None + self.base = base + self.extras = extras + + def __repr__(self): + # type: () -> str + return "{class_name}(base={base!r}, extras={extras!r})".format( + class_name=self.__class__.__name__, + base=self.base, + extras=self.extras, + ) + + def __hash__(self): + # type: () -> int + return hash((self.base, self.extras)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.base == other.base and self.extras == other.extras + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + return format_name(self.base.name, self.extras) + + @property + def version(self): + # type: () -> _BaseVersion + return self.base.version + + def format_for_error(self): + # type: () -> str + return "{} [{}]".format( + self.base.format_for_error(), + ", ".join(sorted(self.extras)) + ) + + @property + def is_installed(self): + # type: () -> bool + return self.base.is_installed + + @property + def is_editable(self): + # type: () -> bool + return self.base.is_editable + + @property + def source_link(self): + # type: () -> Optional[Link] + return self.base.source_link + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + factory = self.base._factory + + # Add a dependency on the exact base + # (See note 2b in the class docstring) + yield factory.make_requirement_from_candidate(self.base) + if not with_requires: + return + + # The user may have specified extras that the candidate doesn't + # support. We ignore any unsupported extras here. + valid_extras = self.extras.intersection(self.base.dist.extras) + invalid_extras = self.extras.difference(self.base.dist.extras) + for extra in sorted(invalid_extras): + logger.warning( + "%s %s does not provide the extra '%s'", + self.base.name, + self.version, + extra + ) + + for r in self.base.dist.requires(valid_extras): + requirement = factory.make_requirement_from_spec( + str(r), self.base._ireq, valid_extras, + ) + if requirement: + yield requirement + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + # We don't return anything here, because we always + # depend on the base candidate, and we'll get the + # install requirement from that. + return None + + +class RequiresPythonCandidate(Candidate): + is_installed = False + source_link = None + + def __init__(self, py_version_info): + # type: (Optional[Tuple[int, ...]]) -> None + if py_version_info is not None: + version_info = normalize_version_info(py_version_info) + else: + version_info = sys.version_info[:3] + self._version = Version(".".join(str(c) for c in version_info)) + + # We don't need to implement __eq__() and __ne__() since there is always + # only one RequiresPythonCandidate in a resolution, i.e. the host Python. + # The built-in object.__eq__() and object.__ne__() do exactly what we want. + + @property + def name(self): + # type: () -> str + # Avoid conflicting with the PyPI package "Python". + return "" + + @property + def version(self): + # type: () -> _BaseVersion + return self._version + + def format_for_error(self): + # type: () -> str + return "Python {}".format(self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + return () + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py new file mode 100644 index 0000000..dab23aa --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -0,0 +1,459 @@ +import collections +import logging + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import ( + DistributionNotFound, + InstallationError, + UnsupportedPythonVersion, + UnsupportedWheel, +) +from pip._internal.models.wheel import Wheel +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + dist_in_site_packages, + dist_in_usersite, + get_installed_distributions, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv + +from .candidates import ( + AlreadyInstalledCandidate, + EditableCandidate, + ExtrasCandidate, + LinkCandidate, + RequiresPythonCandidate, +) +from .requirements import ( + ExplicitRequirement, + RequiresPythonRequirement, + SpecifierRequirement, +) + +if MYPY_CHECK_RUNNING: + from typing import ( + FrozenSet, + Dict, + Iterable, + List, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + ) + + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + from pip._vendor.resolvelib import ResolutionImpossible + + from pip._internal.cache import CacheEntry, WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.resolution.base import InstallRequirementProvider + + from .base import Candidate, Requirement + from .candidates import BaseCandidate + + C = TypeVar("C") + Cache = Dict[Link, C] + VersionCandidates = Dict[_BaseVersion, Candidate] + + +logger = logging.getLogger(__name__) + + +class Factory(object): + def __init__( + self, + finder, # type: PackageFinder + preparer, # type: RequirementPreparer + make_install_req, # type: InstallRequirementProvider + wheel_cache, # type: Optional[WheelCache] + use_user_site, # type: bool + force_reinstall, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool + ): + # type: (...) -> None + self._finder = finder + self.preparer = preparer + self._wheel_cache = wheel_cache + self._python_candidate = RequiresPythonCandidate(py_version_info) + self._make_install_req_from_spec = make_install_req + self._use_user_site = use_user_site + self._force_reinstall = force_reinstall + self._ignore_requires_python = ignore_requires_python + self.use_lazy_wheel = lazy_wheel + + self._link_candidate_cache = {} # type: Cache[LinkCandidate] + self._editable_candidate_cache = {} # type: Cache[EditableCandidate] + + if not ignore_installed: + self._installed_dists = { + canonicalize_name(dist.project_name): dist + for dist in get_installed_distributions() + } + else: + self._installed_dists = {} + + @property + def force_reinstall(self): + # type: () -> bool + return self._force_reinstall + + def _make_candidate_from_dist( + self, + dist, # type: Distribution + extras, # type: FrozenSet[str] + template, # type: InstallRequirement + ): + # type: (...) -> Candidate + base = AlreadyInstalledCandidate(dist, template, factory=self) + if extras: + return ExtrasCandidate(base, extras) + return base + + def _make_candidate_from_link( + self, + link, # type: Link + extras, # type: FrozenSet[str] + template, # type: InstallRequirement + name, # type: Optional[str] + version, # type: Optional[_BaseVersion] + ): + # type: (...) -> Candidate + # TODO: Check already installed candidate, and use it if the link and + # editable flag match. + if template.editable: + if link not in self._editable_candidate_cache: + self._editable_candidate_cache[link] = EditableCandidate( + link, template, factory=self, name=name, version=version, + ) + base = self._editable_candidate_cache[link] # type: BaseCandidate + else: + if link not in self._link_candidate_cache: + self._link_candidate_cache[link] = LinkCandidate( + link, template, factory=self, name=name, version=version, + ) + base = self._link_candidate_cache[link] + if extras: + return ExtrasCandidate(base, extras) + return base + + def _iter_found_candidates( + self, + ireqs, # type: Sequence[InstallRequirement] + specifier, # type: SpecifierSet + ): + # type: (...) -> Iterable[Candidate] + if not ireqs: + return () + + # The InstallRequirement implementation requires us to give it a + # "template". Here we just choose the first requirement to represent + # all of them. + # Hopefully the Project model can correct this mismatch in the future. + template = ireqs[0] + name = canonicalize_name(template.req.name) + + hashes = Hashes() + extras = frozenset() # type: FrozenSet[str] + for ireq in ireqs: + specifier &= ireq.req.specifier + hashes |= ireq.hashes(trust_internet=False) + extras |= frozenset(ireq.extras) + + # We use this to ensure that we only yield a single candidate for + # each version (the finder's preferred one for that version). The + # requirement needs to return only one candidate per version, so we + # implement that logic here so that requirements using this helper + # don't all have to do the same thing later. + candidates = collections.OrderedDict() # type: VersionCandidates + + # Get the installed version, if it matches, unless the user + # specified `--force-reinstall`, when we want the version from + # the index instead. + installed_version = None + installed_candidate = None + if not self._force_reinstall and name in self._installed_dists: + installed_dist = self._installed_dists[name] + installed_version = installed_dist.parsed_version + if specifier.contains(installed_version, prereleases=True): + installed_candidate = self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + template=template, + ) + + found = self._finder.find_best_candidate( + project_name=name, + specifier=specifier, + hashes=hashes, + ) + for ican in found.iter_applicable(): + if ican.version == installed_version and installed_candidate: + candidate = installed_candidate + else: + candidate = self._make_candidate_from_link( + link=ican.link, + extras=extras, + template=template, + name=name, + version=ican.version, + ) + candidates[ican.version] = candidate + + # Yield the installed version even if it is not found on the index. + if installed_version and installed_candidate: + candidates[installed_version] = installed_candidate + + return six.itervalues(candidates) + + def find_candidates(self, requirements, constraint): + # type: (Sequence[Requirement], SpecifierSet) -> Iterable[Candidate] + explicit_candidates = set() # type: Set[Candidate] + ireqs = [] # type: List[InstallRequirement] + for req in requirements: + cand, ireq = req.get_candidate_lookup() + if cand is not None: + explicit_candidates.add(cand) + if ireq is not None: + ireqs.append(ireq) + + # If none of the requirements want an explicit candidate, we can ask + # the finder for candidates. + if not explicit_candidates: + return self._iter_found_candidates(ireqs, constraint) + + if constraint: + name = explicit_candidates.pop().name + raise InstallationError( + "Could not satisfy constraints for {!r}: installation from " + "path or url cannot be constrained to a version".format(name) + ) + + return ( + c for c in explicit_candidates + if all(req.is_satisfied_by(c) for req in requirements) + ) + + def make_requirement_from_install_req(self, ireq, requested_extras): + # type: (InstallRequirement, Iterable[str]) -> Optional[Requirement] + if not ireq.match_markers(requested_extras): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + ireq.name, ireq.markers, + ) + return None + if not ireq.link: + return SpecifierRequirement(ireq) + if ireq.link.is_wheel: + wheel = Wheel(ireq.link.filename) + if not wheel.supported(self._finder.target_python.get_tags()): + msg = "{} is not a supported wheel on this platform.".format( + wheel.filename, + ) + raise UnsupportedWheel(msg) + cand = self._make_candidate_from_link( + ireq.link, + extras=frozenset(ireq.extras), + template=ireq, + name=canonicalize_name(ireq.name) if ireq.name else None, + version=None, + ) + return self.make_requirement_from_candidate(cand) + + def make_requirement_from_candidate(self, candidate): + # type: (Candidate) -> ExplicitRequirement + return ExplicitRequirement(candidate) + + def make_requirement_from_spec( + self, + specifier, # type: str + comes_from, # type: InstallRequirement + requested_extras=(), # type: Iterable[str] + ): + # type: (...) -> Optional[Requirement] + ireq = self._make_install_req_from_spec(specifier, comes_from) + return self.make_requirement_from_install_req(ireq, requested_extras) + + def make_requires_python_requirement(self, specifier): + # type: (Optional[SpecifierSet]) -> Optional[Requirement] + if self._ignore_requires_python or specifier is None: + return None + return RequiresPythonRequirement(specifier, self._python_candidate) + + def get_wheel_cache_entry(self, link, name): + # type: (Link, Optional[str]) -> Optional[CacheEntry] + """Look up the link in the wheel cache. + + If ``preparer.require_hashes`` is True, don't use the wheel cache, + because cached wheels, always built locally, have different hashes + than the files downloaded from the index server and thus throw false + hash mismatches. Furthermore, cached wheels at present have + nondeterministic contents due to file modification times. + """ + if self._wheel_cache is None or self.preparer.require_hashes: + return None + return self._wheel_cache.get_cache_entry( + link=link, + package_name=name, + supported_tags=get_supported(), + ) + + def get_dist_to_uninstall(self, candidate): + # type: (Candidate) -> Optional[Distribution] + # TODO: Are there more cases this needs to return True? Editable? + dist = self._installed_dists.get(candidate.name) + if dist is None: # Not installed, no uninstallation required. + return None + + # We're installing into global site. The current installation must + # be uninstalled, no matter it's in global or user site, because the + # user site installation has precedence over global. + if not self._use_user_site: + return dist + + # We're installing into user site. Remove the user site installation. + if dist_in_usersite(dist): + return dist + + # We're installing into user site, but the installed incompatible + # package is in global site. We can't uninstall that, and would let + # the new user installation to "shadow" it. But shadowing won't work + # in virtual environments, so we error out. + if running_under_virtualenv() and dist_in_site_packages(dist): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to {} in {}".format( + dist.project_name, dist.location, + ) + ) + return None + + def _report_requires_python_error( + self, + requirement, # type: RequiresPythonRequirement + template, # type: Candidate + ): + # type: (...) -> UnsupportedPythonVersion + message_format = ( + "Package {package!r} requires a different Python: " + "{version} not in {specifier!r}" + ) + message = message_format.format( + package=template.name, + version=self._python_candidate.version, + specifier=str(requirement.specifier), + ) + return UnsupportedPythonVersion(message) + + def get_installation_error(self, e): + # type: (ResolutionImpossible) -> InstallationError + + assert e.causes, "Installation error reported with no cause" + + # If one of the things we can't solve is "we need Python X.Y", + # that is what we report. + for cause in e.causes: + if isinstance(cause.requirement, RequiresPythonRequirement): + return self._report_requires_python_error( + cause.requirement, + cause.parent, + ) + + # Otherwise, we have a set of causes which can't all be satisfied + # at once. + + # The simplest case is when we have *one* cause that can't be + # satisfied. We just report that case. + if len(e.causes) == 1: + req, parent = e.causes[0] + if parent is None: + req_disp = str(req) + else: + req_disp = '{} (from {})'.format(req, parent.name) + logger.critical( + "Could not find a version that satisfies the requirement %s", + req_disp, + ) + return DistributionNotFound( + 'No matching distribution found for {}'.format(req) + ) + + # OK, we now have a list of requirements that can't all be + # satisfied at once. + + # A couple of formatting helpers + def text_join(parts): + # type: (List[str]) -> str + if len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def readable_form(cand): + # type: (Candidate) -> str + return "{} {}".format(cand.name, cand.version) + + def describe_trigger(parent): + # type: (Candidate) -> str + ireq = parent.get_install_requirement() + if not ireq or not ireq.comes_from: + return "{} {}".format(parent.name, parent.version) + if isinstance(ireq.comes_from, InstallRequirement): + return str(ireq.comes_from.name) + return str(ireq.comes_from) + + triggers = [] + for req, parent in e.causes: + if parent is None: + # This is a root requirement, so we can report it directly + trigger = req.format_for_error() + else: + trigger = describe_trigger(parent) + triggers.append(trigger) + + if triggers: + info = text_join(triggers) + else: + info = "the requested packages" + + msg = "Cannot install {} because these package versions " \ + "have conflicting dependencies.".format(info) + logger.critical(msg) + msg = "\nThe conflict is caused by:" + for req, parent in e.causes: + msg = msg + "\n " + if parent: + msg = msg + "{} {} depends on ".format( + parent.name, + parent.version + ) + else: + msg = msg + "The user requested " + msg = msg + req.format_for_error() + + msg = msg + "\n\n" + \ + "To fix this you could try to:\n" + \ + "1. loosen the range of package versions you've specified\n" + \ + "2. remove package versions to allow pip attempt to solve " + \ + "the dependency conflict\n" + + logger.info(msg) + + return DistributionNotFound( + "ResolutionImpossible: for help visit " + "https://pip.pypa.io/en/latest/user_guide/" + "#fixing-conflicting-dependencies" + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py new file mode 100644 index 0000000..b2eb9d0 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -0,0 +1,153 @@ +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.resolvelib.providers import AbstractProvider + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, + Dict, + Iterable, + Optional, + Sequence, + Set, + Tuple, + Union, + ) + + from .base import Requirement, Candidate + from .factory import Factory + +# Notes on the relationship between the provider, the factory, and the +# candidate and requirement classes. +# +# The provider is a direct implementation of the resolvelib class. Its role +# is to deliver the API that resolvelib expects. +# +# Rather than work with completely abstract "requirement" and "candidate" +# concepts as resolvelib does, pip has concrete classes implementing these two +# ideas. The API of Requirement and Candidate objects are defined in the base +# classes, but essentially map fairly directly to the equivalent provider +# methods. In particular, `find_matches` and `is_satisfied_by` are +# requirement methods, and `get_dependencies` is a candidate method. +# +# The factory is the interface to pip's internal mechanisms. It is stateless, +# and is created by the resolver and held as a property of the provider. It is +# responsible for creating Requirement and Candidate objects, and provides +# services to those objects (access to pip's finder and preparer). + + +class PipProvider(AbstractProvider): + def __init__( + self, + factory, # type: Factory + constraints, # type: Dict[str, SpecifierSet] + ignore_dependencies, # type: bool + upgrade_strategy, # type: str + user_requested, # type: Set[str] + ): + # type: (...) -> None + self._factory = factory + self._constraints = constraints + self._ignore_dependencies = ignore_dependencies + self._upgrade_strategy = upgrade_strategy + self.user_requested = user_requested + + def _sort_matches(self, matches): + # type: (Iterable[Candidate]) -> Sequence[Candidate] + + # The requirement is responsible for returning a sequence of potential + # candidates, one per version. The provider handles the logic of + # deciding the order in which these candidates should be passed to + # the resolver. + + # The `matches` argument is a sequence of candidates, one per version, + # which are potential options to be installed. The requirement will + # have already sorted out whether to give us an already-installed + # candidate or a version from PyPI (i.e., it will deal with options + # like --force-reinstall and --ignore-installed). + + # We now work out the correct order. + # + # 1. If no other considerations apply, later versions take priority. + # 2. An already installed distribution is preferred over any other, + # unless the user has requested an upgrade. + # Upgrades are allowed when: + # * The --upgrade flag is set, and + # - The project was specified on the command line, or + # - The project is a dependency and the "eager" upgrade strategy + # was requested. + def _eligible_for_upgrade(name): + # type: (str) -> bool + """Are upgrades allowed for this project? + + This checks the upgrade strategy, and whether the project was one + that the user specified in the command line, in order to decide + whether we should upgrade if there's a newer version available. + + (Note that we don't need access to the `--upgrade` flag, because + an upgrade strategy of "to-satisfy-only" means that `--upgrade` + was not specified). + """ + if self._upgrade_strategy == "eager": + return True + elif self._upgrade_strategy == "only-if-needed": + return (name in self.user_requested) + return False + + def sort_key(c): + # type: (Candidate) -> int + """Return a sort key for the matches. + + The highest priority should be given to installed candidates that + are not eligible for upgrade. We use the integer value in the first + part of the key to sort these before other candidates. + + We only pull the installed candidate to the bottom (i.e. most + preferred), but otherwise keep the ordering returned by the + requirement. The requirement is responsible for returning a list + otherwise sorted for the resolver, taking account for versions + and binary preferences as specified by the user. + """ + if c.is_installed and not _eligible_for_upgrade(c.name): + return 1 + return 0 + + return sorted(matches, key=sort_key) + + def identify(self, dependency): + # type: (Union[Requirement, Candidate]) -> str + return dependency.name + + def get_preference( + self, + resolution, # type: Optional[Candidate] + candidates, # type: Sequence[Candidate] + information # type: Sequence[Tuple[Requirement, Candidate]] + ): + # type: (...) -> Any + # Use the "usual" value for now + return len(candidates) + + def find_matches(self, requirements): + # type: (Sequence[Requirement]) -> Iterable[Candidate] + if not requirements: + return [] + constraint = self._constraints.get( + requirements[0].name, SpecifierSet(), + ) + candidates = self._factory.find_candidates(requirements, constraint) + return reversed(self._sort_matches(candidates)) + + def is_satisfied_by(self, requirement, candidate): + # type: (Requirement, Candidate) -> bool + return requirement.is_satisfied_by(candidate) + + def get_dependencies(self, candidate): + # type: (Candidate) -> Sequence[Requirement] + with_requires = not self._ignore_dependencies + return [ + r + for r in candidate.iter_dependencies(with_requires) + if r is not None + ] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py new file mode 100644 index 0000000..bc1061f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -0,0 +1,137 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Requirement, format_name + +if MYPY_CHECK_RUNNING: + from pip._vendor.packaging.specifiers import SpecifierSet + + from pip._internal.req.req_install import InstallRequirement + + from .base import Candidate, CandidateLookup + + +class ExplicitRequirement(Requirement): + def __init__(self, candidate): + # type: (Candidate) -> None + self.candidate = candidate + + def __repr__(self): + # type: () -> str + return "{class_name}({candidate!r})".format( + class_name=self.__class__.__name__, + candidate=self.candidate, + ) + + @property + def name(self): + # type: () -> str + # No need to canonicalise - the candidate did this + return self.candidate.name + + def format_for_error(self): + # type: () -> str + return self.candidate.format_for_error() + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return self.candidate, None + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return candidate == self.candidate + + +class SpecifierRequirement(Requirement): + def __init__(self, ireq): + # type: (InstallRequirement) -> None + assert ireq.link is None, "This is a link, not a specifier" + self._ireq = ireq + self._extras = frozenset(ireq.extras) + + def __str__(self): + # type: () -> str + return str(self._ireq.req) + + def __repr__(self): + # type: () -> str + return "{class_name}({requirement!r})".format( + class_name=self.__class__.__name__, + requirement=str(self._ireq.req), + ) + + @property + def name(self): + # type: () -> str + canonical_name = canonicalize_name(self._ireq.req.name) + return format_name(canonical_name, self._extras) + + def format_for_error(self): + # type: () -> str + + # Convert comma-separated specifiers into "A, B, ..., F and G" + # This makes the specifier a bit more "human readable", without + # risking a change in meaning. (Hopefully! Not all edge cases have + # been checked) + parts = [s.strip() for s in str(self).split(",")] + if len(parts) == 0: + return "" + elif len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return None, self._ireq + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self.name, \ + "Internal issue: Candidate is not for this requirement " \ + " {} vs {}".format(candidate.name, self.name) + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + spec = self._ireq.req.specifier + return spec.contains(candidate.version, prereleases=True) + + +class RequiresPythonRequirement(Requirement): + """A requirement representing Requires-Python metadata. + """ + def __init__(self, specifier, match): + # type: (SpecifierSet, Candidate) -> None + self.specifier = specifier + self._candidate = match + + def __repr__(self): + # type: () -> str + return "{class_name}({specifier!r})".format( + class_name=self.__class__.__name__, + specifier=str(self.specifier), + ) + + @property + def name(self): + # type: () -> str + return self._candidate.name + + def format_for_error(self): + # type: () -> str + return "Python " + str(self.specifier) + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + if self.specifier.contains(self._candidate.version, prereleases=True): + return self._candidate, None + return None, None + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self._candidate.name, "Not Python candidate" + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py new file mode 100644 index 0000000..aecddb1 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -0,0 +1,259 @@ +import functools +import logging + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible +from pip._vendor.resolvelib import Resolver as RLResolver + +from pip._internal.exceptions import InstallationError +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.resolvelib.provider import PipProvider +from pip._internal.utils.misc import dist_is_editable +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .factory import Factory + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional, Set, Tuple + + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.resolvelib.resolvers import Result + from pip._vendor.resolvelib.structs import Graph + + from pip._internal.cache import WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + +logger = logging.getLogger(__name__) + + +class Resolver(BaseResolver): + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool + ): + super(Resolver, self).__init__() + if lazy_wheel: + logger.warning( + 'pip is using lazily downloaded wheels using HTTP ' + 'range requests to obtain dependency information. ' + 'This experimental feature is enabled through ' + '--use-feature=fast-deps and it is not ready for production.' + ) + + assert upgrade_strategy in self._allowed_strategies + + self.factory = Factory( + finder=finder, + preparer=preparer, + make_install_req=make_install_req, + wheel_cache=wheel_cache, + use_user_site=use_user_site, + force_reinstall=force_reinstall, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + py_version_info=py_version_info, + lazy_wheel=lazy_wheel, + ) + self.ignore_dependencies = ignore_dependencies + self.upgrade_strategy = upgrade_strategy + self._result = None # type: Optional[Result] + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + + constraints = {} # type: Dict[str, SpecifierSet] + user_requested = set() # type: Set[str] + requirements = [] + for req in root_reqs: + if req.constraint: + # Ensure we only accept valid constraints + problem = check_invalid_constraint_type(req) + if problem: + raise InstallationError(problem) + if not req.match_markers(): + continue + name = canonicalize_name(req.name) + if name in constraints: + constraints[name] = constraints[name] & req.specifier + else: + constraints[name] = req.specifier + else: + if req.user_supplied and req.name: + user_requested.add(canonicalize_name(req.name)) + r = self.factory.make_requirement_from_install_req( + req, requested_extras=(), + ) + if r is not None: + requirements.append(r) + + provider = PipProvider( + factory=self.factory, + constraints=constraints, + ignore_dependencies=self.ignore_dependencies, + upgrade_strategy=self.upgrade_strategy, + user_requested=user_requested, + ) + reporter = BaseReporter() + resolver = RLResolver(provider, reporter) + + try: + try_to_avoid_resolution_too_deep = 2000000 + self._result = resolver.resolve( + requirements, max_rounds=try_to_avoid_resolution_too_deep, + ) + + except ResolutionImpossible as e: + error = self.factory.get_installation_error(e) + six.raise_from(error, e) + + req_set = RequirementSet(check_supported_wheels=check_supported_wheels) + for candidate in self._result.mapping.values(): + ireq = candidate.get_install_requirement() + if ireq is None: + continue + + # Check if there is already an installation under the same name, + # and set a flag for later stages to uninstall it, if needed. + # * There isn't, good -- no uninstalltion needed. + # * The --force-reinstall flag is set. Always reinstall. + # * The installation is different in version or editable-ness, so + # we need to uninstall it to install the new distribution. + # * The installed version is the same as the pending distribution. + # Skip this distrubiton altogether to save work. + installed_dist = self.factory.get_dist_to_uninstall(candidate) + if installed_dist is None: + ireq.should_reinstall = False + elif self.factory.force_reinstall: + ireq.should_reinstall = True + elif installed_dist.parsed_version != candidate.version: + ireq.should_reinstall = True + elif dist_is_editable(installed_dist) != candidate.is_editable: + ireq.should_reinstall = True + else: + continue + + link = candidate.source_link + if link and link.is_yanked: + # The reason can contain non-ASCII characters, Unicode + # is required for Python 2. + msg = ( + u'The candidate selected for download or install is a ' + u'yanked version: {name!r} candidate (version {version} ' + u'at {link})\nReason for being yanked: {reason}' + ).format( + name=candidate.name, + version=candidate.version, + link=link, + reason=link.yanked_reason or u'', + ) + logger.warning(msg) + + req_set.add_named_requirement(ireq) + + return req_set + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """Get order for installation of requirements in RequirementSet. + + The returned list contains a requirement before another that depends on + it. This helps ensure that the environment is kept consistent as they + get installed one-by-one. + + The current implementation creates a topological ordering of the + dependency graph, while breaking any cycles in the graph at arbitrary + points. We make no guarantees about where the cycle would be broken, + other than they would be broken. + """ + assert self._result is not None, "must call resolve() first" + + graph = self._result.graph + weights = get_topological_weights(graph) + + sorted_items = sorted( + req_set.requirements.items(), + key=functools.partial(_req_set_item_sorter, weights=weights), + reverse=True, + ) + return [ireq for _, ireq in sorted_items] + + +def get_topological_weights(graph): + # type: (Graph) -> Dict[Optional[str], int] + """Assign weights to each node based on how "deep" they are. + + This implementation may change at any point in the future without prior + notice. + + We take the length for the longest path to any node from root, ignoring any + paths that contain a single node twice (i.e. cycles). This is done through + a depth-first search through the graph, while keeping track of the path to + the node. + + Cycles in the graph result would result in node being revisited while also + being it's own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + """ + path = set() # type: Set[Optional[str]] + weights = {} # type: Dict[Optional[str], int] + + def visit(node): + # type: (Optional[str]) -> None + if node in path: + # We hit a cycle, so we'll break it here. + return + + # Time to visit the children! + path.add(node) + for child in graph.iter_children(node): + visit(child) + path.remove(node) + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + + # Sanity checks + assert weights[None] == 0 + assert len(weights) == len(graph) + + return weights + + +def _req_set_item_sorter( + item, # type: Tuple[str, InstallRequirement] + weights, # type: Dict[Optional[str], int] +): + # type: (...) -> Tuple[int, str] + """Key function used to sort install requirements for installation. + + Based on the "weight" mapping calculated in ``get_installation_order()``. + The canonical package name is returned as the second member as a tie- + breaker to ensure the result is predictable, which is useful in tests. + """ + name = canonicalize_name(item[0]) + return weights[name], name diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py new file mode 100644 index 0000000..fbd9dfd --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py @@ -0,0 +1,205 @@ +from __future__ import absolute_import + +import datetime +import hashlib +import json +import logging +import os.path +import sys + +from pip._vendor.packaging import version as packaging_version +from pip._vendor.six import ensure_binary + +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.utils.filesystem import ( + adjacent_tmp_file, + check_path_owner, + replace, +) +from pip._internal.utils.misc import ( + ensure_dir, + get_distribution, + get_installed_version, +) +from pip._internal.utils.packaging import get_installer +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + import optparse + from typing import Any, Dict, Text, Union + + from pip._internal.network.session import PipSession + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +def _get_statefile_name(key): + # type: (Union[str, Text]) -> str + key_bytes = ensure_binary(key) + name = hashlib.sha224(key_bytes).hexdigest() + return name + + +class SelfCheckState(object): + def __init__(self, cache_dir): + # type: (str) -> None + self.state = {} # type: Dict[str, Any] + self.statefile_path = None + + # Try to load the existing state + if cache_dir: + self.statefile_path = os.path.join( + cache_dir, "selfcheck", _get_statefile_name(self.key) + ) + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + @property + def key(self): + # type: () -> str + return sys.prefix + + def save(self, pypi_version, current_time): + # type: (str, datetime.datetime) -> None + # If we do not have a path to cache in, don't bother saving. + if not self.statefile_path: + return + + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + state = { + # Include the key so it's easy to tell which pip wrote the + # file. + "key": self.key, + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + text = json.dumps(state, sort_keys=True, separators=(",", ":")) + + with adjacent_tmp_file(self.statefile_path) as f: + f.write(ensure_binary(text)) + + try: + # Since we have a prefix-specific state file, we can just + # overwrite whatever is there, no need to check. + replace(f.name, self.statefile_path) + except OSError: + # Best effort. + pass + + +def was_installed_by_pip(pkg): + # type: (str) -> bool + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + dist = get_distribution(pkg) + if not dist: + return False + return "pip" == get_installer(dist) + + +def pip_self_version_check(session, options): + # type: (PipSession, optparse.Values) -> None + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if not installed_version: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = SelfCheckState(cache_dir=options.cache_dir) + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + # Lets use PackageFinder to see what the latest pip version is + link_collector = LinkCollector.create( + session, + options=options, + suppress_no_index=True, + ) + + # Pass allow_yanked=False so we don't suggest upgrading to a + # yanked version. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=False, # Explicitly set to False + ) + + finder = PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + best_candidate = finder.find_best_candidate("pip").best_candidate + if best_candidate is None: + return + pypi_version = str(best_candidate.version) + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + local_version_is_older = ( + pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip') + ) + + # Determine if our pypi_version is older + if not local_version_is_older: + return + + # We cannot tell how the current pip is available in the current + # command context, so be pragmatic here and suggest the command + # that's always available. This does not accommodate spaces in + # `sys.executable`. + pip_cmd = "{} -m pip".format(sys.executable) + logger.warning( + "You are using pip version %s; however, version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0b90a359640ad2ea959d6a444867172ed4ff0b1 GIT binary patch literal 227 zcmYk0F$%&k7==@C5TOTgNdLh}5OHvJ5$Eum{J|j6Of_Z@Nun5a&KIm*wF)ZVG%sdstW851Qdu1|DaBnE% zx88hT9>_%Qzh(209LX_S#xi)}O%8AWO3s(s^4v(pZ$Q+RyQSjSs*(njb3G*&Dy@&bfxL{Y3y_?1$%t`=Qnr>wKQ7S^65qLN%;#;)_@5=@Og z7*$+41sp%-+hCX`&zGWFC^=33M0mXkYY)YRHAUl8rpwxZo1tyEtsBMT?_d4!Dm#1r z^z7U0$CtC&H?yzN8h)p(%Nd2(4w6HhXE^NvF3wxAP1n44K0+PLp5b?U7+5rJX<*{F zakf3CJ!q_gEEoAwWr+7SgG!X@cMpgzfB%}kL>HS<`eI=!DbgnThMlV8$lZmLVYNGCB2 z$>*>~wKEc|?ZB?BQ)TNL+lENiWgBTr8wtXOG$Q{tTp3+W0@%Y4FrwuiE!2g6*(ZrQ zd>^MJ56?ehpRy4P*#YZz{>Kc$=07|hpt8e5A_|BH>+lG#8Juj``@n^(=#I%~!9Md| zcpJ80SovV~?0i@HUfC2*Q~JoE2y1`%0LB>hHcXYvCxdPA)YYsNzo c{AK^c`igF)J;o)BM}F*&@Cn%{h@)imFSjUoM*si- literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7773d7fe4ce8638dbccb37d2bf4f3b5592956597 GIT binary patch literal 6052 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
      no>YIt#GtpX!l{R{f)b817v)4?W zo|De?=2Exkrt`gdy|$4oq^(|y8Bt3l*kdP)>9O9i^my-hdZKqiuW3TgQf~=s%!B37 zIb^+);aupxGI}fFe7J!2RM-j^(XNKa!sBR9hbO`%v}eNAa5+5riuKNhr^A)-6ngK3 z=0l@>=EcURQ5K0nMqZGG-t%1Sc+X?G?cM+6zIXM?``(>AEdm*D#z`#qyhp)S*>PXv zz($*?W5H&bCqo(eaa!a;cH2#5Vu?D|AMZ*cUt)_|=ofo(JI}_WEz%kH>(TKzlaVMy z1nOr&8mZ-J&a+6Aah|DG8ua}l3FIIbX$kF1pFh6;*uQi4lRLliAAHg6-s^taHkI{R zTuSBqx{%luB&zXfSR@gax9)2Tep8Ekv2vmFZri``Bz!}`=rcOn_@jOk|2z1Xr$It? zU`Qr;1$T%1cl?a8&kq@3G`q@p&9Q;3H@3%2qs4Awu+`@=NKB$#=${#9@Z&829!08}*_7)GV^3z0vK`f|Mq)#xLm<4-`Pq6I%ji-O^ml@-s9Z1NVjZf1F7Q|%$~YkfQ(o0>=oEWuW!g|*=6;rk zxjaY5r^6YhSOuNIi149AF!3)RfJozjRmLG3v5JYi6+7UE#uMw0#)yy1ktNNF?=w(y zpAGn^@q1&yC~aOj(yAJjwa-5`_SwkJ?8?|eTiF`j8I@#;ga0zAJP)#ERK{=5jaX1c61k-MT=E>CkqQfA?a|p9##)%_Q#!NqGyM$%b}AwS3Y^vVEkJ zYCvm$TiIJ89~PxD;T|yuiS4E!xWx}aXwlPLpoudy^Fdz@gG9?v^Q3c<88(TH%G%f; z&~|O{;;dn7m?)P(4DK?E9mmgYI{$R$Z6?~_&S2aCgHEVJ|0z1!9dPUclHia%F>7~L zJmfoEd{$YZ`OFXlxEuAZGFOetT15~rcmtfqG$%WYT^4rLt7qmaN+!Fjm`~}R?Im$G z+`V%7y{os#>B`={e$BsjRax=1>(^#iTvqPUhWFsgxgroUrW&B^peUj&Y&U9X$$J1( zdqPBmcvm@kP)5a=T@D9e*Kks<)^NV#_lKp-Q=mFF>`gWLDOQcKwSlW+J8X%aWKLs= ziT5%0U)WN*Xl!qzGleQ6Tro!U9x`#BN)6J%%1&3fPk`_>q)yGL6U3@0wym!C3FIid z4(&D#}XGL;wq$Kip)h1d8|t-3dBQ{+sWJj zu@M@&9K0rRf(VX!mij-UK2yzcO1y3TvbMf1kdw4|j4h=NV(YpzvuHLr=4HdZz!JWBT9A#i>W$)U)+z@w7s|d`eqJBJxF&}5HhwsbrO!;NU)r=b`06{w0a09Y&8OPbygmaP9 z$nA(^r{3De#+nBuYa3VAF6#Xx6pU`3MIG-E)=gADkCVjPjJ%*Maa+T{d}6Ja4~6&P zNB26@22670#@OaPtu)$2HTNcS-d;ZRo(Gxqo&`x99_gQ)9kxy^xi`=zUG#=!q$TOw z#w+(q8Kp-8XDpzA`#$Znf%w1MFcqjC$(}JH*o?o=2C)CXiXpJp+f}?@;5oK9oAtQg zH{%l5!_h<1RP$4oi`&Q;@gWG3_XFyGbi{CUjnEmVE1XVlWk< zGg~7D^cLgw@88yUwlh7lnlmFu+=C<(uP!bWeC>0p>HA^c_kHF1{$Pk4jeK8FT~u5| z>MUNC9hB27%tfbuZX&@t6t%*{A`Qe8E^lffJ$1(0HTHOzcJ#xvBlil(5LCpJJs!-H ztOX)1B6Q!VCcVVsBoZF6=vn&aTC1Hy^}O~K9cv26H&?oIGtI+c65XP!r2H|6WiD`s cpLZ7k;7Pu~T@!ctigU_Ya2(e)nErkHA5n{h($ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7194f9ed39576f0516e13ce230c2519efd6efac2 GIT binary patch literal 548 zcmYjPy>1jS5VqI5;W!c~c!4dDqATuPP$40N1WK6((OhFW>*3b9U3;--6Ao=U3SNOO zDS0KgRJ;OdGCmS1Bh8;@JoC-i`sirRh<C73s_$h+|Cx**U*1ZrGJTcFiX?@u|2GSLGz8a$6D* ze!388mV4zssmxZY&>Ov5RH@v74yo$B!!`*z3%Po#HueJCZx-@e{{57nBZMfMRy)1% z&S?43H)9Vpl59N4uX;&p3Eu_?)FdAdJA{a82>u)zymy!2E@WrVvfFz1di6#|i?AGY zv(+1*TXvAjI)vbKCr7khln>BX!!Ett<5o7Rj>ZTI3DHx67!OFWV$Poz;gBqv?1@sw tH%g_sQeQ?K11J^pJBGZ^eb#=uN8%X;((G;T&Dg;^`Un5(mg2COi96zll?ea< literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91b9e354f04b8cb5c763d5194944fb53842a13b5 GIT binary patch literal 2874 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84ada5a5eab36447a3b9961722af65f3d0b739cb GIT binary patch literal 1167 zcmZ`3OK;OK*v_L(yKd^nv;zl}Yb4qr!2u9MOzR#9s3u@St3Z*Lcxkr8$!uq=Rm%mg zaOVcx_)EER;xE93&*}O&j4$$Me-D3ehb%AG5y028zhu`$=$9fEK?C9mEb$spfC3z& z1otpjXD!wey{DIFJvI`vXJSN6ejj8;TuG|EYGU;)j6xKcKX9)`bz1v~d-b3aR6n8K zQeeG8PVF~XMh-4kJCTsNjAF6H!yV334$P%p!u-J=m1!n{TzheJaO7?8?`%KwULJP4 zPrG}LRv4Ygmxb{h?mG=fA2#SE^;;RIC(-3*w?M!$`)NWqyS%D=>BHrS2}$C(bxg!{ z?0`rz*KSU=`;-Kfi@BsRmBi8UoIBIcVj}+|$vM4VB77cYa=}M+n#eSGlc%Yic`J-u zP(I1b$EucB0PRFISlh6~KA;GV@d$O$yN6?KxFOLg9%&&C@Mrv18|!1^4Pt8slG@-N zM(R64=hhuG!r%0facX>rrk`6Q3@y`!9}Wl?)XsQ%5d}1`2@msxGAVA`oXVWDh=n#` zc6xkDeQC=+k#?U5IMBKckPzwj7w@&}C4Y+z?3|sk^gWx~gcG%+kfodk?NSXU!2p-S zF^g3%n2wDXmSa*>COcQNTU7E4nn?@G14UnYo?~)Vm7>C_$YWXPV5q=eVU{udM0^dT zf4;bfP*vgjeZokXvVgdq)Su+&`YCnKNei|Xz04?&%GBL#AgPcZ6h$R{MYbrraG}$R z?5FH*`++MWNn085&qzpxn?;%HMNCr8NbHvL@6J)7L9}?mZK;mW}xp3gmnJcH9IROWFvzu5xbgX^9_j~W{yr199h8r6$g7xd^zoBI# z^rt54s|@6Gc*HMY2qKuFpgPYLj$l%>>O*v$#H{}a=byxIle(EIc^enXpBn1o77lrk?Il$H%X0b{D6XOf)7yr z#wBP&y*gG~kDfhw)^C5=ZGY2$derNE)%&7uDr-L$QrU;oj8SDB(ldE(Pvx-j;M~!0 z@BVqEz{Y^CTt6bCX!a)|@z}2Jc9F;ml3gYvo;7bBo|97H40h@9^AOl$MBCdUsM<7TwqT zbfAwZ%ZmyrFLCy}bQjenTIvB;&VO!?X>{s^-e4xF@Py>>zrA6~A@uO^BTuGYlyXiZ z$z~p*5*$yCsYjDyM6T<>+6x&Qgs=~kvos1BZH}nSI2}@Wms}U`K%Jmr8ar+J!OX?isQX{O<5r8K)FJOT#8q*94nL2L^*_l zBC2eWu~@2Vx7U8$fosx#+EtFAA&|-u1HGE7Ud_B$Q_eEDGF+dUa;}I8gj?F^FGQ8$$ofZzdk<9Q%DZ=8)mCo* zVRxPFcUNVt-BqHsyIuxtzssw7^HP*fvY1i6srzaD`3bCOEWB-Au_E@j<+|qoS7dg$ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb66b0a7a44e8edb145abf3843e724f208647da8 GIT binary patch literal 1371 zcmZ8h&yV9Y7){blr<559A;bZ3_-?C8ZE}D;%nBiP2gD$tjFwrU?WJ;Kr}d5#8{26& z5(gx>@fUD|BmYvboVap?13V`I6)eT^&-Xol@7e6~avGuhc>gE+HjScRL+7-N(D?wL zdxDBbJXX;v{1X*d$tsCMd!$BHx=Q0nr1o9(Q*~C2SL5n@b&mOSe#Xb&$EyjCzl>&+ z-+&V>XYnzun5^-C{qyIy&)3&CA6={ABs@EICfTFkb4Ksbl(?biRngwxNV} z;B}49y@AR{FXEpP9{(K2kso~%eHS7n>~}1@7PYrb(ZMofgr!njVoH%#oWfLVw5+|$ zrc*+>K`3oyffX$oX=ukBK2A>TMSwZkLR^%x7CfUTqF{{^q)U>{HNtgR%t|NgEtOVR zbo==3W=^hknPGl6r>aq28YSqq^*dd=Ir*KCmcRvZig0v{v6@q*o$v5~jx`nN>;o+8 z_{rLNkQIDRU|=1&rYv#HK({3?D*=!b*0dGAr&sAcxcA4t(;+8fFPRqzA{`lM)5kYf z?eu{j0}MgOMKfNpiFifs?HF`d)C_ zw1HNzfGeyt{vXtg?!w^@5(X)mRM>!XmyK_%AY{}*mY_16kxg%vU=ZC2K|Ak_dz9x} z>37X0D|D5c*03Dw^4<~o!-rQ_z4`cFA_D*_<;x*a)YM#9)pi_%gR*ImWUnP3sj3M{weC>^!2{`2C&iDt8FFFs!&|TY+6j)w**(?tI>Osa$??*)r1}C+k7{ zK+R21MVI3^`752+3$$nPa`uWHqjh`}XxYDLS)pVW9-Hon$G84F*@en{Ymj_93cJTc p2hU+-&q5X8yM&xnuZE?Q_bSaBCEgEDk_)^h$whpTr15K`zX6)Zsfqvq literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..870a18892775ae370ec19986c03a5f0cc87ce705 GIT binary patch literal 5708 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b34dc49f208a2192df6c153083d00d2e0d925452 GIT binary patch literal 4546 zcmbVQNpsuC748N=5TqzdmSx$pJt<_H2~!c}C6jm@$Faxu*ow!r<85NpxL}BAk+85( z4N$Te>X4BuGndp<<(5lI${|($7x@MG0l4Orb8g8Y`Cfz6Hg=^dP@&n&>+QYoeeXfu z>RPBd8h-Uqj||6%N=_*<9aD`N zVL7rKE2=n^sOnTzUn!i3YEDhn&2TcRJ9W-PUH%Dcl*6fL+L?}yIme{S(d!$exgYfNUkyqB&=dv$%#<6|Ixh@+X{A9p=~m%$@n$%%Ak@ubE#Lr?++I z4BAuvG}_bR4BBUX?GbAn`v>mC8a$u!meVBcXTlAlULvzrL(eNgCM4b{%`K{~ip?~y zQf=J}`yz!%?T(jr?*(b(W$kW0F>0kj+{uqE#cAe+p_c_od`rqiVw(vmJl}11`|)au z=FGik51zR0n3X%af6~`|<282-zvP>*nNt#`U-qrnY@0h}zv5TXXZaI;4ZoE_;ZC(sv@_u! z^Jmas^N;(p_?`4m_;dKJE7hOMO**qk2i|piLwdcQkapJfGJClngnnvA{V)r9p|I&j zBDFD+#BE`Fv2VA#qP?0zTtv3l@dBv2YU5drlZ|+xlO(=f*r=+yTWt*&8$VR1_Xgf{ z4#klByw7AUV6isjL*@eH0Eae6)NES#a?g`qG&;&<`?~GJMs~7ddtoO5NV<_7dl63A zOM*Cq{p~EVLs&2^gu-M)_~)gZ_ZDv^?S3TU44sKsT=N$2vmjnUqe0Q#+sv?$+mp#l(asi& z=7NaV7sFtgn%yM6*!*ZQ1-ch{UVGKU!4`W#Z_&kdgp9p#v7ZHDx>%g0+1tcHF&1R5 z+aNtE1)GDZ!lupf*dfK2AXvex6z`*wLG>Bm*3x`F z^traBZyAHxp*GY@Y@Q8`pE8|YVV|+J?-(pIH-F6;{S6J0xWb;H{~X?o54CNW@eOY^ z%5YmaCHxTpl4SC{-b%8i2=F2W316P1PNSXlHYIsOZrv4|h1ZtkDe5RCK%30)zDjqCX)W?;3>Td)a-eEi<5@8`BUg;4Ltpb$@ef0l} z0Wht3E-#Nzc=RHbD;*I7MDpr2%=sr?Hh6z@@?whHPWo}yyb>mDFHEmC_a=ZD^|T); zrONMP;v24%cyx=}!F7HPxdKM|;P_kiY18$_57h}yN>8L_7@xglJABAHY*$;yRU3S; zSTLr&(G_Go;tRVItc&=7_t^ocrSQ0BLo0BgcSHv6RCcd)K|d}zcV30hy2X)?-ef9B zT_reL>SG+KxJ-F4H@eIbkwdOghCGE~b)UbW5|A4xt1CCSvZW7ZGHpj6>bn3o0UZud zhXWW{X+_(-Q#e6p`h2HMGTZzM4p*qd73xEBq_bN_W|2#vUf(JWOFNaJvC9FB&(|Jg z)zOHp+|3-CI}^KV_n|(|)|p&KaD|Jmtq<8wZK%;a#p3$liyOqnw&GmU05NsJ$e?R9 zh|M#5MJAC=q0?Iiqk}h#7>L>)`T1VvH4DB5J=zbwAQd*LZX93jV8xEHrmdpw0nx#U zC$VCF#m|a}FWbM_YBeS$`BGuiG93NC}w6=6mv(~8f67{ zF*W@b3T7F+4$hbXV`$v^PtCBv9(8RRJr*CFAY+lArsf#!1y=>%#2iiVEEmKB&8F) zcK7hi;i%CofL`zUe?i58NtY*RL=?*bJHhvYuWnDP^^OrAJbabiDK>py-&e zt(i}PYw7Ey(EZ@A$jlSD>M1t_D=In$r}tCgQ&5fV4e@~lAs4f3uOMKO^lT~C`hisW zRpGl!ncYsJ9zL>E=5BlK3|cIvAR9lh9^7C2AA_#rPVOj|v5Q0(L$*x?Wp^@U8=UJR~`_5ozeRcvFftz_eK_ zE3Gx&BM)2Y;{z8SCh30iA_`{;A0bT_DVv|jW*WRwfw&0kDn6f5LOVD)A2#CMh3P z3eP7WJ$`t*aJjvT`8F6D)F$&1!|h5&1$oy8n0;vKdaa9 PMN!vkC8K2N)6f44i+)yQ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a58a34e94ed3f8bb0efd17c94cc17545ba48408b GIT binary patch literal 1000 zcmZvb&2AGh5PP}IJV!vKQMgc1=Xq+RD7O2~6I1X+M#V=X2Um>Y7(a&-ckvn+vZVqo8J zWeaWGCew=LW3NWd=Kc^C2D3vwk%Aoo6Iub|mH{_%=9NNQn;%KoWKJ_s7UMB4B{Oh3 z0gHEVC7Kq{Nh{0lYs1cv0H$EtjH7R608PR8z#-%uELX=@(U7O7-2(p%5EQQ$8lQuZ z)oU=m#~<^opKRhKpsG!os}Y9p7od`4m*o~YbyMSOkavi1n3Mh8Wcnz295sJqR!J}O z@?!Xmui$)%(>fH6oKQhWp}8(dLO!mFKoA+6lXpbjtPvN^B8nBD}8QPgG8-?UiW32>_U+HWz2IX*3Lww3R_}76C*r54*xtKX@o3t?6w`e>!JD{d90(o<& z)mHQ*wz-2&!Lt!R1{))L>~X*-F5`*IrHvPmS5g!evvTAATWiQ{@1P)Ilh%TOuHvt; x&|9=c8-clwyX~-CPE(iHlEJEx@})H>+@4vX@rei{{WEO9bNzc literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4bd4a18ed8e39b7a3a663aeaf03941877040a3c5 GIT binary patch literal 9267 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 auVI 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a527e4bd3d5525d657bce591400a5c897841e3cd GIT binary patch literal 2027 zcmb_c!EW3(5G5t;T3+w29mk0gq)mka1-b`Yhn@-qMUWIIilRmUBbU9fw1(d0S}V#V zZN$dj9P}T0OO8I~cXaKkztCPfBkkHwnnjJe6v!b*qtTlg&Xc`fhvE3`=r8#`V(fQ1 z+#IN5&v3c~7hppM9DOLqxesk}u`J}s6d=>@O?EO>g-4}P>9O2Fv7=s>%IZd4o_XcGD6N^O%%{z9M-``Ot`F!q zHpMr)-=HsfSBAGE(p4_!~sW^BSQ_&&2OJPv{?8cR2>6Extqjk-Dq%|c%g3NjJmKAs$T zW6Lo#mRW5@mN;ln*y|GYaWi9Yu}q#%f_He+8yq42W?u9vHkelFjjh^3CPy?%4 z#*dZ#2%n{@Kg79!_IOEp$kTrSW(l&T3e<3v5cVEv=%!p}$x?D2;rv$==-|#MMi|N` zQEVZC&u)Q&9NZnnvRphs1Yg_+#s85Db-W}ObSJ*N1qyO-=Uf!aa}lMv2lpwCgF@v!i%dmimHxwx^P|=nW_n|>hBr<g?o+>0K`1nH8wf`EYBTitv%cEc!^UND}Z_p>QDC87lsX9U+lbz(W?+S5z zBJ=s16(PW6s9qJq<;FWK;2QqJ_GqDYuDi+7WwvyeES1tyCYxrwZoPU(EM+SxV--q0IrYb^YQ-{8wJ9_-PZvcSNK$14pem{2T47w_^YR literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca9ddb8c7173e3ed834b4bf7563ff2e66584f20f GIT binary patch literal 2680 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 G*Zmvw5x#){ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfcadcb5b1b7618185a87b7cbe556b8d3664d80e GIT binary patch literal 2973 zcmZ`*-EQ1O6t=xyud{zi!(U5VaH-V9DBi6A3DFP)l>Pxtt0ui@09jto?5?TTcE{ed zS=<}c3obz77KsbE>Xl@BKXeOo1~@K^30r>Gc)IW^PMyHI-RD1=cn6$ zd5>ODls`x~{L~gkU%hqx zmV5s4h4Y`eH*RijUfH~4*9!gfAk7N%T9O5E{r%?_08E+;{Lkjy2BvkuDemn!;KNoyet!|^LcC5 zN}TX$WzC|>UY$>{*Zr9BjbTr4kFDCMlF;+{>dKemOVVCJX|E4^ei)~G)n4cKxWDSh zlLXtLW_)dbZz$8~ifv!?bpoJz>Zs{Ek1xH5B2&IrN9uh&uZfp)ouU1%0!e3Dru|aO zm0#77CVN!zPOj(Ws3ty!Ms2POlyxsw6{Tt6xx@k4K3W#+n|Kkk0Mm5Bqb#-V2HDt3 z$HBz9E4(D(tW>uMH7kgAW8b5*=r!eX=MqnNlTyV^_T0qF#^^+q9adhj&0F>8myxGGz`U&13N2c3LS@=K2*da2=(~K{!JK?_WiLJ zd80UDUjLKWFVDX3$Gm^Xb5J?_9?%OUCHl!;#?#DAMEo`Pvr2S$wA&AZ0g2-{db{^t zKMgYOB%XiU8}YQC1WDfwqKpe1L4TSBVcI`no;#QZApS|M6Mp?GR5%m6l?5tumQ*tF&naZ>uYBFYO3~@B7l@XMeaib$T>YLjSHPONp z(LqtP24GO;-W_uu;sDu0Rqkfj1?5kvSahWMbIKNr3;+d@q?XG9A-beVfS1J*t!q8& z=%Ib2zi&Y+B~_)Rsq+2XsCVJ9AS8T^MyiK$^?s&!4Z>62YPrVXt;`K*mp%$YvZ(tZ z_o7+dal9}TFQUt?9r*QJyI+0QZn=cC_!8!)V13Ze#s5d z&UMlLRQXmdZO>~=#Yr{t27~p|cC_U=HK6$c`jPpn{yI~psl>whq0>4=2dk7hiX!8=*q(S6+sZ(R?oF7S(%~x2wziM2(bh~&SKHu4 zn`%v*hK5g!m_Kx<&?oPaH_?y?Yrt2#UjxpXyc=q{D$YPcEfK>A-4(cuOJ__z8Xi)kf?3{BPlG4y8X)fx2r6qWOb z);I7;uO6Y1o8VBr%gh??Ik>@*aoG&K6>*%2 zQg8o4>d6bKm+d8hRZwazj7p$L5)?jyGM1p! zB`*M(g=K1!5hsYCa+^bmUZ#AR?alWmF`I_`1N?a@DV>I?k%=!ZX#Z#}MaBE1dE9)- K?3$;{<$nPg>jHlO literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1908f8d02901cf329f6008a83ae914a876d2c09e GIT binary patch literal 5736 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 a)3UAFY^_;yYZtUGml|ER|LOk1nEh|wsXX`q literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88e9bc36ced4445a48fb0050d582c1582792c8eb GIT binary patch literal 7166 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 MeoxOjMSC{Z%pEv)>;V?=5=E?o%0F!U= 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2e46a6e52c8ec39c7ebf8c041720bab81d593b4 GIT binary patch literal 3404 zcmaJ@TW=f372erha#s{>DYopot>I1_pbi6xRL~Yki`)#Ec8%DIV97{KBv`L@hU7}i zoz2XwWUfequIas=XL`Tqo4(eonZDkun?C3T z(0f@pn(NIGhtJ7xVD4v)(R^=y)a*53%{#fqub-0M0yUrG%zs(leJJA1x-C#GNzr);3r?vDwNbIyowGeMBk(HW9>8KD= zufy7n=dU(iMQgwBu6+@`c)7m*bp1)ot^8-H(pC6np;M8^+4!S0PqLEn6?LFAAFZfV z^OYh_cH;q8->45hf%pB-Ha^~qvyy+RWyufwai(}H!K-JV8cf#URZGxlXNPE4gE4V* z?Cp9Wt~<`xhCJC-H0@Kkue}{B9u<=J(>FBEnOU1mN3l+Z?M8#bvo%Z=9m0l;E4tC$ zpub-I4Nb(T$kI61bT5^RtubFJFuU^PE;L(8i?xU{O<8>(Qyb*#1gha(Xm zruomGJ1-TNs*}KV@j&D(?mQAnY5ud52;TWFUV(0fzbUv(?Q(ITxzeDA*x`xp*y##L z)5+3poDN0)tM+d@7fjnJ(xMZkx#kkw&?$AAsm??bINB}_s+*B4^E}N5QJFI?qdi!^ zg$8z^aq6-O?OR?zFdHwRPTJ9gkz<64J04xMz2tE=wLO?uaebAwud*#o;H>71?x%W4 zi-RJ5pr%eh(I=*#xd3n%3}c1XmI26hZg4xAhqdb6OX^BKD6<&Uw(f<0z6aZ^@_}4{ z@mtChSTxF`X|!|f6b(&|9Z;W)pS<9@lsVl32qXUX`2)%6#KJVqRmwPxXY^R6j}hop z=pmQ;sp3|#mTL@e1h`U7#oN6z?j zUE8T20wpo;FYe#&9g#zF5}Z0G;i>bL;{w(Aj_(|M1ZJDghmLx*>JamL1?^W(zT`W4 z?to3#n7`?)!YMFLZTzPfJZ6gi0fW+p#}NP*0oP9Y0~4Flm=8i%D~LV7EkqfO70rN8 zsOUqVb7kYzT3XR{k@ID=b*kY=nK7CRO#v6I6DJ8*ih^lrKg}kl9XHPJ9M?+SU-<}l zEaZ727=*!kt5LZ^RellcVdd|LG`DC%yH#!~Dqj>luYA0rYAB!wZKSG#r(brZ6cTW_ zV6TWktq5?4vMx+gW}pAuHvbtSdC zzoV=U-U{L#;M@c=ef@tt_L}yEL^=gZrx$n!kPdvWfc&kmrgW+bo}~R02v!ecP4|a9 zzxsR(QG;$&Wl;zPET9G5<|gL%Qk8CJ;87_?^emKSd3l+>K6mkJIw}D>2&OpAEfz$U ziGA?3`9HM#@2r@Z27WA4(NCyJOLq4|#!{Q#vV&!s2|zI2k0o*`!qI{cCsX{vVc=gI z;OpbRuAPB(g3|}*Qa_-Ks{*p*)f$2=@Q>p}r+d6jA1YOjaAA;%ZOBoVe4&gfC-R`_ zj2(cEA@ZiVti4s$O!lp6M#aaix`AC)2gL?lR>4HI>b>ac`r5OXkGj$3tIe(M^Jw$w zRyW#sxc0@vC*95HMfXYf>#AllYgHRaQ5KLB=P(j%nE2Sq`vBUvLn0TERbeI>eFi(# zZD^ckorIoG+#md!?}ns7Zj+G6U&5CJKTjVdxwA516($psL#p$%zXKH!1c6;eszc&4 z1;j~!;bGF)zbx{>jrVi0Vr9BmwY2pceV_6-KuikUVTc{=4ms7r!_*U_6d3{dOAOKk z?T(3TCIg!YVjS`__zyh@A%U4-5)}rZ7rlv*F%5wUp07xs7G&d}|A_FdP~HIJ$eYkq z0ZXAm81X8A8qYExUR#Bc;jbu?SZ`Hzt1mEI6F)~I!6GTv-f$5rkIdy)sc%U*~uFBam%+k8Ubh%HA*<6ttt2n*)N@!7w5|F!fvZ$ zy-w;bz7qMr_0y^WW-p`E4pFyS9KCATFrqXNc zg95jExS>9oVA_xv9@QKqpl3F?!OiG>xa}ACCBU*5(w2M|H)A@o`e8j`SiuCHafp%l rN}a=x0Uc$=KSdWQEK8aYv}ZrJ>H1{RZ4evK;au1VgK#mt7heA#JsG`P literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..442be9e41b32e4eee323f5d68ce917b651c36375 GIT binary patch literal 6383 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 b=kcB}s-|UD%*h%1ZTp~o(yrRm_Wu6>gT}5v literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000..3989ed3 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,44 @@ +""" +This code wraps the vendored appdirs module to so the return values are +compatible for the current pip code base. + +The intention is to rewrite current usages gradually, keeping the tests pass, +and eventually drop this after all usages are changed. +""" + +from __future__ import absolute_import + +import os + +from pip._vendor import appdirs as _appdirs + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +def user_cache_dir(appname): + # type: (str) -> str + return _appdirs.user_cache_dir(appname, appauthor=False) + + +def user_config_dir(appname, roaming=True): + # type: (str, bool) -> str + path = _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) + if _appdirs.system == "darwin" and not os.path.isdir(path): + path = os.path.expanduser('~/.config/') + if appname: + path = os.path.join(path, appname) + return path + + +# for the discussion regarding site_config_dir locations +# see +def site_config_dirs(appname): + # type: (str) -> List[str] + dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) + if _appdirs.system not in ["win32", "darwin"]: + # always look in /etc directly as well + return dirval.split(os.pathsep) + ['/etc'] + return [dirval] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compat.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..89c5169 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,271 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import, division + +import codecs +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import PY2, text_type + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Tuple, Union + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress # type: ignore + except ImportError: + import ipaddr as ipaddress # type: ignore + ipaddress.ip_address = ipaddress.IPAddress # type: ignore + ipaddress.ip_network = ipaddress.IPNetwork # type: ignore + + +__all__ = [ + "ipaddress", "uses_pycache", "console_to_str", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", +] + + +logger = logging.getLogger(__name__) + +if PY2: + import imp + + try: + cache_from_source = imp.cache_from_source # type: ignore + except AttributeError: + # does not use __pycache__ + cache_from_source = None + + uses_pycache = cache_from_source is not None +else: + uses_pycache = True + from importlib.util import cache_from_source + + +if PY2: + # In Python 2.7, backslashreplace exists + # but does not support use for decoding. + # We implement our own replace handler for this + # situation, so that we can consistently use + # backslash replacement for all versions. + def backslashreplace_decode_fn(err): + raw_bytes = (err.object[i] for i in range(err.start, err.end)) + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(map(u"\\x{:x}".format, raw_bytes)), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" +else: + backslashreplace_decode = "backslashreplace" + + +def has_tls(): + # type: () -> bool + try: + import _ssl # noqa: F401 # ignore unused + return True + except ImportError: + pass + + from pip._vendor.urllib3.util import IS_PYOPENSSL + return IS_PYOPENSSL + + +def str_to_display(data, desc=None): + # type: (Union[bytes, Text], Optional[str]) -> Text + """ + For display or logging purposes, convert a bytes object (or text) to + text (e.g. unicode in Python 2) safe for output. + + :param desc: An optional phrase describing the input data, for use in + the log message if a warning is logged. Defaults to "Bytes object". + + This function should never error out and so can take a best effort + approach. It is okay to be lossy if needed since the return value is + just for display. + + We assume the data is in the locale preferred encoding. If it won't + decode properly, we warn the user but decode as best we can. + + We also ensure that the output can be safely written to standard output + without encoding errors. + """ + if isinstance(data, text_type): + return data + + # Otherwise, data is a bytes object (str in Python 2). + # First, get the encoding we assume. This is the preferred + # encoding for the locale, unless that is not found, or + # it is ASCII, in which case assume UTF-8 + encoding = locale.getpreferredencoding() + if (not encoding) or codecs.lookup(encoding).name == "ascii": + encoding = "utf-8" + + # Now try to decode the data - if we fail, warn the user and + # decode with replacement. + try: + decoded_data = data.decode(encoding) + except UnicodeDecodeError: + logger.warning( + '%s does not appear to be encoded as %s', + desc or 'Bytes object', + encoding, + ) + decoded_data = data.decode(encoding, errors=backslashreplace_decode) + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then + # decoding again. + # We use stderr's encoding because it's less likely to be + # redirected and if we don't find an encoding we skip this + # step (on the assumption that output is wrapped by something + # that won't fail). + # The double getattr is to deal with the possibility that we're + # being called in a situation where sys.__stderr__ doesn't exist, + # or doesn't have an encoding attribute. Neither of these cases + # should occur in normal pip use, but there's no harm in checking + # in case people use pip in (unsupported) unusual situations. + output_encoding = getattr(getattr(sys, "__stderr__", None), + "encoding", None) + + if output_encoding: + output_encoded = decoded_data.encode( + output_encoding, + errors="backslashreplace" + ) + decoded_data = output_encoded.decode(output_encoding) + + return decoded_data + + +def console_to_str(data): + # type: (bytes) -> Text + """Return a string, safe for output, of subprocess output. + """ + return str_to_display(data, desc='Subprocess output') + + +def get_path_uid(path): + # type: (str) -> int + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "{} is a symlink; Will not return uid for symlinks".format( + path) + ) + return file_uid + + +def expanduser(path): + # type: (str) -> str + """ + Expand ~ and ~user constructions. + + Includes a workaround for https://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = {"python", "wsgiref", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + # type: (str, str) -> bool + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) # type: ignore +else: + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack_from( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') + ) + except Exception: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + if sys.platform != "win32": + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except Exception: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py new file mode 100644 index 0000000..4f21874 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py @@ -0,0 +1,166 @@ +"""Generate and work with PEP 425 Compatibility Tags. +""" + +from __future__ import absolute_import + +import re + +from pip._vendor.packaging.tags import ( + Tag, + compatible_tags, + cpython_tags, + generic_tags, + interpreter_name, + interpreter_version, + mac_platforms, +) + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import PythonVersion + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def version_info_to_nodot(version_info): + # type: (Tuple[int, ...]) -> str + # Only use up to the first two numbers. + return ''.join(map(str, version_info[:2])) + + +def _mac_platforms(arch): + # type: (str) -> List[str] + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + mac_version = (int(major), int(minor)) + arches = [ + # Since we have always only checked that the platform starts + # with "macosx", for backwards-compatibility we extract the + # actual prefix provided by the user in case they provided + # something like "macosxcustom_". It may be good to remove + # this as undocumented or deprecate it in the future. + '{}_{}'.format(name, arch[len('macosx_'):]) + for arch in mac_platforms(mac_version, actual_arch) + ] + else: + # arch pattern didn't match (?!) + arches = [arch] + return arches + + +def _custom_manylinux_platforms(arch): + # type: (str) -> List[str] + arches = [arch] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch_prefix == 'manylinux2014': + # manylinux1/manylinux2010 wheels run on most manylinux2014 systems + # with the exception of wheels depending on ncurses. PEP 599 states + # manylinux1/manylinux2010 wheels should be considered + # manylinux2014 wheels: + # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels + if arch_suffix in {'i686', 'x86_64'}: + arches.append('manylinux2010' + arch_sep + arch_suffix) + arches.append('manylinux1' + arch_sep + arch_suffix) + elif arch_prefix == 'manylinux2010': + # manylinux1 wheels run on most manylinux2010 systems with the + # exception of wheels depending on ncurses. PEP 571 states + # manylinux1 wheels should be considered manylinux2010 wheels: + # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels + arches.append('manylinux1' + arch_sep + arch_suffix) + return arches + + +def _get_custom_platforms(arch): + # type: (str) -> List[str] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch.startswith('macosx'): + arches = _mac_platforms(arch) + elif arch_prefix in ['manylinux2014', 'manylinux2010']: + arches = _custom_manylinux_platforms(arch) + else: + arches = [arch] + return arches + + +def _get_python_version(version): + # type: (str) -> PythonVersion + if len(version) > 1: + return int(version[0]), int(version[1:]) + else: + return (int(version[0]),) + + +def _get_custom_interpreter(implementation=None, version=None): + # type: (Optional[str], Optional[str]) -> str + if implementation is None: + implementation = interpreter_name() + if version is None: + version = interpreter_version() + return "{}{}".format(implementation, version) + + +def get_supported( + version=None, # type: Optional[str] + platform=None, # type: Optional[str] + impl=None, # type: Optional[str] + abi=None # type: Optional[str] +): + # type: (...) -> List[Tag] + """Return a list of supported tags for each version specified in + `versions`. + + :param version: a string version, of the form "33" or "32", + or None. The version will be assumed to support our ABI. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] # type: List[Tag] + + python_version = None # type: Optional[PythonVersion] + if version is not None: + python_version = _get_python_version(version) + + interpreter = _get_custom_interpreter(impl, version) + + abis = None # type: Optional[List[str]] + if abi is not None: + abis = [abi] + + platforms = None # type: Optional[List[str]] + if platform is not None: + platforms = _get_custom_platforms(platform) + + is_cpython = (impl or interpreter_name()) == "cp" + if is_cpython: + supported.extend( + cpython_tags( + python_version=python_version, + abis=abis, + platforms=platforms, + ) + ) + else: + supported.extend( + generic_tags( + interpreter=interpreter, + abis=abis, + platforms=platforms, + ) + ) + supported.extend( + compatible_tags( + python_version=python_version, + interpreter=interpreter, + platforms=platforms, + ) + ) + + return supported diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/datetime.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/datetime.py new file mode 100644 index 0000000..4d0503c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/datetime.py @@ -0,0 +1,14 @@ +"""For when pip wants to check the date or time. +""" + +from __future__ import absolute_import + +import datetime + + +def today_is_later_than(year, month, day): + # type: (int, int, int) -> bool + today = datetime.date.today() + given = datetime.date(year, month, day) + + return today > given diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..2f20cfd --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,104 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import warnings + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional + + +DEPRECATION_MSG_PREFIX = "DEPRECATION: " + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning = None # type: Any + + +# Warnings <-> Logging Integration +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _original_showwarning is not None: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # type: () -> None + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated(reason, replacement, gone_in, issue=None): + # type: (str, Optional[str], Optional[str], Optional[int]) -> None + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises errors if pip's current version is greater than or equal to + this. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + + Always pass replacement, gone_in and issue as keyword arguments for clarity + at the call site. + """ + + # Construct a nice message. + # This is eagerly formatted as we want it to get logged as if someone + # typed this entire message out. + sentences = [ + (reason, DEPRECATION_MSG_PREFIX + "{}"), + (gone_in, "pip {} will remove support for this functionality."), + (replacement, "A possible replacement is {}."), + (issue, ( + "You can find discussion regarding this at " + "https://github.com/pypa/pip/issues/{}." + )), + ] + message = " ".join( + template.format(val) for val, template in sentences if val is not None + ) + + # Raise as an error if it has to be removed. + if gone_in is not None and parse(current_version) >= parse(gone_in): + raise PipDeprecationWarning(message) + + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py new file mode 100644 index 0000000..f1fe209 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -0,0 +1,130 @@ +import logging + +from pip._internal.models.direct_url import ( + DIRECT_URL_METADATA_NAME, + ArchiveInfo, + DirectUrl, + DirectUrlValidationError, + DirInfo, + VcsInfo, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs import vcs + +try: + from json import JSONDecodeError +except ImportError: + # PY2 + JSONDecodeError = ValueError # type: ignore + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._internal.models.link import Link + + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +def direct_url_as_pep440_direct_reference(direct_url, name): + # type: (DirectUrl, str) -> str + """Convert a DirectUrl to a pip requirement string.""" + direct_url.validate() # if invalid, this is a pip bug + requirement = name + " @ " + fragments = [] + if isinstance(direct_url.info, VcsInfo): + requirement += "{}+{}@{}".format( + direct_url.info.vcs, direct_url.url, direct_url.info.commit_id + ) + elif isinstance(direct_url.info, ArchiveInfo): + requirement += direct_url.url + if direct_url.info.hash: + fragments.append(direct_url.info.hash) + else: + assert isinstance(direct_url.info, DirInfo) + # pip should never reach this point for editables, since + # pip freeze inspects the editable project location to produce + # the requirement string + assert not direct_url.info.editable + requirement += direct_url.url + if direct_url.subdirectory: + fragments.append("subdirectory=" + direct_url.subdirectory) + if fragments: + requirement += "#" + "&".join(fragments) + return requirement + + +def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False): + # type: (Link, Optional[str], bool) -> DirectUrl + if link.is_vcs: + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend + url, requested_revision, _ = ( + vcs_backend.get_url_rev_and_auth(link.url_without_fragment) + ) + # For VCS links, we need to find out and add commit_id. + if link_is_in_wheel_cache: + # If the requested VCS link corresponds to a cached + # wheel, it means the requested revision was an + # immutable commit hash, otherwise it would not have + # been cached. In that case we don't have a source_dir + # with the VCS checkout. + assert requested_revision + commit_id = requested_revision + else: + # If the wheel was not in cache, it means we have + # had to checkout from VCS to build and we have a source_dir + # which we can inspect to find out the commit id. + assert source_dir + commit_id = vcs_backend.get_revision(source_dir) + return DirectUrl( + url=url, + info=VcsInfo( + vcs=vcs_backend.name, + commit_id=commit_id, + requested_revision=requested_revision, + ), + subdirectory=link.subdirectory_fragment, + ) + elif link.is_existing_dir(): + return DirectUrl( + url=link.url_without_fragment, + info=DirInfo(), + subdirectory=link.subdirectory_fragment, + ) + else: + hash = None + hash_name = link.hash_name + if hash_name: + hash = "{}={}".format(hash_name, link.hash) + return DirectUrl( + url=link.url_without_fragment, + info=ArchiveInfo(hash=hash), + subdirectory=link.subdirectory_fragment, + ) + + +def dist_get_direct_url(dist): + # type: (Distribution) -> Optional[DirectUrl] + """Obtain a DirectUrl from a pkg_resource.Distribution. + + Returns None if the distribution has no `direct_url.json` metadata, + or if `direct_url.json` is invalid. + """ + if not dist.has_metadata(DIRECT_URL_METADATA_NAME): + return None + try: + return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME)) + except ( + DirectUrlValidationError, + JSONDecodeError, + UnicodeDecodeError + ) as e: + logger.warning( + "Error parsing %s for %s: %s", + DIRECT_URL_METADATA_NAME, + dist.project_name, + e, + ) + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py new file mode 100644 index 0000000..e38e402 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py @@ -0,0 +1,48 @@ +from distutils.errors import DistutilsArgError +from distutils.fancy_getopt import FancyGetopt + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, List + + +_options = [ + ("exec-prefix=", None, ""), + ("home=", None, ""), + ("install-base=", None, ""), + ("install-data=", None, ""), + ("install-headers=", None, ""), + ("install-lib=", None, ""), + ("install-platlib=", None, ""), + ("install-purelib=", None, ""), + ("install-scripts=", None, ""), + ("prefix=", None, ""), + ("root=", None, ""), + ("user", None, ""), +] + + +# typeshed doesn't permit Tuple[str, None, str], see python/typeshed#3469. +_distutils_getopt = FancyGetopt(_options) # type: ignore + + +def parse_distutils_args(args): + # type: (List[str]) -> Dict[str, str] + """Parse provided arguments, returning an object that has the + matched arguments. + + Any unknown arguments are ignored. + """ + result = {} + for arg in args: + try: + _, match = _distutils_getopt.getopt(args=[arg]) + except DistutilsArgError: + # We don't care about any other options, which here may be + # considered unrecognized since our option list is not + # exhaustive. + pass + else: + result.update(match.__dict__) + return result diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/encoding.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..5b83d61 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,41 @@ +import codecs +import locale +import re +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Tuple, Text + +BOMS = [ + (codecs.BOM_UTF8, 'utf-8'), + (codecs.BOM_UTF16, 'utf-16'), + (codecs.BOM_UTF16_BE, 'utf-16-be'), + (codecs.BOM_UTF16_LE, 'utf-16-le'), + (codecs.BOM_UTF32, 'utf-32'), + (codecs.BOM_UTF32_BE, 'utf-32-be'), + (codecs.BOM_UTF32_LE, 'utf-32-le'), +] # type: List[Tuple[bytes, Text]] + +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + # type: (bytes) -> Text + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + result = ENCODING_RE.search(line) + assert result is not None + encoding = result.groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode( + locale.getpreferredencoding(False) or sys.getdefaultencoding(), + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py new file mode 100644 index 0000000..befd01c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py @@ -0,0 +1,31 @@ +import sys + +from pip._internal.cli.main import main +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List + + +def _wrapper(args=None): + # type: (Optional[List[str]]) -> int + """Central wrapper for all old entrypoints. + + Historically pip has had several entrypoints defined. Because of issues + arising from PATH, sys.path, multiple Pythons, their interactions, and most + of them having a pip installed, users suffer every time an entrypoint gets + moved. + + To alleviate this pain, and provide a mechanism for warning users and + directing them to an appropriate place for help, we now define all of + our old entrypoints as wrappers for the current one. + """ + sys.stderr.write( + "WARNING: pip is being invoked by an old script wrapper. This will " + "fail in a future version of pip.\n" + "Please see https://github.com/pypa/pip/issues/5599 for advice on " + "fixing the underlying issue.\n" + "To avoid this problem you can invoke Python with '-m pip' instead of " + "running pip directly.\n" + ) + return main(args) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..303243f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,224 @@ +import errno +import fnmatch +import os +import os.path +import random +import shutil +import stat +import sys +from contextlib import contextmanager +from tempfile import NamedTemporaryFile + +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2 + +from pip._internal.utils.compat import get_path_uid +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast + +if MYPY_CHECK_RUNNING: + from typing import Any, BinaryIO, Iterator, List, Union + + class NamedTemporaryFileResult(BinaryIO): + @property + def file(self): + # type: () -> BinaryIO + pass + + +def check_path_owner(path): + # type: (str) -> bool + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if sys.platform == "win32" or not hasattr(os, "geteuid"): + return True + + assert os.path.isabs(path) + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) + return False # assume we don't own the path + + +def copy2_fixed(src, dest): + # type: (str, str) -> None + """Wrap shutil.copy2() but map errors copying socket files to + SpecialFileError as expected. + + See also https://bugs.python.org/issue37700. + """ + try: + shutil.copy2(src, dest) + except (OSError, IOError): + for f in [src, dest]: + try: + is_socket_file = is_socket(f) + except OSError: + # An error has already occurred. Another error here is not + # a problem and we can ignore it. + pass + else: + if is_socket_file: + raise shutil.SpecialFileError( + "`{f}` is a socket".format(**locals())) + + raise + + +def is_socket(path): + # type: (str) -> bool + return stat.S_ISSOCK(os.lstat(path).st_mode) + + +@contextmanager +def adjacent_tmp_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + """Return a file-like object pointing to a tmp file next to path. + + The file is created securely and is ensured to be written to disk + after the context reaches its end. + + kwargs will be passed to tempfile.NamedTemporaryFile to control + the way the temporary file will be opened. + """ + with NamedTemporaryFile( + delete=False, + dir=os.path.dirname(path), + prefix=os.path.basename(path), + suffix='.tmp', + **kwargs + ) as f: + result = cast('NamedTemporaryFileResult', f) + try: + yield result + finally: + result.file.flush() + os.fsync(result.file.fileno()) + + +_replace_retry = retry(stop_max_delay=1000, wait_fixed=250) + +if PY2: + @_replace_retry + def replace(src, dest): + # type: (str, str) -> None + try: + os.rename(src, dest) + except OSError: + os.remove(dest) + os.rename(src, dest) + +else: + replace = _replace_retry(os.replace) + + +# test_writable_dir and _test_writable_dir_win are copied from Flit, +# with the author's agreement to also place them under pip's license. +def test_writable_dir(path): + # type: (str) -> bool + """Check if a directory is writable. + + Uses os.access() on POSIX, tries creating files on Windows. + """ + # If the directory doesn't exist, find the closest parent that does. + while not os.path.isdir(path): + parent = os.path.dirname(path) + if parent == path: + break # Should never get here, but infinite loops are bad + path = parent + + if os.name == 'posix': + return os.access(path, os.W_OK) + + return _test_writable_dir_win(path) + + +def _test_writable_dir_win(path): + # type: (str) -> bool + # os.access doesn't work on Windows: http://bugs.python.org/issue2528 + # and we can't use tempfile: http://bugs.python.org/issue22107 + basename = 'accesstest_deleteme_fishfingers_custard_' + alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' + for _ in range(10): + name = basename + ''.join(random.choice(alphabet) for _ in range(6)) + file = os.path.join(path, name) + try: + fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) + # Python 2 doesn't support FileExistsError and PermissionError. + except OSError as e: + # exception FileExistsError + if e.errno == errno.EEXIST: + continue + # exception PermissionError + if e.errno == errno.EPERM or e.errno == errno.EACCES: + # This could be because there's a directory with the same name. + # But it's highly unlikely there's a directory called that, + # so we'll assume it's because the parent dir is not writable. + # This could as well be because the parent dir is not readable, + # due to non-privileged user access. + return False + raise + else: + os.close(fd) + os.unlink(file) + return True + + # This should never be reached + raise EnvironmentError( + 'Unexpected condition testing for writable directory' + ) + + +def find_files(path, pattern): + # type: (str, str) -> List[str] + """Returns a list of absolute paths of files beneath path, recursively, + with filenames which match the UNIX-style shell glob pattern.""" + result = [] # type: List[str] + for root, _, files in os.walk(path): + matches = fnmatch.filter(files, pattern) + result.extend(os.path.join(root, f) for f in matches) + return result + + +def file_size(path): + # type: (str) -> Union[int, float] + # If it's a symlink, return 0. + if os.path.islink(path): + return 0 + return os.path.getsize(path) + + +def format_file_size(path): + # type: (str) -> str + return format_size(file_size(path)) + + +def directory_size(path): + # type: (str) -> Union[int, float] + size = 0.0 + for root, _dirs, files in os.walk(path): + for filename in files: + file_path = os.path.join(root, filename) + size += file_size(file_path) + return size + + +def format_directory_size(path): + # type: (str) -> str + return format_size(directory_size(path)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py new file mode 100644 index 0000000..daa0ca7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py @@ -0,0 +1,16 @@ +"""Filetype information. +""" +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple + +WHEEL_EXTENSION = '.whl' +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') # type: Tuple[str, ...] +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', + '.tar.lz', '.tar.lzma') # type: Tuple[str, ...] +ZIP_EXTENSIONS = ('.zip', WHEEL_EXTENSION) # type: Tuple[str, ...] +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') # type: Tuple[str, ...] +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS +) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/glibc.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..3610424 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,98 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + +def glibc_version_string(): + # type: () -> Optional[str] + "Returns glibc version string, or None if not using glibc." + return glibc_version_string_confstr() or glibc_version_string_ctypes() + + +def glibc_version_string_confstr(): + # type: () -> Optional[str] + "Primary implementation of glibc_version_string using os.confstr." + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module: + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + if sys.platform == "win32": + return None + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": + _, version = os.confstr("CS_GNU_LIBC_VERSION").split() + except (AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def glibc_version_string_ctypes(): + # type: () -> Optional[str] + "Fallback implementation of glibc_version_string using ctypes." + + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + # type: () -> Tuple[str, str] + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/hashes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..d1b062f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,145 @@ +from __future__ import absolute_import + +import hashlib + +from pip._vendor.six import iteritems, iterkeys, itervalues + +from pip._internal.exceptions import ( + HashMismatch, + HashMissing, + InstallationError, +) +from pip._internal.utils.misc import read_chunks +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Dict, List, BinaryIO, NoReturn, Iterator + ) + from pip._vendor.six import PY3 + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + # type: (Dict[str, List[str]]) -> None + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def __or__(self, other): + # type: (Hashes) -> Hashes + if not isinstance(other, Hashes): + return NotImplemented + new = self._allowed.copy() + for alg, values in iteritems(other._allowed): + try: + new[alg] += values + except KeyError: + new[alg] = values + return Hashes(new) + + @property + def digest_count(self): + # type: () -> int + return sum(len(digests) for digests in self._allowed.values()) + + def is_hash_allowed( + self, + hash_name, # type: str + hex_digest, # type: str + ): + # type: (...) -> bool + """Return whether the given hex digest is allowed.""" + return hex_digest in self._allowed.get(hash_name, []) + + def check_against_chunks(self, chunks): + # type: (Iterator[bytes]) -> None + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError( + 'Unknown hash name: {}'.format(hash_name) + ) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + # type: (BinaryIO) -> None + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + # type: (str) -> None + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + # type: () -> bool + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + # type: () -> bool + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + # type: () -> None + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py new file mode 100644 index 0000000..5b93b1d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py @@ -0,0 +1,36 @@ +"""A helper module that injects SecureTransport, on import. + +The import should be done as early as possible, to ensure all requests and +sessions (or whatever) are created after injecting SecureTransport. + +Note that we only do the injection on macOS, when the linked OpenSSL is too +old to handle TLSv1.2. +""" + +import sys + + +def inject_securetransport(): + # type: () -> None + # Only relevant on macOS + if sys.platform != "darwin": + return + + try: + import ssl + except ImportError: + return + + # Checks for OpenSSL 1.0.1 + if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100f: + return + + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + return + + securetransport.inject_into_urllib3() + + +inject_securetransport() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/logging.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..9a017cf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,399 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import logging +import logging.handlers +import os +import sys +from logging import Filter, getLogger + +from pip._vendor.six import PY2 + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX +from pip._internal.utils.misc import ensure_dir + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +try: + # Use "import as" and set colorama in the else clause to avoid mypy + # errors and get the following correct revealed type for colorama: + # `Union[_importlib_modulespec.ModuleType, None]` + # Otherwise, we get an error like the following in the except block: + # > Incompatible types in assignment (expression has type "None", + # variable has type Module) + # TODO: eliminate the need to use "import as" once mypy addresses some + # of its issues with conditional imports. Here is an umbrella issue: + # https://github.com/python/mypy/issues/1297 + from pip._vendor import colorama as _colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None +else: + # Import Fore explicitly rather than accessing below as colorama.Fore + # to avoid the following error running mypy: + # > Module has no attribute "Fore" + # TODO: eliminate the need to import Fore once mypy addresses some of its + # issues with conditional imports. This particular case could be an + # instance of the following issue (but also see the umbrella issue above): + # https://github.com/python/mypy/issues/3500 + from pip._vendor.colorama import Fore + + colorama = _colorama + + +_log_state = threading.local() +subprocess_logger = getLogger('pip.subprocessor') + + +class BrokenStdoutLoggingError(Exception): + """ + Raised if BrokenPipeError occurs for the stdout stream while logging. + """ + pass + + +# BrokenPipeError does not exist in Python 2 and, in addition, manifests +# differently in Windows and non-Windows. +if WINDOWS: + # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: + # https://bugs.python.org/issue19612 + # https://bugs.python.org/issue30418 + if PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and + exc.errno in (errno.EINVAL, errno.EPIPE)) + else: + # In Windows, a broken pipe IOError became OSError in Python 3. + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return ((exc_class is BrokenPipeError) or # noqa: F821 + (exc_class is OSError and + exc.errno in (errno.EINVAL, errno.EPIPE))) +elif PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and exc.errno == errno.EPIPE) +else: + # Then we are in the non-Windows Python 3 case. + def _is_broken_pipe_error(exc_class, exc): + """ + Return whether an exception is a broken pipe error. + + Args: + exc_class: an exception class. + exc: an exception instance. + """ + return (exc_class is BrokenPipeError) # noqa: F821 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + # For thread-safety + _log_state.indentation = get_indentation() + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def __init__(self, *args, **kwargs): + """ + A logging.Formatter that obeys the indent_log() context manager. + + :param add_timestamp: A bool indicating output lines should be prefixed + with their record's timestamp. + """ + self.add_timestamp = kwargs.pop("add_timestamp", False) + super(IndentingFormatter, self).__init__(*args, **kwargs) + + def get_message_start(self, formatted, levelno): + """ + Return the start of the formatted log message (not counting the + prefix to add to each line). + """ + if levelno < logging.WARNING: + return '' + if formatted.startswith(DEPRECATION_MSG_PREFIX): + # Then the message already has a prefix. We don't want it to + # look like "WARNING: DEPRECATION: ...." + return '' + if levelno < logging.ERROR: + return 'WARNING: ' + + return 'ERROR: ' + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log message + lines by our current indentation level. + """ + formatted = super(IndentingFormatter, self).format(record) + message_start = self.get_message_start(formatted, record.levelno) + formatted = message_start + formatted + + prefix = '' + if self.add_timestamp: + # TODO: Use Formatter.default_time_format after dropping PY2. + t = self.formatTime(record, "%Y-%m-%dT%H:%M:%S") + prefix = '{t},{record.msecs:03.0f} '.format(**locals()) + prefix += " " * get_indentation() + formatted = "".join([ + prefix + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(Fore.RED)), + (logging.WARNING, _color_wrap(Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None, no_color=None): + logging.StreamHandler.__init__(self, stream) + self._no_color = no_color + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def _using_stdout(self): + """ + Return whether the handler is using sys.stdout. + """ + if WINDOWS and colorama: + # Then self.stream is an AnsiToWin32 object. + return self.stream.wrapped is sys.stdout + + return self.stream is sys.stdout + + def should_color(self): + # Don't colorize things if we do not have colorama or if told not to + if not colorama or self._no_color: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ANSI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + # The logging module says handleError() can be customized. + def handleError(self, record): + exc_class, exc = sys.exc_info()[:2] + # If a broken pipe occurred while calling write() or flush() on the + # stdout stream in logging's Handler.emit(), then raise our special + # exception so we can handle it in main() instead of logging the + # broken pipe error and continuing. + if (exc_class and self._using_stdout() and + _is_broken_pipe_error(exc_class, exc)): + raise BrokenStdoutLoggingError() + + return super(ColorizedStreamHandler, self).handleError(record) + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level + + +class ExcludeLoggerFilter(Filter): + + """ + A logging Filter that excludes records from a logger (or its children). + """ + + def filter(self, record): + # The base Filter class allows only records from a logger (or its + # children). + return not super(ExcludeLoggerFilter, self).filter(record) + + +def setup_logging(verbosity, no_color, user_log_file): + """Configures and sets up all of the logging + + Returns the requested logging level, as its integer value. + """ + + # Determine the level to be logging at. + if verbosity >= 1: + level = "DEBUG" + elif verbosity == -1: + level = "WARNING" + elif verbosity == -2: + level = "ERROR" + elif verbosity <= -3: + level = "CRITICAL" + else: + level = "INFO" + + level_number = getattr(logging, level) + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.ColorizedStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + handlers = ["console", "console_errors", "console_subprocess"] + ( + ["user_log"] if include_user_log else [] + ) + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + "restrict_to_subprocess": { + "()": "logging.Filter", + "name": subprocess_logger.name, + }, + "exclude_subprocess": { + "()": "pip._internal.utils.logging.ExcludeLoggerFilter", + "name": subprocess_logger.name, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + "indent_with_timestamp": { + "()": IndentingFormatter, + "format": "%(message)s", + "add_timestamp": True, + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_subprocess", "exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["exclude_subprocess"], + "formatter": "indent", + }, + # A handler responsible for logging to the console messages + # from the "subprocessor" logger. + "console_subprocess": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["restrict_to_subprocess"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "delay": True, + "formatter": "indent_with_timestamp", + }, + }, + "root": { + "level": root_level, + "handlers": handlers, + }, + "loggers": { + "pip._vendor": { + "level": vendored_log_level + } + }, + }) + + return level_number diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/misc.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..5629c60 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,959 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import getpass +import hashlib +import io +import logging +import os +import posixpath +import shutil +import stat +import sys +from collections import deque +from itertools import tee + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2, text_type +from pip._vendor.six.moves import filter, filterfalse, input, map, zip_longest +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote + +from pip import __version__ +from pip._internal.exceptions import CommandError +from pip._internal.locations import ( + get_major_minor_version, + site_packages, + user_site, +) +from pip._internal.utils.compat import ( + WINDOWS, + expanduser, + stdlib_pkgs, + str_to_display, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, AnyStr, Callable, Container, Iterable, Iterator, List, Optional, + Text, Tuple, TypeVar, Union, + ) + from pip._vendor.pkg_resources import Distribution + + VersionInfo = Tuple[int, int, int] + T = TypeVar("T") + + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'normalize_path', + 'renames', 'get_prog', + 'captured_stdout', 'ensure_dir', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = logging.getLogger(__name__) + + +def get_pip_version(): + # type: () -> str + pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") + pip_pkg_dir = os.path.abspath(pip_pkg_dir) + + return ( + 'pip {} from {} (python {})'.format( + __version__, pip_pkg_dir, get_major_minor_version(), + ) + ) + + +def normalize_version_info(py_version_info): + # type: (Tuple[int, ...]) -> Tuple[int, int, int] + """ + Convert a tuple of ints representing a Python version to one of length + three. + + :param py_version_info: a tuple of ints representing a Python version, + or None to specify no version. The tuple can have any length. + + :return: a tuple of length three if `py_version_info` is non-None. + Otherwise, return `py_version_info` unchanged (i.e. None). + """ + if len(py_version_info) < 3: + py_version_info += (3 - len(py_version_info)) * (0,) + elif len(py_version_info) > 3: + py_version_info = py_version_info[:3] + + return cast('VersionInfo', py_version_info) + + +def ensure_dir(path): + # type: (AnyStr) -> None + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + # Windows can raise spurious ENOTEMPTY errors. See #6426. + if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY: + raise + + +def get_prog(): + # type: () -> str + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "{} -m pip".format(sys.executable) + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + # type: (Text, bool) -> None + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + try: + has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE) + except (IOError, OSError): + # it's equivalent to os.path.exists + return + + if has_attr_readonly: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def path_to_display(path): + # type: (Optional[Union[str, Text]]) -> Optional[Text] + """ + Convert a bytes (or text) path to text (unicode in Python 2) for display + and logging purposes. + + This function should never error out. Also, this function is mainly needed + for Python 2 since in Python 3 str paths are already text. + """ + if path is None: + return None + if isinstance(path, text_type): + return path + # Otherwise, path is a bytes object (str in Python 2). + try: + display_path = path.decode(sys.getfilesystemencoding(), 'strict') + except UnicodeDecodeError: + # Include the full bytes to make troubleshooting easier, even though + # it may not be very human readable. + if PY2: + # Convert the bytes to a readable str representation using + # repr(), and then convert the str to unicode. + # Also, we add the prefix "b" to the repr() return value both + # to make the Python 2 output look like the Python 3 output, and + # to signal to the user that this is a bytes representation. + display_path = str_to_display('b{!r}'.format(path)) + else: + # Silence the "F821 undefined name 'ascii'" flake8 error since + # in Python 3 ascii() is a built-in. + display_path = ascii(path) # noqa: F821 + + return display_path + + +def display_path(path): + # type: (Union[str, Text]) -> str + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + # type: (str, str) -> str + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + # type: (str, Iterable[str]) -> str + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def _check_no_input(message): + # type: (str) -> None + """Raise an error if no input is allowed.""" + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: {}'.format( + message) + ) + + +def ask(message, options): + # type: (str, Iterable[str]) -> str + """Ask the message interactively, with the given possible responses""" + while 1: + _check_no_input(message) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response ({!r}) was not one of the expected responses: ' + '{}'.format(response, ', '.join(options)) + ) + else: + return response + + +def ask_input(message): + # type: (str) -> str + """Ask for input interactively.""" + _check_no_input(message) + return input(message) + + +def ask_password(message): + # type: (str) -> str + """Ask for a password interactively.""" + _check_no_input(message) + return getpass.getpass(message) + + +def format_size(bytes): + # type: (float) -> str + if bytes > 1000 * 1000: + return '{:.1f} MB'.format(bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '{} kB'.format(int(bytes / 1000)) + elif bytes > 1000: + return '{:.1f} kB'.format(bytes / 1000.0) + else: + return '{} bytes'.format(int(bytes)) + + +def tabulate(rows): + # type: (Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]] + """Return a list of formatted rows and a list of column sizes. + + For example:: + + >>> tabulate([['foobar', 2000], [0xdeadbeef]]) + (['foobar 2000', '3735928559'], [10, 4]) + """ + rows = [tuple(map(str, row)) for row in rows] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue='')] + table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] + return table, sizes + + +def is_installable_dir(path): + # type: (str) -> bool + """Is path is a directory containing setup.py or pyproject.toml? + """ + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + pyproject_toml = os.path.join(path, 'pyproject.toml') + if os.path.isfile(pyproject_toml): + return True + return False + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def normalize_path(path, resolve_symlinks=True): + # type: (str, bool) -> str + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + # type: (str) -> Tuple[str, str] + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + # type: (str, str) -> None + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + # type: (str) -> bool + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + Caution: this function assumes the head of path has been normalized + with normalize_path. + """ + if not running_under_virtualenv(): + return True + return path.startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is installed in user site. + """ + return dist_location(dist).startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is installed in + sysconfig.get_python_lib(). + """ + return dist_location(dist).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is an editable install. + """ + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions( + local_only=True, # type: bool + skip=stdlib_pkgs, # type: Container[str] + include_editables=True, # type: bool + editables_only=False, # type: bool + user_only=False, # type: bool + paths=None # type: Optional[List[str]] +): + # type: (...) -> List[Distribution] + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``include_editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + If ``paths`` is set, only report the distributions present at the + specified list of locations. + """ + if paths: + working_set = pkg_resources.WorkingSet(paths) + else: + working_set = pkg_resources.working_set + + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def _search_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Find a distribution matching the ``req_name`` in the environment. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + # Canonicalize the name before searching in the list of + # installed distributions and also while creating the package + # dictionary to get the Distribution object + req_name = canonicalize_name(req_name) + packages = get_installed_distributions( + local_only=False, + skip=(), + include_editables=True, + editables_only=False, + user_only=False, + paths=None, + ) + pkg_dict = {canonicalize_name(p.key): p for p in packages} + return pkg_dict.get(req_name) + + +def get_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Given a requirement name, return the installed Distribution object. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + + # Search the distribution by looking through the working set + dist = _search_distribution(req_name) + + # If distribution could not be found, call working_set.require + # to update the working set, and try to find the distribution + # again. + # This might happen for e.g. when you install a package + # twice, once using setup.py develop and again using setup.py install. + # Now when run pip uninstall twice, the package gets removed + # from the working set in the first uninstall, so we have to populate + # the working set again so that pip knows about it and the packages + # gets picked up and is successfully uninstalled the second time too. + if not dist: + try: + pkg_resources.working_set.require(req_name) + except pkg_resources.DistributionNotFound: + return None + return _search_distribution(req_name) + + +def egg_link_path(dist): + # type: (Distribution) -> Optional[str] + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + sites.append(site_packages) + if not virtualenv_no_global() and user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + return None + + +def dist_location(dist): + # type: (Distribution) -> str + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + The returned location is normalized (in particular, with symlinks removed). + """ + egg_link = egg_link_path(dist) + if egg_link: + return normalize_path(egg_link) + return normalize_path(dist.location) + + +def write_output(msg, *args): + # type: (Any, Any) -> None + logger.info(msg, *args) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = iter(lines) + + def readline(self): + try: + return next(self._gen) + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +def captured_stderr(): + """ + See captured_stdout(). + """ + return captured_output('stderr') + + +def get_installed_version(dist_name, working_set=None): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + if working_set is None: + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) + + +# Simulates an enum +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + + +def build_netloc(host, port): + # type: (str, Optional[int]) -> str + """ + Build a netloc from a host-port pair + """ + if port is None: + return host + if ':' in host: + # Only wrap host with square brackets when it is IPv6 + host = '[{}]'.format(host) + return '{}:{}'.format(host, port) + + +def build_url_from_netloc(netloc, scheme='https'): + # type: (str, str) -> str + """ + Build a full URL from a netloc. + """ + if netloc.count(':') >= 2 and '@' not in netloc and '[' not in netloc: + # It must be a bare IPv6 address, so wrap it with brackets. + netloc = '[{}]'.format(netloc) + return '{}://{}'.format(scheme, netloc) + + +def parse_netloc(netloc): + # type: (str) -> Tuple[str, Optional[int]] + """ + Return the host-port pair from a netloc. + """ + url = build_url_from_netloc(netloc) + parsed = urllib_parse.urlparse(url) + return parsed.hostname, parsed.port + + +def split_auth_from_netloc(netloc): + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if '@' not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit('@', 1) + if ':' in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user_pass = auth.split(':', 1) + else: + user_pass = auth, None + + user_pass = tuple( + None if x is None else urllib_unquote(x) for x in user_pass + ) + + return netloc, user_pass + + +def redact_netloc(netloc): + # type: (str) -> str + """ + Replace the sensitive data in a netloc with "****", if it exists. + + For example: + - "user:pass@example.com" returns "user:****@example.com" + - "accesstoken@example.com" returns "****@example.com" + """ + netloc, (user, password) = split_auth_from_netloc(netloc) + if user is None: + return netloc + if password is None: + user = '****' + password = '' + else: + user = urllib_parse.quote(user) + password = ':****' + return '{user}{password}@{netloc}'.format(user=user, + password=password, + netloc=netloc) + + +def _transform_url(url, transform_netloc): + """Transform and replace netloc in a url. + + transform_netloc is a function taking the netloc and returning a + tuple. The first element of this tuple is the new netloc. The + entire tuple is returned. + + Returns a tuple containing the transformed url as item 0 and the + original tuple returned by transform_netloc as item 1. + """ + purl = urllib_parse.urlsplit(url) + netloc_tuple = transform_netloc(purl.netloc) + # stripped url + url_pieces = ( + purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl, netloc_tuple + + +def _get_netloc(netloc): + return split_auth_from_netloc(netloc) + + +def _redact_netloc(netloc): + return (redact_netloc(netloc),) + + +def split_auth_netloc_from_url(url): + # type: (str) -> Tuple[str, str, Tuple[str, str]] + """ + Parse a url into separate netloc, auth, and url with no auth. + + Returns: (url_without_auth, netloc, (username, password)) + """ + url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc) + return url_without_auth, netloc, auth + + +def remove_auth_from_url(url): + # type: (str) -> str + """Return a copy of url with 'username:password@' removed.""" + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + return _transform_url(url, _get_netloc)[0] + + +def redact_auth_from_url(url): + # type: (str) -> str + """Replace the password in a given url with ****.""" + return _transform_url(url, _redact_netloc)[0] + + +class HiddenText(object): + def __init__( + self, + secret, # type: str + redacted, # type: str + ): + # type: (...) -> None + self.secret = secret + self.redacted = redacted + + def __repr__(self): + # type: (...) -> str + return ''.format(str(self)) + + def __str__(self): + # type: (...) -> str + return self.redacted + + # This is useful for testing. + def __eq__(self, other): + # type: (Any) -> bool + if type(self) != type(other): + return False + + # The string being used for redaction doesn't also have to match, + # just the raw, original string. + return (self.secret == other.secret) + + # We need to provide an explicit __ne__ implementation for Python 2. + # TODO: remove this when we drop PY2 support. + def __ne__(self, other): + # type: (Any) -> bool + return not self == other + + +def hide_value(value): + # type: (str) -> HiddenText + return HiddenText(value, redacted='****') + + +def hide_url(url): + # type: (str) -> HiddenText + redacted = redact_auth_from_url(url) + return HiddenText(url, redacted=redacted) + + +def protect_pip_from_modification_on_windows(modifying_pip): + # type: (bool) -> None + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip.exe", + "pip{}.exe".format(sys.version_info[0]), + "pip{}.{}.exe".format(*sys.version_info[:2]) + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and + WINDOWS and + os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [ + sys.executable, "-m", "pip" + ] + sys.argv[1:] + raise CommandError( + 'To modify pip, please run the following command:\n{}' + .format(" ".join(new_command)) + ) + + +def is_console_interactive(): + # type: () -> bool + """Is this console interactive? + """ + return sys.stdin is not None and sys.stdin.isatty() + + +def hash_file(path, blocksize=1 << 20): + # type: (Text, int) -> Tuple[Any, int] + """Return (hash, length) for path using hashlib.sha256() + """ + + h = hashlib.sha256() + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + return h, length + + +def is_wheel_installed(): + """ + Return whether the wheel package is installed. + """ + try: + import wheel # noqa: F401 + except ImportError: + return False + + return True + + +def pairwise(iterable): + # type: (Iterable[Any]) -> Iterator[Tuple[Any, Any]] + """ + Return paired elements. + + For example: + s -> (s0, s1), (s2, s3), (s4, s5), ... + """ + iterable = iter(iterable) + return zip_longest(iterable, iterable) + + +def partition( + pred, # type: Callable[[T], bool] + iterable, # type: Iterable[T] +): + # type: (...) -> Tuple[Iterable[T], Iterable[T]] + """ + Use a predicate to partition entries into false entries and true entries, + like + + partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + """ + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/models.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..d1c2f22 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,44 @@ +"""Utilities for defining models +""" +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparison capabilities that is based on a key + """ + + __slots__ = ['_compare_key', '_defining_class'] + + def __init__(self, key, defining_class): + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self): + return hash(self._compare_key) + + def __lt__(self, other): + return self._compare(other, operator.__lt__) + + def __le__(self, other): + return self._compare(other, operator.__le__) + + def __gt__(self, other): + return self._compare(other, operator.__gt__) + + def __ge__(self, other): + return self._compare(other, operator.__ge__) + + def __eq__(self, other): + return self._compare(other, operator.__eq__) + + def __ne__(self, other): + return self._compare(other, operator.__ne__) + + def _compare(self, other, method): + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/packaging.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..68aa86e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import logging +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal.exceptions import NoneMetadataError +from pip._internal.utils.misc import display_path +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from email.message import Message + from pip._vendor.pkg_resources import Distribution + + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python, version_info): + # type: (Optional[str], Tuple[int, ...]) -> bool + """ + Check if the given Python version matches a "Requires-Python" specifier. + + :param version_info: A 3-tuple of ints representing a Python + major-minor-micro version to check (e.g. `sys.version_info[:3]`). + + :return: `True` if the given Python version satisfies the requirement. + Otherwise, return `False`. + + :raises InvalidSpecifier: If `requires_python` has an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + python_version = version.parse('.'.join(map(str, version_info))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + # type: (Distribution) -> Message + """ + :raises NoneMetadataError: if the distribution reports `has_metadata()` + True but `get_metadata()` returns None. + """ + metadata_name = 'METADATA' + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata(metadata_name)): + metadata = dist.get_metadata(metadata_name) + elif dist.has_metadata('PKG-INFO'): + metadata_name = 'PKG-INFO' + metadata = dist.get_metadata(metadata_name) + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + if metadata is None: + raise NoneMetadataError(dist, metadata_name) + + feed_parser = FeedParser() + # The following line errors out if with a "NoneType" TypeError if + # passed metadata=None. + feed_parser.feed(metadata) + return feed_parser.close() + + +def get_requires_python(dist): + # type: (pkg_resources.Distribution) -> Optional[str] + """ + Return the "Requires-Python" metadata for a distribution, or None + if not present. + """ + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + + if requires_python is not None: + # Convert to a str to satisfy the type checker, since requires_python + # can be a Header object. + requires_python = str(requires_python) + + return requires_python + + +def get_installer(dist): + # type: (Distribution) -> str + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/parallel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/parallel.py new file mode 100644 index 0000000..9fe1fe8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/parallel.py @@ -0,0 +1,107 @@ +"""Convenient parallelization of higher order functions. + +This module provides two helper functions, with appropriate fallbacks on +Python 2 and on systems lacking support for synchronization mechanisms: + +- map_multiprocess +- map_multithread + +These helpers work like Python 3's map, with two differences: + +- They don't guarantee the order of processing of + the elements of the iterable. +- The underlying process/thread pools chop the iterable into + a number of chunks, so that for very long iterables using + a large value for chunksize can make the job complete much faster + than using the default value of 1. +""" + +__all__ = ['map_multiprocess', 'map_multithread'] + +from contextlib import contextmanager +from multiprocessing import Pool as ProcessPool +from multiprocessing.dummy import Pool as ThreadPool + +from pip._vendor.requests.adapters import DEFAULT_POOLSIZE +from pip._vendor.six import PY2 +from pip._vendor.six.moves import map + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, Iterable, Iterator, Union, TypeVar + from multiprocessing import pool + + Pool = Union[pool.Pool, pool.ThreadPool] + S = TypeVar('S') + T = TypeVar('T') + +# On platforms without sem_open, multiprocessing[.dummy] Pool +# cannot be created. +try: + import multiprocessing.synchronize # noqa +except ImportError: + LACK_SEM_OPEN = True +else: + LACK_SEM_OPEN = False + +# Incredibly large timeout to work around bpo-8296 on Python 2. +TIMEOUT = 2000000 + + +@contextmanager +def closing(pool): + # type: (Pool) -> Iterator[Pool] + """Return a context manager making sure the pool closes properly.""" + try: + yield pool + finally: + # For Pool.imap*, close and join are needed + # for the returned iterator to begin yielding. + pool.close() + pool.join() + pool.terminate() + + +def _map_fallback(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Make an iterator applying func to each element in iterable. + + This function is the sequential fallback either on Python 2 + where Pool.imap* doesn't react to KeyboardInterrupt + or when sem_open is unavailable. + """ + return map(func, iterable) + + +def _map_multiprocess(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a process pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ProcessPool()) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +def _map_multithread(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a thread pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ThreadPool(DEFAULT_POOLSIZE)) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +if LACK_SEM_OPEN or PY2: + map_multiprocess = map_multithread = _map_fallback +else: + map_multiprocess = _map_multiprocess + map_multithread = _map_multithread diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py new file mode 100644 index 0000000..0bc129a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py @@ -0,0 +1,44 @@ +from pip._vendor.pkg_resources import yield_lines +from pip._vendor.six import ensure_str + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List + + +class DictMetadata(object): + """IMetadataProvider that reads metadata files from a dictionary. + """ + def __init__(self, metadata): + # type: (Dict[str, bytes]) -> None + self._metadata = metadata + + def has_metadata(self, name): + # type: (str) -> bool + return name in self._metadata + + def get_metadata(self, name): + # type: (str) -> str + try: + return ensure_str(self._metadata[name]) + except UnicodeDecodeError as e: + # Mirrors handling done in pkg_resources.NullProvider. + e.reason += " in {} file".format(name) + raise + + def get_metadata_lines(self, name): + # type: (str) -> Iterable[str] + return yield_lines(self.get_metadata(name)) + + def metadata_isdir(self, name): + # type: (str) -> bool + return False + + def metadata_listdir(self, name): + # type: (str) -> List[str] + return [] + + def run_script(self, script_name, namespace): + # type: (str, str) -> None + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..2a664b0 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,181 @@ +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + +# Shim to wrap setup.py invocation with setuptools +# +# We set sys.argv[0] to the path to the underlying setup.py file so +# setuptools / distutils don't take the path to the setup.py to be "-c" when +# invoking via the shim. This avoids e.g. the following manifest_maker +# warning: "warning: manifest_maker: standard file '-c' not found". +_SETUPTOOLS_SHIM = ( + "import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) + + +def make_setuptools_shim_args( + setup_py_path, # type: str + global_options=None, # type: Sequence[str] + no_user_config=False, # type: bool + unbuffered_output=False # type: bool +): + # type: (...) -> List[str] + """ + Get setuptools command arguments with shim wrapped setup file invocation. + + :param setup_py_path: The path to setup.py to be wrapped. + :param global_options: Additional global options. + :param no_user_config: If True, disables personal user configuration. + :param unbuffered_output: If True, adds the unbuffered switch to the + argument list. + """ + args = [sys.executable] + if unbuffered_output: + args += ["-u"] + args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)] + if global_options: + args += global_options + if no_user_config: + args += ["--no-user-cfg"] + return args + + +def make_setuptools_bdist_wheel_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + build_options, # type: Sequence[str] + destination_dir, # type: str +): + # type: (...) -> List[str] + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["bdist_wheel", "-d", destination_dir] + args += build_options + return args + + +def make_setuptools_clean_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["clean", "--all"] + return args + + +def make_setuptools_develop_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + no_user_config, # type: bool + prefix, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + ) + + args += ["develop", "--no-deps"] + + args += install_options + + if prefix: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + + if use_user_site: + args += ["--user", "--prefix="] + + return args + + +def make_setuptools_egg_info_args( + setup_py_path, # type: str + egg_info_dir, # type: Optional[str] + no_user_config, # type: bool +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, no_user_config=no_user_config + ) + + args += ["egg_info"] + + if egg_info_dir: + args += ["--egg-base", egg_info_dir] + + return args + + +def make_setuptools_install_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + record_filename, # type: str + root, # type: Optional[str] + prefix, # type: Optional[str] + header_dir, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool + no_user_config, # type: bool + pycompile # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + assert not (use_user_site and root) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + unbuffered_output=True + ) + args += ["install", "--record", record_filename] + args += ["--single-version-externally-managed"] + + if root is not None: + args += ["--root", root] + if prefix is not None: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + if use_user_site: + args += ["--user", "--prefix="] + + if pycompile: + args += ["--compile"] + else: + args += ["--no-compile"] + + if header_dir: + args += ["--install-headers", header_dir] + + args += install_options + + return args diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py new file mode 100644 index 0000000..d398e68 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py @@ -0,0 +1,280 @@ +from __future__ import absolute_import + +import logging +import os +import subprocess + +from pip._vendor.six.moves import shlex_quote + +from pip._internal.cli.spinners import SpinnerInterface, open_spinner +from pip._internal.exceptions import InstallationError +from pip._internal.utils.compat import console_to_str, str_to_display +from pip._internal.utils.logging import subprocess_logger +from pip._internal.utils.misc import HiddenText, path_to_display +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Iterable, List, Mapping, Optional, Text, Union, + ) + + CommandArgs = List[Union[str, HiddenText]] + + +LOG_DIVIDER = '----------------------------------------' + + +def make_command(*args): + # type: (Union[str, HiddenText, CommandArgs]) -> CommandArgs + """ + Create a CommandArgs object. + """ + command_args = [] # type: CommandArgs + for arg in args: + # Check for list instead of CommandArgs since CommandArgs is + # only known during type-checking. + if isinstance(arg, list): + command_args.extend(arg) + else: + # Otherwise, arg is str or HiddenText. + command_args.append(arg) + + return command_args + + +def format_command_args(args): + # type: (Union[List[str], CommandArgs]) -> str + """ + Format command arguments for display. + """ + # For HiddenText arguments, display the redacted form by calling str(). + # Also, we don't apply str() to arguments that aren't HiddenText since + # this can trigger a UnicodeDecodeError in Python 2 if the argument + # has type unicode and includes a non-ascii character. (The type + # checker doesn't ensure the annotations are correct in all cases.) + return ' '.join( + shlex_quote(str(arg)) if isinstance(arg, HiddenText) + else shlex_quote(arg) for arg in args + ) + + +def reveal_command_args(args): + # type: (Union[List[str], CommandArgs]) -> List[str] + """ + Return the arguments in their raw, unredacted form. + """ + return [ + arg.secret if isinstance(arg, HiddenText) else arg for arg in args + ] + + +def make_subprocess_output_error( + cmd_args, # type: Union[List[str], CommandArgs] + cwd, # type: Optional[str] + lines, # type: List[Text] + exit_status, # type: int +): + # type: (...) -> Text + """ + Create and return the error message to use to log a subprocess error + with command output. + + :param lines: A list of lines, each ending with a newline. + """ + command = format_command_args(cmd_args) + # Convert `command` and `cwd` to text (unicode in Python 2) so we can use + # them as arguments in the unicode format string below. This avoids + # "UnicodeDecodeError: 'ascii' codec can't decode byte ..." in Python 2 + # if either contains a non-ascii character. + command_display = str_to_display(command, desc='command bytes') + cwd_display = path_to_display(cwd) + + # We know the joined output value ends in a newline. + output = ''.join(lines) + msg = ( + # Use a unicode string to avoid "UnicodeEncodeError: 'ascii' + # codec can't encode character ..." in Python 2 when a format + # argument (e.g. `output`) has a non-ascii character. + u'Command errored out with exit status {exit_status}:\n' + ' command: {command_display}\n' + ' cwd: {cwd_display}\n' + 'Complete output ({line_count} lines):\n{output}{divider}' + ).format( + exit_status=exit_status, + command_display=command_display, + cwd_display=cwd_display, + line_count=len(lines), + output=output, + divider=LOG_DIVIDER, + ) + return msg + + +def call_subprocess( + cmd, # type: Union[List[str], CommandArgs] + show_stdout=False, # type: bool + cwd=None, # type: Optional[str] + on_returncode='raise', # type: str + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + command_desc=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + unset_environ=None, # type: Optional[Iterable[str]] + spinner=None, # type: Optional[SpinnerInterface] + log_failed_cmd=True # type: Optional[bool] +): + # type: (...) -> Text + """ + Args: + show_stdout: if true, use INFO to log the subprocess's stderr and + stdout streams. Otherwise, use DEBUG. Defaults to False. + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + log_failed_cmd: if false, failed commands are not logged, only raised. + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + if unset_environ is None: + unset_environ = [] + # Most places in pip use show_stdout=False. What this means is-- + # + # - We connect the child's output (combined stderr and stdout) to a + # single pipe, which we read. + # - We log this output to stderr at DEBUG level as it is received. + # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't + # requested), then we show a spinner so the user can still see the + # subprocess is in progress. + # - If the subprocess exits with an error, we log the output to stderr + # at ERROR level if it hasn't already been displayed to the console + # (e.g. if --verbose logging wasn't enabled). This way we don't log + # the output to the console twice. + # + # If show_stdout=True, then the above is still done, but with DEBUG + # replaced by INFO. + if show_stdout: + # Then log the subprocess output at INFO level. + log_subprocess = subprocess_logger.info + used_level = logging.INFO + else: + # Then log the subprocess output using DEBUG. This also ensures + # it will be logged to the log file (aka user_log), if enabled. + log_subprocess = subprocess_logger.debug + used_level = logging.DEBUG + + # Whether the subprocess will be visible in the console. + showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level + + # Only use the spinner if we're not showing the subprocess output + # and we have a spinner. + use_spinner = not showing_subprocess and spinner is not None + + if command_desc is None: + command_desc = format_command_args(cmd) + + log_subprocess("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stderr=subprocess.STDOUT, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, cwd=cwd, env=env, + ) + assert proc.stdin + assert proc.stdout + proc.stdin.close() + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + while True: + # The "line" value is a unicode string in Python 2. + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + + # Show the line immediately. + log_subprocess(line) + # Update the spinner. + if use_spinner: + assert spinner + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + proc_had_error = ( + proc.returncode and proc.returncode not in extra_ok_returncodes + ) + if use_spinner: + assert spinner + if proc_had_error: + spinner.finish("error") + else: + spinner.finish("done") + if proc_had_error: + if on_returncode == 'raise': + if not showing_subprocess and log_failed_cmd: + # Then the subprocess streams haven't been logged to the + # console yet. + msg = make_subprocess_output_error( + cmd_args=cmd, + cwd=cwd, + lines=all_output, + exit_status=proc.returncode, + ) + subprocess_logger.error(msg) + exc_msg = ( + 'Command errored out with exit status {}: {} ' + 'Check the logs for full command output.' + ).format(proc.returncode, command_desc) + raise InstallationError(exc_msg) + elif on_returncode == 'warn': + subprocess_logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, + proc.returncode, + cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode={!r}'.format( + on_returncode)) + return ''.join(all_output) + + +def runner_with_spinner_message(message): + # type: (str) -> Callable[..., None] + """Provide a subprocess_runner that shows a spinner message. + + Intended for use with for pep517's Pep517HookCaller. Thus, the runner has + an API that matches what's expected by Pep517HookCaller.subprocess_runner. + """ + + def runner( + cmd, # type: List[str] + cwd=None, # type: Optional[str] + extra_environ=None # type: Optional[Mapping[str, Any]] + ): + # type: (...) -> None + with open_spinner(message) as spinner: + call_subprocess( + cmd, + cwd=cwd, + extra_environ=extra_environ, + spinner=spinner, + ) + + return runner diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..03aa828 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,274 @@ +from __future__ import absolute_import + +import errno +import itertools +import logging +import os.path +import tempfile +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack +from pip._vendor.six import ensure_text + +from pip._internal.utils.misc import enum, rmtree +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, Optional, TypeVar, Union + + _T = TypeVar('_T', bound='TempDirectory') + + +logger = logging.getLogger(__name__) + + +# Kinds of temporary directories. Only needed for ones that are +# globally-managed. +tempdir_kinds = enum( + BUILD_ENV="build-env", + EPHEM_WHEEL_CACHE="ephem-wheel-cache", + REQ_BUILD="req-build", +) + + +_tempdir_manager = None # type: Optional[ExitStack] + + +@contextmanager +def global_tempdir_manager(): + # type: () -> Iterator[None] + global _tempdir_manager + with ExitStack() as stack: + old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack + try: + yield + finally: + _tempdir_manager = old_tempdir_manager + + +class TempDirectoryTypeRegistry(object): + """Manages temp directory behavior + """ + + def __init__(self): + # type: () -> None + self._should_delete = {} # type: Dict[str, bool] + + def set_delete(self, kind, value): + # type: (str, bool) -> None + """Indicate whether a TempDirectory of the given kind should be + auto-deleted. + """ + self._should_delete[kind] = value + + def get_delete(self, kind): + # type: (str) -> bool + """Get configured auto-delete flag for a given TempDirectory type, + default True. + """ + return self._should_delete.get(kind, True) + + +_tempdir_registry = None # type: Optional[TempDirectoryTypeRegistry] + + +@contextmanager +def tempdir_registry(): + # type: () -> Iterator[TempDirectoryTypeRegistry] + """Provides a scoped global tempdir registry that can be used to dictate + whether directories should be deleted. + """ + global _tempdir_registry + old_tempdir_registry = _tempdir_registry + _tempdir_registry = TempDirectoryTypeRegistry() + try: + yield _tempdir_registry + finally: + _tempdir_registry = old_tempdir_registry + + +class _Default(object): + pass + + +_default = _Default() + + +class TempDirectory(object): + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + cleanup() + Deletes the temporary directory + + When used as a context manager, if the delete attribute is True, on + exiting the context the temporary directory is deleted. + """ + + def __init__( + self, + path=None, # type: Optional[str] + delete=_default, # type: Union[bool, None, _Default] + kind="temp", # type: str + globally_managed=False, # type: bool + ): + super(TempDirectory, self).__init__() + + if delete is _default: + if path is not None: + # If we were given an explicit directory, resolve delete option + # now. + delete = False + else: + # Otherwise, we wait until cleanup and see what + # tempdir_registry says. + delete = None + + if path is None: + path = self._create(kind) + + self._path = path + self._deleted = False + self.delete = delete + self.kind = kind + + if globally_managed: + assert _tempdir_manager is not None + _tempdir_manager.enter_context(self) + + @property + def path(self): + # type: () -> str + assert not self._deleted, ( + "Attempted to access deleted path: {}".format(self._path) + ) + return self._path + + def __repr__(self): + # type: () -> str + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + # type: (_T) -> _T + return self + + def __exit__(self, exc, value, tb): + # type: (Any, Any, Any) -> None + if self.delete is not None: + delete = self.delete + elif _tempdir_registry: + delete = _tempdir_registry.get_delete(self.kind) + else: + delete = True + + if delete: + self.cleanup() + + def _create(self, kind): + # type: (str) -> str + """Create a temporary directory and store its path in self.path + """ + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + logger.debug("Created temporary directory: %s", path) + return path + + def cleanup(self): + # type: () -> None + """Remove the temporary directory created and reset state + """ + self._deleted = True + if os.path.exists(self._path): + # Make sure to pass unicode on Python 2 to make the contents also + # use unicode, ensuring non-ASCII names and can be represented. + rmtree(ensure_text(self._path)) + + +class AdjacentTempDirectory(TempDirectory): + """Helper class that creates a temporary directory adjacent to a real one. + + Attributes: + original + The original directory to create a temp directory for. + path + After calling create() or entering, contains the full + path to the temporary directory. + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + """ + # The characters that may be used to name the temp directory + # We always prepend a ~ and then rotate through these until + # a usable name is found. + # pkg_resources raises a different error for .dist-info folder + # with leading '-' and invalid metadata + LEADING_CHARS = "-~.=%0123456789" + + def __init__(self, original, delete=None): + # type: (str, Optional[bool]) -> None + self.original = original.rstrip('/\\') + super(AdjacentTempDirectory, self).__init__(delete=delete) + + @classmethod + def _generate_names(cls, name): + # type: (str) -> Iterator[str] + """Generates a series of temporary names. + + The algorithm replaces the leading characters in the name + with ones that are valid filesystem characters, but are not + valid package names (for both Python and pip definitions of + package). + """ + for i in range(1, len(name)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i - 1): + new_name = '~' + ''.join(candidate) + name[i:] + if new_name != name: + yield new_name + + # If we make it this far, we will have to make a longer name + for i in range(len(cls.LEADING_CHARS)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i): + new_name = '~' + ''.join(candidate) + name + if new_name != name: + yield new_name + + def _create(self, kind): + # type: (str) -> str + root, name = os.path.split(self.original) + for candidate in self._generate_names(name): + path = os.path.join(root, candidate) + try: + os.mkdir(path) + except OSError as ex: + # Continue if the name exists already + if ex.errno != errno.EEXIST: + raise + else: + path = os.path.realpath(path) + break + else: + # Final fallback on the default behavior. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + + logger.debug("Created temporary directory: %s", path) + return path diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/typing.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000..8505a29 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,38 @@ +"""For neatly implementing static typing in pip. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In pip, all static-typing related imports should be guarded as follows: + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False + + +if MYPY_CHECK_RUNNING: + from typing import cast +else: + # typing's cast() is needed at runtime, but we don't want to import typing. + # Thus, we use a dummy no-op version, which we tell mypy to ignore. + def cast(type_, value): # type: ignore + return value diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py new file mode 100644 index 0000000..620f31e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py @@ -0,0 +1,281 @@ +"""Utilities related archives. +""" + +from __future__ import absolute_import + +import logging +import os +import shutil +import stat +import tarfile +import zipfile + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.filetypes import ( + BZ2_EXTENSIONS, + TAR_EXTENSIONS, + XZ_EXTENSIONS, + ZIP_EXTENSIONS, +) +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, List, Optional, Text, Union + from zipfile import ZipInfo + + +logger = logging.getLogger(__name__) + + +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS + +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def current_umask(): + # type: () -> int + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def split_leading_dir(path): + # type: (Union[str, Text]) -> List[Union[str, Text]] + path = path.lstrip('/').lstrip('\\') + if ( + '/' in path and ( + ('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path + ) + ): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return [path, ''] + + +def has_leading_dir(paths): + # type: (Iterable[Union[str, Text]]) -> bool + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def is_within_directory(directory, target): + # type: ((Union[str, Text]), (Union[str, Text])) -> bool + """ + Return true if the absolute path of target is within the directory + """ + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + return prefix == abs_directory + + +def set_extracted_file_to_default_mode_plus_executable(path): + # type: (Union[str, Text]) -> None + """ + Make file present at path have execute for user/group/world + (chmod +x) is no-op on windows per python docs + """ + os.chmod(path, (0o777 & ~current_umask() | 0o111)) + + +def zip_item_is_executable(info): + # type: (ZipInfo) -> bool + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + return bool(mode and stat.S_ISREG(mode) and mode & 0o111) + + +def unzip_file(filename, location, flatten=True): + # type: (str, str, bool) -> None + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not is_within_directory(location, fn): + message = ( + 'The zip file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError(message.format(filename, fn, location)) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + # Don't use read() to avoid allocating an arbitrarily large + # chunk of memory for the file's content + fp = zip.open(name) + try: + with open(fn, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + finally: + fp.close() + if zip_item_is_executable(info): + set_extracted_file_to_default_mode_plus_executable(fn) + finally: + zipfp.close() + + +def untar_file(filename, location): + # type: (str, str) -> None + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + leading = has_leading_dir([ + member.name for member in tar.getmembers() + ]) + for member in tar.getmembers(): + fn = member.name + if leading: + # https://github.com/python/mypy/issues/1174 + fn = split_leading_dir(fn)[1] # type: ignore + path = os.path.join(location, fn) + if not is_within_directory(location, path): + message = ( + 'The tar file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError( + message.format(filename, path, location) + ) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + # https://github.com/python/typeshed/issues/2673 + tar._extract_member(member, path) # type: ignore + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + assert fp is not None + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + # https://github.com/python/typeshed/issues/2673 + tar.utime(member, path) # type: ignore + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + set_extracted_file_to_default_mode_plus_executable(path) + finally: + tar.close() + + +def unpack_file( + filename, # type: str + location, # type: str + content_type=None, # type: Optional[str] +): + # type: (...) -> None + filename = os.path.realpath(filename) + if ( + content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename) + ): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif ( + content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS + ) + ): + untar_file(filename, location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of {}'.format(location) + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/urls.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/urls.py new file mode 100644 index 0000000..f37bc8f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/urls.py @@ -0,0 +1,55 @@ +import os +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Union + + +def get_url_scheme(url): + # type: (Union[str, Text]) -> Optional[Text] + if ':' not in url: + return None + return url.split(':', 1)[0].lower() + + +def path_to_url(path): + # type: (Union[str, Text]) -> str + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def url_to_path(url): + # type: (str) -> str + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not {url!r})" + .format(**locals())) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + if not netloc or netloc == 'localhost': + # According to RFC 8089, same as empty authority. + netloc = '' + elif sys.platform == 'win32': + # If we have a UNC path, prepend UNC share notation. + netloc = '\\\\' + netloc + else: + raise ValueError( + 'non-local file URIs are not supported on this platform: {url!r}' + .format(**locals()) + ) + + path = urllib_request.url2pathname(netloc + path) + return path diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py new file mode 100644 index 0000000..4a78128 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py @@ -0,0 +1,119 @@ +from __future__ import absolute_import + +import io +import logging +import os +import re +import site +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) +_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( + r"include-system-site-packages\s*=\s*(?Ptrue|false)" +) + + +def _running_under_venv(): + # type: () -> bool + """Checks if sys.base_prefix and sys.prefix match. + + This handles PEP 405 compliant virtual environments. + """ + return sys.prefix != getattr(sys, "base_prefix", sys.prefix) + + +def _running_under_regular_virtualenv(): + # type: () -> bool + """Checks if sys.real_prefix is set. + + This handles virtual environments created with pypa's virtualenv. + """ + # pypa/virtualenv case + return hasattr(sys, 'real_prefix') + + +def running_under_virtualenv(): + # type: () -> bool + """Return True if we're running inside a virtualenv, False otherwise. + """ + return _running_under_venv() or _running_under_regular_virtualenv() + + +def _get_pyvenv_cfg_lines(): + # type: () -> Optional[List[str]] + """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines + + Returns None, if it could not read/access the file. + """ + pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') + try: + # Although PEP 405 does not specify, the built-in venv module always + # writes with UTF-8. (pypa/pip#8717) + with io.open(pyvenv_cfg_file, encoding='utf-8') as f: + return f.read().splitlines() # avoids trailing newlines + except IOError: + return None + + +def _no_global_under_venv(): + # type: () -> bool + """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion + + PEP 405 specifies that when system site-packages are not supposed to be + visible from a virtual environment, `pyvenv.cfg` must contain the following + line: + + include-system-site-packages = false + + Additionally, log a warning if accessing the file fails. + """ + cfg_lines = _get_pyvenv_cfg_lines() + if cfg_lines is None: + # We're not in a "sane" venv, so assume there is no system + # site-packages access (since that's PEP 405's default state). + logger.warning( + "Could not access 'pyvenv.cfg' despite a virtual environment " + "being active. Assuming global site-packages is not accessible " + "in this environment." + ) + return True + + for line in cfg_lines: + match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) + if match is not None and match.group('value') == 'false': + return True + return False + + +def _no_global_under_regular_virtualenv(): + # type: () -> bool + """Check if "no-global-site-packages.txt" exists beside site.py + + This mirrors logic in pypa/virtualenv for determining whether system + site-packages are visible in the virtual environment. + """ + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_site_packages_file = os.path.join( + site_mod_dir, 'no-global-site-packages.txt', + ) + return os.path.exists(no_global_site_packages_file) + + +def virtualenv_no_global(): + # type: () -> bool + """Returns a boolean, whether running in venv with no system site-packages. + """ + # PEP 405 compliance needs to be checked first since virtualenv >=20 would + # return True for both checks, but is only able to use the PEP 405 config. + if _running_under_venv(): + return _no_global_under_venv() + + if _running_under_regular_virtualenv(): + return _no_global_under_regular_virtualenv() + + return False diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/wheel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/wheel.py new file mode 100644 index 0000000..9ce371c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/utils/wheel.py @@ -0,0 +1,225 @@ +"""Support functions for working with wheel files. +""" + +from __future__ import absolute_import + +import logging +from email.parser import Parser +from zipfile import ZipFile + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import DistInfoDistribution +from pip._vendor.six import PY2, ensure_str + +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.utils.pkg_resources import DictMetadata +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from email.message import Message + from typing import Dict, Tuple + + from pip._vendor.pkg_resources import Distribution + +if PY2: + from zipfile import BadZipfile as BadZipFile +else: + from zipfile import BadZipFile + + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +class WheelMetadata(DictMetadata): + """Metadata provider that maps metadata decoding exceptions to our + internal exception type. + """ + def __init__(self, metadata, wheel_name): + # type: (Dict[str, bytes], str) -> None + super(WheelMetadata, self).__init__(metadata) + self._wheel_name = wheel_name + + def get_metadata(self, name): + # type: (str) -> str + try: + return super(WheelMetadata, self).get_metadata(name) + except UnicodeDecodeError as e: + # Augment the default error with the origin of the file. + raise UnsupportedWheel( + "Error decoding metadata for {}: {}".format( + self._wheel_name, e + ) + ) + + +def pkg_resources_distribution_for_wheel(wheel_zip, name, location): + # type: (ZipFile, str, str) -> Distribution + """Get a pkg_resources distribution given a wheel. + + :raises UnsupportedWheel: on any errors + """ + info_dir, _ = parse_wheel(wheel_zip, name) + + metadata_files = [ + p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) + ] + + metadata_text = {} # type: Dict[str, bytes] + for path in metadata_files: + # If a flag is set, namelist entries may be unicode in Python 2. + # We coerce them to native str type to match the types used in the rest + # of the code. This cannot fail because unicode can always be encoded + # with UTF-8. + full_path = ensure_str(path) + _, metadata_name = full_path.split("/", 1) + + try: + metadata_text[metadata_name] = read_wheel_metadata_file( + wheel_zip, full_path + ) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + metadata = WheelMetadata(metadata_text, location) + + return DistInfoDistribution( + location=location, metadata=metadata, project_name=name + ) + + +def parse_wheel(wheel_zip, name): + # type: (ZipFile, str) -> Tuple[str, Message] + """Extract information from the provided wheel, ensuring it meets basic + standards. + + Returns the name of the .dist-info directory and the parsed WHEEL metadata. + """ + try: + info_dir = wheel_dist_info_dir(wheel_zip, name) + metadata = wheel_metadata(wheel_zip, info_dir) + version = wheel_version(metadata) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + check_compatibility(version, name) + + return info_dir, metadata + + +def wheel_dist_info_dir(source, name): + # type: (ZipFile, str) -> str + """Returns the name of the contained .dist-info directory. + + Raises AssertionError or UnsupportedWheel if not found, >1 found, or + it doesn't match the provided name. + """ + # Zip file path separators must be / + subdirs = set(p.split("/", 1)[0] for p in source.namelist()) + + info_dirs = [s for s in subdirs if s.endswith('.dist-info')] + + if not info_dirs: + raise UnsupportedWheel(".dist-info directory not found") + + if len(info_dirs) > 1: + raise UnsupportedWheel( + "multiple .dist-info directories found: {}".format( + ", ".join(info_dirs) + ) + ) + + info_dir = info_dirs[0] + + info_dir_name = canonicalize_name(info_dir) + canonical_name = canonicalize_name(name) + if not info_dir_name.startswith(canonical_name): + raise UnsupportedWheel( + ".dist-info directory {!r} does not start with {!r}".format( + info_dir, canonical_name + ) + ) + + # Zip file paths can be unicode or str depending on the zip entry flags, + # so normalize it. + return ensure_str(info_dir) + + +def read_wheel_metadata_file(source, path): + # type: (ZipFile, str) -> bytes + try: + return source.read(path) + # BadZipFile for general corruption, KeyError for missing entry, + # and RuntimeError for password-protected files + except (BadZipFile, KeyError, RuntimeError) as e: + raise UnsupportedWheel( + "could not read {!r} file: {!r}".format(path, e) + ) + + +def wheel_metadata(source, dist_info_dir): + # type: (ZipFile, str) -> Message + """Return the WHEEL metadata of an extracted wheel, if possible. + Otherwise, raise UnsupportedWheel. + """ + path = "{}/WHEEL".format(dist_info_dir) + # Zip file path separators must be / + wheel_contents = read_wheel_metadata_file(source, path) + + try: + wheel_text = ensure_str(wheel_contents) + except UnicodeDecodeError as e: + raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) + + # FeedParser (used by Parser) does not raise any exceptions. The returned + # message may have .defects populated, but for backwards-compatibility we + # currently ignore them. + return Parser().parsestr(wheel_text) + + +def wheel_version(wheel_data): + # type: (Message) -> Tuple[int, ...] + """Given WHEEL metadata, return the parsed Wheel-Version. + Otherwise, raise UnsupportedWheel. + """ + version_text = wheel_data["Wheel-Version"] + if version_text is None: + raise UnsupportedWheel("WHEEL is missing Wheel-Version") + + version = version_text.strip() + + try: + return tuple(map(int, version.split('.'))) + except ValueError: + raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) + + +def check_compatibility(version, name): + # type: (Tuple[int, ...], str) -> None + """Raises errors or warns if called with an incompatible Wheel-Version. + + pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "{}'s Wheel-Version ({}) is not compatible with this version " + "of pip".format(name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..2a4eb13 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,15 @@ +# Expose a limited set of classes and functions so callers outside of +# the vcs package don't need to import deeper than `pip._internal.vcs`. +# (The test directory and imports protected by MYPY_CHECK_RUNNING may +# still need to import from a vcs sub-package.) +# Import all vcs modules to register each VCS in the VcsSupport object. +import pip._internal.vcs.bazaar +import pip._internal.vcs.git +import pip._internal.vcs.mercurial +import pip._internal.vcs.subversion # noqa: F401 +from pip._internal.vcs.versioncontrol import ( # noqa: F401 + RemoteNotFoundError, + is_url, + make_vcs_requirement_url, + vcs, +) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d89eeca4ac19a5bf144d4ae787ca7ab4dcb2592 GIT binary patch literal 520 zcmZWlu};G<5Otb1O)EMfq)ITbc1S!hAytS07(0MivP5ojYE5H1Jv$Zf0sI4>z%O}a z;um1y(pCtmw)~#od-CagVl)~uTJNhLAv=tHw&MRHk6qKTQ-YF}LB+CQ%bh)kd+u(z zQ}tAoMXI0mRh-3YkPQNQ^}r_K7gb=hN!K6WgE9_g#@(4lm$!%peI#w(VCBb3tRUYM zHb+=D5+ax4&GW6qG)@d%a1Jt5Q$xP?sRS5JUCH~|vUP$Lp4{ZflG$=*o&kjS| z%r}5m8tu;y(|uGJ?a)-%4hh2+x-IkwJ{TUg_ZGh2_@kv_yjTh?7DktX-gc!z(#&<hb literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb5eb15c861b72bda0e63fbef8745a60177cc7b1 GIT binary patch literal 3784 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 D@%XDQ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d1afd70952a17754fe592c099047de149fec568 GIT binary patch literal 5092 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..94408c5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,119 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import display_path, rmtree +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import VersionControl, vcs + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, *args, **kwargs): + super(Bazaar, self).__init__(*args, **kwargs) + # This is only needed for python <2.7.5 + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """ + Export the Bazaar repository at the url to the destination location + """ + # Remove the location to make sure Bazaar can export it correctly + if os.path.exists(location): + rmtree(location) + + url, rev_options = self.get_url_rev_options(url) + self.run_command( + make_command('export', location, url, rev_options.to_args()) + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ( + make_command('branch', '-q', rev_options.to_args(), url, dest) + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(make_command('switch', url), cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command('pull', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev, user_pass = super(Bazaar, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev, user_pass + + @classmethod + def get_remote_url(cls, location): + urls = cls.run_command(['info'], cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if cls._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + @classmethod + def get_revision(cls, location): + revision = cls.run_command( + ['revno'], cwd=location, + ) + return revision.splitlines()[-1] + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/git.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..a9c7fb6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,397 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os.path +import re + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import BadCommand, SubProcessError +from pip._internal.utils.misc import display_path, hide_url +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol import ( + RemoteNotFoundError, + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') + + +def looks_like_hash(sha): + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + _, rev_options = self.get_url_rev_options(hide_url(url)) + if not rev_options.rev: + return False + if not self.is_commit_id_equal(dest, rev_options.rev): + # the current commit is different from rev, + # which means rev was something else than a commit hash + return False + # return False in the rare case rev is both a commit hash + # and a tag or a branch; we don't want to cache in that case + # because that branch/tag could point to something else in the future + is_tag_or_branch = bool( + self.get_revision_sha(dest, rev_options.rev)[0] + ) + return not is_tag_or_branch + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command(['version']) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):].split()[0] + else: + version = '' + # get first 3 positions of the git version because + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + @classmethod + def get_current_branch(cls, location): + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + # git-symbolic-ref exits with empty stdout if "HEAD" is a detached + # HEAD rather than a symbolic ref. In addition, the -q causes the + # command to exit with status code 1 instead of 128 in this case + # and to suppress the message to stderr. + args = ['symbolic-ref', '-q', 'HEAD'] + output = cls.run_command( + args, extra_ok_returncodes=(1, ), cwd=location, + ) + ref = output.strip() + + if ref.startswith('refs/heads/'): + return ref[len('refs/heads/'):] + + return None + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the Git repository at the url to the destination location""" + if not location.endswith('/'): + location = location + '/' + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path, url=url) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + cwd=temp_dir.path + ) + + @classmethod + def get_revision_sha(cls, dest, rev): + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + + output = '' + try: + output = cls.run_command(['show-ref', rev], cwd=dest) + except SubProcessError: + pass + + refs = {} + for line in output.strip().splitlines(): + try: + sha, ref = line.split() + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError('unexpected show-ref line: {!r}'.format(line)) + + refs[ref] = sha + + branch_ref = 'refs/remotes/origin/{}'.format(rev) + tag_ref = 'refs/tags/{}'.format(rev) + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + @classmethod + def resolve_revision(cls, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> RevOptions + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + # The arg_rev property's implementation for Git ensures that the + # rev return value is always non-None. + assert rev is not None + + sha, is_branch = cls.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not rev.startswith('refs/'): + return rev_options + + # If it looks like a ref, we have to fetch it explicitly. + cls.run_command( + make_command('fetch', '-q', url, rev_options.to_args()), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = cls.get_revision(dest, rev='FETCH_HEAD') + rev_options = rev_options.make_new(sha) + + return rev_options + + @classmethod + def is_commit_id_equal(cls, dest, name): + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return cls.get_revision(dest) == name + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest)) + self.run_command(make_command('clone', '-q', url, dest)) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, 'branch_name', None) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = make_command( + 'checkout', '-q', rev_options.to_args(), + ) + self.run_command(cmd_args, cwd=dest) + elif self.get_current_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = 'origin/{}'.format(branch_name) + cmd_args = [ + 'checkout', '-b', branch_name, '--track', track_branch, + ] + self.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command( + make_command('config', 'remote.origin.url', url), + cwd=dest, + ) + cmd_args = make_command('checkout', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + @classmethod + def get_remote_url(cls, location): + """ + Return URL of the first remote encountered. + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + # We need to pass 1 for extra_ok_returncodes since the command + # exits with return code 1 if there are no matching lines. + stdout = cls.run_command( + ['config', '--get-regexp', r'remote\..*\.url'], + extra_ok_returncodes=(1, ), cwd=location, + ) + remotes = stdout.splitlines() + try: + found_remote = remotes[0] + except IndexError: + raise RemoteNotFoundError + + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + @classmethod + def get_revision(cls, location, rev=None): + if rev is None: + rev = 'HEAD' + current_rev = cls.run_command( + ['rev-parse', rev], cwd=location, + ) + return current_rev.strip() + + @classmethod + def get_subdirectory(cls, location): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # find the repo root + git_dir = cls.run_command( + ['rev-parse', '--git-dir'], + cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + repo_root = os.path.abspath(os.path.join(git_dir, '..')) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + if '://' not in url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + url = url.replace('ssh://', '') + else: + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + + return url, rev, user_pass + + @classmethod + def update_submodules(cls, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + cls.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def get_repository_root(cls, location): + loc = super(Git, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['rev-parse', '--show-toplevel'], + cwd=location, + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return None + except SubProcessError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Git) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..69763fe --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,158 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import BadCommand, SubProcessError +from pip._internal.utils.misc import display_path +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import ( + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import RevOptions + + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ( + 'hg', 'hg+file', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http', + ) + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path, url=url) + + self.run_command( + ['archive', location], cwd=temp_dir.path + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(make_command('clone', '--noupdate', '-q', url, dest)) + self.run_command( + make_command('update', '-q', rev_options.to_args()), + cwd=dest, + ) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.RawConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url.secret) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + cmd_args = make_command('update', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = make_command('update', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_remote_url(cls, location): + url = cls.run_command( + ['showconfig', 'paths.default'], + cwd=location).strip() + if cls._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + @classmethod + def get_revision(cls, location): + """ + Return the repository-local changeset revision number, as an integer. + """ + current_revision = cls.run_command( + ['parents', '--template={rev}'], cwd=location).strip() + return current_revision + + @classmethod + def get_requirement_revision(cls, location): + """ + Return the changeset identification hash, as a 40-character + hexadecimal string + """ + current_rev_hash = cls.run_command( + ['parents', '--template={node}'], + cwd=location).strip() + return current_rev_hash + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + @classmethod + def get_subdirectory(cls, location): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # find the repo root + repo_root = cls.run_command( + ['root'], cwd=location).strip() + if not os.path.isabs(repo_root): + repo_root = os.path.abspath(os.path.join(location, repo_root)) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_repository_root(cls, location): + loc = super(Mercurial, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['root'], + cwd=location, + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under hg control " + "because hg is not available", location) + return None + except SubProcessError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Mercurial) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..ab13497 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,336 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os +import re + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, + is_console_interactive, + rmtree, + split_auth_from_netloc, +) +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol import VersionControl, vcs + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'(.*)') + + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.subprocess import CommandArgs + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + return True + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + @classmethod + def get_revision(cls, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, _ in os.walk(location): + if cls.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(cls.dirname) + entries_fn = os.path.join(base, cls.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = cls._get_svn_url_rev(base) + + if base == location: + base = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, cls).get_netloc_and_auth(netloc, scheme) + + return split_auth_from_netloc(netloc) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + extra_args = [] # type: CommandArgs + if username: + extra_args += ['--username', username] + if password: + extra_args += ['--password', password] + + return extra_args + + @classmethod + def get_remote_url(cls, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return cls._get_svn_url_rev(location)[0] + + @classmethod + def _get_svn_url_rev(cls, location): + from pip._internal.exceptions import SubProcessError + + entries_path = os.path.join(location, cls.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('= 1.7 + # Note that using get_remote_call_options is not necessary here + # because `svn info` is being run against a local directory. + # We don't need to worry about making sure interactive mode + # is being used to prompt for passwords, because passwords + # are only potentially needed for remote server requests. + xml = cls.run_command( + ['info', '--xml', location], + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except SubProcessError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + def __init__(self, use_interactive=None): + # type: (bool) -> None + if use_interactive is None: + use_interactive = is_console_interactive() + self.use_interactive = use_interactive + + # This member is used to cache the fetched version of the current + # ``svn`` client. + # Special value definitions: + # None: Not evaluated yet. + # Empty tuple: Could not parse version. + self._vcs_version = None # type: Optional[Tuple[int, ...]] + + super(Subversion, self).__init__() + + def call_vcs_version(self): + # type: () -> Tuple[int, ...] + """Query the version of the currently installed Subversion client. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + # Example versions: + # svn, version 1.10.3 (r1842928) + # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 + # svn, version 1.7.14 (r1542130) + # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu + # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) + # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 + version_prefix = 'svn, version ' + version = self.run_command(['--version']) + + if not version.startswith(version_prefix): + return () + + version = version[len(version_prefix):].split()[0] + version_list = version.partition('-')[0].split('.') + try: + parsed_version = tuple(map(int, version_list)) + except ValueError: + return () + + return parsed_version + + def get_vcs_version(self): + # type: () -> Tuple[int, ...] + """Return the version of the currently installed Subversion client. + + If the version of the Subversion client has already been queried, + a cached value will be used. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + if self._vcs_version is not None: + # Use cached version, if available. + # If parsing the version failed previously (empty tuple), + # do not attempt to parse it again. + return self._vcs_version + + vcs_version = self.call_vcs_version() + self._vcs_version = vcs_version + return vcs_version + + def get_remote_call_options(self): + # type: () -> CommandArgs + """Return options to be used on calls to Subversion that contact the server. + + These options are applicable for the following ``svn`` subcommands used + in this class. + + - checkout + - export + - switch + - update + + :return: A list of command line arguments to pass to ``svn``. + """ + if not self.use_interactive: + # --non-interactive switch is available since Subversion 0.14.4. + # Subversion < 1.8 runs in interactive mode by default. + return ['--non-interactive'] + + svn_version = self.get_vcs_version() + # By default, Subversion >= 1.8 runs in non-interactive mode if + # stdin is not a TTY. Since that is how pip invokes SVN, in + # call_subprocess(), pip must pass --force-interactive to ensure + # the user can be prompted for a password, if required. + # SVN added the --force-interactive option in SVN 1.8. Since + # e.g. RHEL/CentOS 7, which is supported until 2024, ships with + # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip + # can't safely add the option if the SVN version is < 1.8 (or unknown). + if svn_version >= (1, 8): + return ['--force-interactive'] + + return [] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(url) + + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + cmd_args = make_command( + 'export', self.get_remote_call_options(), + rev_options.to_args(), url, location, + ) + self.run_command(cmd_args) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = make_command( + 'checkout', '-q', self.get_remote_call_options(), + rev_options.to_args(), url, dest, + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'switch', self.get_remote_call_options(), rev_options.to_args(), + url, dest, + ) + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'update', self.get_remote_call_options(), rev_options.to_args(), + dest, + ) + self.run_command(cmd_args) + + +vcs.register(Subversion) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py new file mode 100644 index 0000000..96f830f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py @@ -0,0 +1,811 @@ +"""Handles all VCS (version control) support""" + +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import subprocess +import sys + +from pip._vendor import pkg_resources +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import ( + BadCommand, + InstallationError, + SubProcessError, +) +from pip._internal.utils.compat import console_to_str, samefile +from pip._internal.utils.logging import subprocess_logger +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + hide_url, + hide_value, + rmtree, +) +from pip._internal.utils.subprocess import ( + format_command_args, + make_command, + make_subprocess_output_error, + reveal_command_args, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme + +if MYPY_CHECK_RUNNING: + from typing import ( + Dict, Iterable, Iterator, List, Optional, Text, Tuple, + Type, Union, Mapping, Any + ) + from pip._internal.utils.misc import HiddenText + from pip._internal.utils.subprocess import CommandArgs + + AuthInfo = Tuple[Optional[str], Optional[str]] + + +__all__ = ['vcs'] + + +logger = logging.getLogger(__name__) + + +def is_url(name): + # type: (Union[str, Text]) -> bool + """ + Return true if the name looks like a URL. + """ + scheme = get_url_scheme(name) + if scheme is None: + return False + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): + # type: (str, str, str, Optional[str]) -> str + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + project_name: the (unescaped) project name. + """ + egg_project_name = pkg_resources.to_filename(project_name) + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + +def call_subprocess( + cmd, # type: Union[List[str], CommandArgs] + cwd=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + log_failed_cmd=True # type: Optional[bool] +): + # type: (...) -> Text + """ + Args: + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + log_failed_cmd: if false, failed commands are not logged, + only raised. + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + + # log the subprocess output at DEBUG level. + log_subprocess = subprocess_logger.debug + + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + + # Whether the subprocess will be visible in the console. + showing_subprocess = True + + command_desc = format_command_args(cmd) + try: + proc = subprocess.Popen( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=cwd + ) + if proc.stdin: + proc.stdin.close() + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + while True: + # The "line" value is a unicode string in Python 2. + line = None + if proc.stdout: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + + # Show the line immediately. + log_subprocess(line) + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + + proc_had_error = ( + proc.returncode and proc.returncode not in extra_ok_returncodes + ) + if proc_had_error: + if not showing_subprocess and log_failed_cmd: + # Then the subprocess streams haven't been logged to the + # console yet. + msg = make_subprocess_output_error( + cmd_args=cmd, + cwd=cwd, + lines=all_output, + exit_status=proc.returncode, + ) + subprocess_logger.error(msg) + exc_msg = ( + 'Command errored out with exit status {}: {} ' + 'Check the logs for full command output.' + ).format(proc.returncode, command_desc) + raise SubProcessError(exc_msg) + return ''.join(all_output) + + +def find_path_to_setup_from_repo_root(location, repo_root): + # type: (str, str) -> Optional[str] + """ + Find the path to `setup.py` by searching up the filesystem from `location`. + Return the path to `setup.py` relative to `repo_root`. + Return None if `setup.py` is in `repo_root` or cannot be found. + """ + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + if samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) + + +class RemoteNotFoundError(Exception): + pass + + +class RevOptions(object): + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__( + self, + vc_class, # type: Type[VersionControl] + rev=None, # type: Optional[str] + extra_args=None, # type: Optional[CommandArgs] + ): + # type: (...) -> None + """ + Args: + vc_class: a VersionControl subclass. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vc_class = vc_class + self.branch_name = None # type: Optional[str] + + def __repr__(self): + # type: () -> str + return ''.format(self.vc_class.name, self.rev) + + @property + def arg_rev(self): + # type: () -> Optional[str] + if self.rev is None: + return self.vc_class.default_arg_rev + + return self.rev + + def to_args(self): + # type: () -> CommandArgs + """ + Return the VCS-specific command arguments. + """ + args = [] # type: CommandArgs + rev = self.arg_rev + if rev is not None: + args += self.vc_class.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self): + # type: () -> str + if not self.rev: + return '' + + return ' (to revision {})'.format(self.rev) + + def make_new(self, rev): + # type: (str) -> RevOptions + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport(object): + _registry = {} # type: Dict[str, VersionControl] + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # type: () -> None + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + # type: () -> Iterator[str] + return self._registry.__iter__() + + @property + def backends(self): + # type: () -> List[VersionControl] + return list(self._registry.values()) + + @property + def dirnames(self): + # type: () -> List[str] + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + # type: () -> List[str] + schemes = [] # type: List[str] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + # type: (Type[VersionControl]) -> None + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls() + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, name): + # type: (str) -> None + if name in self._registry: + del self._registry[name] + + def get_backend_for_dir(self, location): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object if a repository of that type is found + at the given directory. + """ + vcs_backends = {} + for vcs_backend in self._registry.values(): + repo_path = vcs_backend.get_repository_root(location) + if not repo_path: + continue + logger.debug('Determine that %s uses VCS: %s', + location, vcs_backend.name) + vcs_backends[repo_path] = vcs_backend + + if not vcs_backends: + return None + + # Choose the VCS in the inner-most directory. Since all repository + # roots found here would be either `location` or one of its + # parents, the longest path should have the most path components, + # i.e. the backend representing the inner-most repository. + inner_most_repo_path = max(vcs_backends, key=len) + return vcs_backends[inner_most_repo_path] + + def get_backend_for_scheme(self, scheme): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + for vcs_backend in self._registry.values(): + if scheme in vcs_backend.schemes: + return vcs_backend + return None + + def get_backend(self, name): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + name = name.lower() + return self._registry.get(name) + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + repo_name = '' + # List of supported schemes for this Version Control + schemes = () # type: Tuple[str, ...] + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ = () # type: Tuple[str, ...] + default_arg_rev = None # type: Optional[str] + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + # type: (str) -> bool + """ + Return whether the vcs prefix (e.g. "git+") should be added to a + repository's remote url when used in a requirement. + """ + return not remote_url.lower().startswith('{}:'.format(cls.name)) + + @classmethod + def get_subdirectory(cls, location): + # type: (str) -> Optional[str] + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir): + # type: (str) -> str + """ + Return the revision string that should be used in a requirement. + """ + return cls.get_revision(repo_dir) + + @classmethod + def get_src_requirement(cls, repo_dir, project_name): + # type: (str, str) -> Optional[str] + """ + Return the requirement string to use to redownload the files + currently at the given repository directory. + + Args: + project_name: the (unescaped) project name. + + The return value has a form similar to the following: + + {repository_url}@{revision}#egg={project_name} + """ + repo_url = cls.get_remote_url(repo_dir) + if repo_url is None: + return None + + if cls.should_add_vcs_url_prefix(repo_url): + repo_url = '{}+{}'.format(cls.name, repo_url) + + revision = cls.get_requirement_revision(repo_dir) + subdir = cls.get_subdirectory(repo_dir) + req = make_vcs_requirement_url(repo_url, revision, project_name, + subdir=subdir) + + return req + + @staticmethod + def get_base_rev_args(rev): + # type: (str) -> List[str] + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + """ + Return true if the commit hash checked out at dest matches + the revision in url. + + Always return False, if the VCS does not support immutable commit + hashes. + + This method does not check if there are local uncommitted changes + in dest after checkout, as pip currently has no use case for that. + """ + return False + + @classmethod + def make_rev_options(cls, rev=None, extra_args=None): + # type: (Optional[str], Optional[CommandArgs]) -> RevOptions + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(cls, rev, extra_args=extra_args) + + @classmethod + def _is_local_repository(cls, repo): + # type: (str) -> bool + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or bool(drive) + + def export(self, location, url): + # type: (str, HiddenText) -> None + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + + :param url: the repository URL starting with a vcs prefix. + """ + raise NotImplementedError + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + # type: (str, str) -> Tuple[str, Tuple[Optional[str], Optional[str]]] + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + if '+' not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is +://, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split('+', 1)[1] + netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + if not rev: + raise InstallationError( + "The URL {!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL.".format(url) + ) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + # type: (HiddenText) -> Tuple[HiddenText, RevOptions] + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) + username, secret_password = user_pass + password = None # type: Optional[HiddenText] + if secret_password is not None: + password = hide_value(secret_password) + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return hide_url(secret_url), rev_options + + @staticmethod + def normalize_url(url): + # type: (str) -> str + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + @classmethod + def compare_urls(cls, url1, url2): + # type: (str, str) -> bool + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (cls.normalize_url(url1) == cls.normalize_url(url2)) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + @classmethod + def is_commit_id_equal(cls, dest, name): + # type: (str, Optional[str]) -> bool + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest, url): + # type: (str, HiddenText) -> None + """ + Install or update in editable mode the package represented by this + VersionControl object. + + :param dest: the repository directory in which to install or update. + :param url: the repository URL starting with a vcs prefix. + """ + url, rev_options = self.get_url_rev_options(url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_remote_url(dest) + if self.compare_urls(existing_url, url.secret): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info('Skipping because already up-to-date.') + return + + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + # https://github.com/python/mypy/issues/1174 + prompt = ('(i)gnore, (w)ipe, (b)ackup ', # type: ignore + ('i', 'w', 'b')) + + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? {}'.format( + prompt[0]), prompt[1]) + + if response == 'a': + sys.exit(-1) + + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options) + return + + if response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options) + return + + # Do nothing if the response is "i". + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location, url): + # type: (str, HiddenText) -> None + """ + Clean up current location and download the url repository + (and vcs infos) into location + + :param url: the repository URL starting with a vcs prefix. + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location, url=url) + + @classmethod + def get_remote_url(cls, location): + # type: (str) -> str + """ + Return the url used at location + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + raise NotImplementedError + + @classmethod + def get_revision(cls, location): + # type: (str) -> str + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd, # type: Union[List[str], CommandArgs] + cwd=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + log_failed_cmd=True # type: bool + ): + # type: (...) -> Text + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = make_command(cls.name, *cmd) + try: + return call_subprocess(cmd, cwd, + extra_environ=extra_environ, + extra_ok_returncodes=extra_ok_returncodes, + log_failed_cmd=log_failed_cmd) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand( + 'Cannot find command {cls.name!r} - do you have ' + '{cls.name!r} installed and in your ' + 'PATH?'.format(**locals())) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def is_repository_directory(cls, path): + # type: (str) -> bool + """ + Return whether a directory path is a repository directory. + """ + logger.debug('Checking in %s for %s (%s)...', + path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def get_repository_root(cls, location): + # type: (str) -> Optional[str] + """ + Return the "root" (top-level) directory controlled by the vcs, + or `None` if the directory is not in any. + + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For + example, the Git override checks that Git is actually available. + """ + if cls.is_repository_directory(location): + return location + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py new file mode 100644 index 0000000..fa08016 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_internal/wheel_builder.py @@ -0,0 +1,308 @@ +"""Orchestrator for building wheels from InstallRequirements. +""" + +import logging +import os.path +import re +import shutil + +from pip._internal.models.link import Link +from pip._internal.operations.build.wheel import build_wheel_pep517 +from pip._internal.operations.build.wheel_legacy import build_wheel_legacy +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed +from pip._internal.utils.setuptools_build import make_setuptools_clean_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Iterable, List, Optional, Tuple, + ) + + from pip._internal.cache import WheelCache + from pip._internal.req.req_install import InstallRequirement + + BinaryAllowedPredicate = Callable[[InstallRequirement], bool] + BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] + +logger = logging.getLogger(__name__) + +_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.IGNORECASE) + + +def _contains_egg_info(s): + # type: (str) -> bool + """Determine whether the string looks like an egg_info. + + :param s: The string to parse. E.g. foo-2.1 + """ + return bool(_egg_info_re.search(s)) + + +def _should_build( + req, # type: InstallRequirement + need_wheel, # type: bool + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + """Return whether an InstallRequirement should be built into a wheel.""" + if req.constraint: + # never build requirements that are merely constraints + return False + if req.is_wheel: + if need_wheel: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + return False + + if need_wheel: + # i.e. pip wheel, not pip install + return True + + # From this point, this concerns the pip install command only + # (need_wheel=False). + + if req.editable or not req.source_dir: + return False + + if not check_binary_allowed(req): + logger.info( + "Skipping wheel build for %s, due to binaries " + "being disabled for it.", req.name, + ) + return False + + if not req.use_pep517 and not is_wheel_installed(): + # we don't build legacy requirements if wheel is not installed + logger.info( + "Using legacy 'setup.py install' for %s, " + "since package 'wheel' is not installed.", req.name, + ) + return False + + return True + + +def should_build_for_wheel_command( + req, # type: InstallRequirement +): + # type: (...) -> bool + return _should_build( + req, need_wheel=True, check_binary_allowed=_always_true + ) + + +def should_build_for_install_command( + req, # type: InstallRequirement + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + return _should_build( + req, need_wheel=False, check_binary_allowed=check_binary_allowed + ) + + +def _should_cache( + req, # type: InstallRequirement +): + # type: (...) -> Optional[bool] + """ + Return whether a built InstallRequirement can be stored in the persistent + wheel cache, assuming the wheel cache is available, and _should_build() + has determined a wheel needs to be built. + """ + if req.editable or not req.source_dir: + # never cache editable requirements + return False + + if req.link and req.link.is_vcs: + # VCS checkout. Do not cache + # unless it points to an immutable commit hash. + assert not req.editable + assert req.source_dir + vcs_backend = vcs.get_backend_for_scheme(req.link.scheme) + assert vcs_backend + if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir): + return True + return False + + assert req.link + base, ext = req.link.splitext() + if _contains_egg_info(base): + return True + + # Otherwise, do not cache. + return False + + +def _get_cache_dir( + req, # type: InstallRequirement + wheel_cache, # type: WheelCache +): + # type: (...) -> str + """Return the persistent or temporary cache directory where the built + wheel need to be stored. + """ + cache_available = bool(wheel_cache.cache_dir) + assert req.link + if cache_available and _should_cache(req): + cache_dir = wheel_cache.get_path_for_link(req.link) + else: + cache_dir = wheel_cache.get_ephem_path_for_link(req.link) + return cache_dir + + +def _always_true(_): + # type: (Any) -> bool + return True + + +def _build_one( + req, # type: InstallRequirement + output_dir, # type: str + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + return None + + # Install build deps into temporary directory (PEP 518) + with req.build_env: + return _build_one_inside_env( + req, output_dir, build_options, global_options + ) + + +def _build_one_inside_env( + req, # type: InstallRequirement + output_dir, # type: str + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + with TempDirectory(kind="wheel") as temp_dir: + assert req.name + if req.use_pep517: + assert req.metadata_directory + wheel_path = build_wheel_pep517( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + build_options=build_options, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_legacy( + name=req.name, + setup_py_path=req.setup_py_path, + source_dir=req.unpacked_source_directory, + global_options=global_options, + build_options=build_options, + tempd=temp_dir.path, + ) + + if wheel_path is not None: + wheel_name = os.path.basename(wheel_path) + dest_path = os.path.join(output_dir, wheel_name) + try: + wheel_hash, length = hash_file(wheel_path) + shutil.move(wheel_path, dest_path) + logger.info('Created wheel for %s: ' + 'filename=%s size=%d sha256=%s', + req.name, wheel_name, length, + wheel_hash.hexdigest()) + logger.info('Stored in directory: %s', output_dir) + return dest_path + except Exception as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + # Ignore return, we can't do anything else useful. + if not req.use_pep517: + _clean_one_legacy(req, global_options) + return None + + +def _clean_one_legacy(req, global_options): + # type: (InstallRequirement, List[str]) -> bool + clean_args = make_setuptools_clean_args( + req.setup_py_path, + global_options=global_options, + ) + + logger.info('Running setup.py clean for %s', req.name) + try: + call_subprocess(clean_args, cwd=req.source_dir) + return True + except Exception: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + +def build( + requirements, # type: Iterable[InstallRequirement] + wheel_cache, # type: WheelCache + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> BuildResult + """Build wheels. + + :return: The list of InstallRequirement that succeeded to build and + the list of InstallRequirement that failed to build. + """ + if not requirements: + return [], [] + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join(req.name for req in requirements), # type: ignore + ) + + with indent_log(): + build_successes, build_failures = [], [] + for req in requirements: + cache_dir = _get_cache_dir(req, wheel_cache) + wheel_file = _build_one( + req, cache_dir, build_options, global_options + ) + if wheel_file: + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + req.local_file_path = req.link.file_path + assert req.link.is_wheel + build_successes.append(req) + else: + build_failures.append(req) + + # notify success/failure + if build_successes: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_successes]), # type: ignore + ) + if build_failures: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failures]), # type: ignore + ) + # Return a list of requirements that failed to build + return build_successes, build_failures diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..581db54 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,110 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = False + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.dirname(__file__)) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # This error used to be silenced in earlier variants of this file, to instead + # raise the error when pip actually tries to use the missing module. + # Based on inputs in #5354, this was changed to explicitly raise the error. + # Re-raising the exception without modifying it is an intentional choice. + raise + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("appdirs") + vendored("cachecontrol") + vendored("certifi") + vendored("colorama") + vendored("contextlib2") + vendored("distlib") + vendored("distro") + vendored("html5lib") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pep517") + vendored("pkg_resources") + vendored("progress") + vendored("retrying") + vendored("requests") + vendored("requests.exceptions") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") + vendored("resolvelib") + vendored("toml") + vendored("toml.encoder") + vendored("toml.decoder") + vendored("urllib3") diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd77f514b48842e5cf6476eea3a4af82c8dd59a6 GIT binary patch literal 3007 zcmbuB-EZ7P5Wv?T=et}kZ9>zuDYUrXO-p=jL+A$qAxJ7JRebQq$aj$Ekr$BfB7NjV{cad|*_mT6+50Lke_mK~f50M`t7YwQMC7JoC z{Iy*qMG&sDoO0nO(q948qaADJ!G(JID7MTL zUM{c&&u8$+b!e!yOO&-m_Uvt|Jge+2qMRL~+#Nz~>ip_mSrfXUYWXsxZQI|IM?62)@d)rYXm4-b{ww!(49S5R8AR6A&via)vcJS)X zwlM31l{ZpwbgHf3btPzP58{(WO1Hu=k1`g9x*g(ILBF}0^5rO%+DmzCIQqEy;3Xlr z(2m@cx|wl0fH-JR$RbUYZc3&irNoS@+x46=tjFpkV6=LVc9$c`wA*J9{e_50Slu7r zcArUbd^he#d9=!N8g&ej z>~U+{5>p^68#jY*`l)V4ML`oGbtjJEK7&M4f`d3_LM1DSZpA$1BFZ8?YUUtwVNrK= zlP1#iv}Zbk>sDW7>AaCRGTA6cpb0X*&SW`$&1Tl{RVg#IATl{qUL9fv zVwRc8!o;vlKm)^^2z#S|tY_tgqu(}ANm|YfpAQaY@sK@XOa=SPx_*qwgS8DwI5Y@b zyS#jiITNX2p|4CSD8_s+H18@sC}Fy7>P!k}5L7!?@hmMnrVZdP0f%hE5rtw3Bi*d4 z-rUSd-3&v@Un={zQP)k>{T;Ej literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/appdirs.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/appdirs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf7cd0fc3a4f169ced88593a8e71ce85ad8e5a21 GIT binary patch literal 21435 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 JpMI&H{{<9nQ8WMm literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/contextlib2.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/contextlib2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18d0a7d86c1057966956fdeeac9793f153579017 GIT binary patch literal 15611 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/ipaddress.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/ipaddress.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbe95e4ecf85ba5a55d5b15affdc5974f7a9e32d GIT binary patch literal 64715 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 QuW1_gT~n`V4@`aRUnp;^y#N3J literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/six.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/__pycache__/six.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8047eb921c4df942b1e0d966830ea7a39221d73 GIT binary patch literal 26981 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/appdirs.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/appdirs.py new file mode 100644 index 0000000..33a3b77 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/appdirs.py @@ -0,0 +1,633 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version__ = "1.4.4" +__version_info__ = tuple(int(segment) for segment in __version__.split(".")) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +elif sys.platform == 'cli' and os.name == 'nt': + # Detect Windows in IronPython to match pip._internal.utils.compat.WINDOWS + # Discussion: + system = 'win32' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ # or ~/.config/, if the other does not exist + Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.path.join(x, appname) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +# for the discussion regarding site_config_dir locations +# see +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS (missing or empty) + # see + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS') or '/etc/xdg' + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) if x] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.path.join(x, appname) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + # When using Python 2, return paths as bytes on Windows like we do on + # other operating systems. See helper function docs for more details. + if not PY3 and isinstance(path, unicode): + path = _win_path_to_bytes(path) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + if PY3: + import winreg as _winreg + else: + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +def _win_path_to_bytes(path): + """Encode Windows paths to bytes. Only used on Python 2. + + Motivation is to be consistent with other operating systems where paths + are also returned as bytes. This avoids problems mixing bytes and Unicode + elsewhere in the codebase. For more details and discussion see + . + + If encoding using ASCII and MBCS fails, return the original Unicode path. + """ + for encoding in ('ASCII', 'MBCS'): + try: + return path.encode(encoding) + except (UnicodeEncodeError, LookupError): + pass + return path + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py new file mode 100644 index 0000000..a1bbbbe --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py @@ -0,0 +1,11 @@ +"""CacheControl import Interface. + +Make it easy to import from cachecontrol without long namespaces. +""" +__author__ = "Eric Larson" +__email__ = "eric@ionrock.org" +__version__ = "0.12.6" + +from .wrapper import CacheControl +from .adapter import CacheControlAdapter +from .controller import CacheController diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b69a08ddc35ef4022af4c15c423082cc8d0d4de GIT binary patch literal 585 zcmYjN%Z}496iwQ+X)_&xG(V7S7mayH2qc7NG_XJdHi%6&mdC!+nkK&H+MS{O2R>jv zfnUm&6~6!*Zl;42TfRQ$$UZ*4>h!coc-(FOswz#AUp*O)5tDOX;UyQ7)QL&!)Qswp z8P{WznMsqilX{XSjHk`C&FlOg=gq90*K@|R&&hIrxVlt&1DEI`VN=@HBgOK)i$K>( zLsb+X)fP${OHg57M*M%|ny@W(e^)=;y|rP|1kVnkDh?;_$m;TgqJZx3 z7?_+}bcA|aA*~P7C)Lwu)r;T!&19K&i(w+>XG4Brl;>ui*=^80S~x<#{V;7#AZ(8tWOEaf>`q>J<)E&8>- literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b2e2d6a22a84ad22d639360f5cb48b09041583c GIT binary patch literal 1606 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%0DMm9NxL!nu;b38{6TG@hZCvNvG}2sZaRm(K z(QGcgn6p8*SqDi{u#rfTI!uz0MQCCKQNFin3z zw#->_4Lx8f2o31*0laJ0>DZ%+qaAK3arftl@b+DLM zv%Pe>jt*D@|kSw8)C+G5Gnk0h#WolMXu4B*wV9-F)>^v{E zGVaJ2Wts@_4r@mIq~I$7&6e)=EYXEd7MCD%WfB7y6AxzNrdV6@`My`d?1X*yEL$mGR$}L>2@p#Ni=;!jSGoOjsLy<20 z`k1~#18mBdOtLd2bCy0;yedIDEGkGZ*c`VxTxl>xU)Ht5nHSoqbKoxR(>ch?6fJGj zrIH89VQLEp^BZGQY&l)qmU70Iz?F1H34Ep0%q6;RqDQ65D*=xw_Gj;7@aUArbl>+d O#&kDa=ic9Xg!Mmi+S(2P literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52973264cae7a18a1a83319d9ffdba600d24bbd1 GIT binary patch literal 7799 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 Qiu_;I@FyQY!*a|22QCXacmMzZ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25d2debd891930f998863a370bfa257121a4c410 GIT binary patch literal 2207 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bf9f6a6671e40dbd47f41537810ddc026775875 GIT binary patch literal 709 zcmYjPy>1jS5VrUKa(56ygLnX>EiNvoKnQ^@6~#4#=9-mbTenMg*T(iGGS}B{W$+ee??z;mAR>?>X-JbsVL;tz(yCH9Z!l+|J`d#h#pmQ!Vg2KVTsVRi^DPFVp* z?`;Gm=s9fj4!~iK)<{3F=nLv2hqu^e>WRhM%w-O{$i*&ksmnS3o^1<d2OwFNyw%M!~iQ}9Ye($hKPLH$#rkbkxz_(1nH1KCn25&$O44X`YGLwW%nr^ zy7&2=?^UVTlC3+@TX0H2KG7LGX5zDgj&^Q!WreYo((x^Sw(A z-I%8UXnceV9PO{$teD_Qqz@rl=8Mxsurk$C(8HMYIIv-A;mpwN9y|n}FJ5($uUqjt Q{8Hu!0CJ`X@2$W80U74D1^@s6 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py new file mode 100644 index 0000000..f1e0ad9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py @@ -0,0 +1,57 @@ +import logging + +from pip._vendor import requests + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import logger + +from argparse import ArgumentParser + + +def setup_logging(): + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + logger.addHandler(handler) + + +def get_session(): + adapter = CacheControlAdapter( + DictCache(), cache_etags=True, serializer=None, heuristic=None + ) + sess = requests.Session() + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + sess.cache_controller = adapter.controller + return sess + + +def get_args(): + parser = ArgumentParser() + parser.add_argument("url", help="The URL to try and cache") + return parser.parse_args() + + +def main(args=None): + args = get_args() + sess = get_session() + + # Make a request to get a response + resp = sess.get(args.url) + + # Turn on logging + setup_logging() + + # try setting the cache + sess.cache_controller.cache_response(resp.request, resp.raw) + + # Now try to get it + if sess.cache_controller.cached_request(resp.request): + print("Cached!") + else: + print("Not cached :(") + + +if __name__ == "__main__": + main() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py new file mode 100644 index 0000000..815650e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py @@ -0,0 +1,133 @@ +import types +import functools +import zlib + +from pip._vendor.requests.adapters import HTTPAdapter + +from .controller import CacheController +from .cache import DictCache +from .filewrapper import CallbackFileWrapper + + +class CacheControlAdapter(HTTPAdapter): + invalidating_methods = {"PUT", "DELETE"} + + def __init__( + self, + cache=None, + cache_etags=True, + controller_class=None, + serializer=None, + heuristic=None, + cacheable_methods=None, + *args, + **kw + ): + super(CacheControlAdapter, self).__init__(*args, **kw) + self.cache = DictCache() if cache is None else cache + self.heuristic = heuristic + self.cacheable_methods = cacheable_methods or ("GET",) + + controller_factory = controller_class or CacheController + self.controller = controller_factory( + self.cache, cache_etags=cache_etags, serializer=serializer + ) + + def send(self, request, cacheable_methods=None, **kw): + """ + Send a request. Use the request information to see if it + exists in the cache and cache the response if we need to and can. + """ + cacheable = cacheable_methods or self.cacheable_methods + if request.method in cacheable: + try: + cached_response = self.controller.cached_request(request) + except zlib.error: + cached_response = None + if cached_response: + return self.build_response(request, cached_response, from_cache=True) + + # check for etags and add headers if appropriate + request.headers.update(self.controller.conditional_headers(request)) + + resp = super(CacheControlAdapter, self).send(request, **kw) + + return resp + + def build_response( + self, request, response, from_cache=False, cacheable_methods=None + ): + """ + Build a response by making a request or using the cache. + + This will end up calling send and returning a potentially + cached response + """ + cacheable = cacheable_methods or self.cacheable_methods + if not from_cache and request.method in cacheable: + # Check for any heuristics that might update headers + # before trying to cache. + if self.heuristic: + response = self.heuristic.apply(response) + + # apply any expiration heuristics + if response.status == 304: + # We must have sent an ETag request. This could mean + # that we've been expired already or that we simply + # have an etag. In either case, we want to try and + # update the cache if that is the case. + cached_response = self.controller.update_cached_response( + request, response + ) + + if cached_response is not response: + from_cache = True + + # We are done with the server response, read a + # possible response body (compliant servers will + # not return one, but we cannot be 100% sure) and + # release the connection back to the pool. + response.read(decode_content=False) + response.release_conn() + + response = cached_response + + # We always cache the 301 responses + elif response.status == 301: + self.controller.cache_response(request, response) + else: + # Wrap the response file with a wrapper that will cache the + # response when the stream has been consumed. + response._fp = CallbackFileWrapper( + response._fp, + functools.partial( + self.controller.cache_response, request, response + ), + ) + if response.chunked: + super_update_chunk_length = response._update_chunk_length + + def _update_chunk_length(self): + super_update_chunk_length() + if self.chunk_left == 0: + self._fp._close() + + response._update_chunk_length = types.MethodType( + _update_chunk_length, response + ) + + resp = super(CacheControlAdapter, self).build_response(request, response) + + # See if we should invalidate the cache. + if request.method in self.invalidating_methods and resp.ok: + cache_url = self.controller.cache_url(request.url) + self.cache.delete(cache_url) + + # Give the request a from_cache attr to let people use it + resp.from_cache = from_cache + + return resp + + def close(self): + self.cache.close() + super(CacheControlAdapter, self).close() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py new file mode 100644 index 0000000..94e0773 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py @@ -0,0 +1,39 @@ +""" +The cache object API for implementing caches. The default is a thread +safe in-memory dictionary. +""" +from threading import Lock + + +class BaseCache(object): + + def get(self, key): + raise NotImplementedError() + + def set(self, key, value): + raise NotImplementedError() + + def delete(self, key): + raise NotImplementedError() + + def close(self): + pass + + +class DictCache(BaseCache): + + def __init__(self, init_dict=None): + self.lock = Lock() + self.data = init_dict or {} + + def get(self, key): + return self.data.get(key, None) + + def set(self, key, value): + with self.lock: + self.data.update({key: value}) + + def delete(self, key): + with self.lock: + if key in self.data: + self.data.pop(key) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py new file mode 100644 index 0000000..0e1658f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py @@ -0,0 +1,2 @@ +from .file_cache import FileCache # noqa +from .redis_cache import RedisCache # noqa diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2492574d1bee18d987f7dd0c1e3478fd2f61e5f GIT binary patch literal 329 zcmYk1F;2uV5Jlr;g&?pu-~uVKQ&1rxgao3b070>hW$a;N*0GK4U9tC|;RswQEfrUQ zhH*l{NS;6Mf6MZS`MfTu-q%0kf%G%O|0)SC=)_}cs!+@nJzFxC=hBpYwXA5a?uusi z#pp`IhJ$Nu@KVSQ3F=!^I*xSH%!c}jGA?r%^3%g%$oZdBk@Hc6J(96~zrlUP5J7gr zij}iUz?GAm9&IAiA-sr|q{UZ{K~J{dC5(yt;GR(?n6O2=1*0D`cFvwGP9f@qtrv1F sRv5u+4}6POxd7w$q_ZhFGeHE-wbhC9h2IVPXT4J!gXekSnAbRBzdsaTcmMzZ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18af09a87f75051217307dbc4353370f2c5d0c7a GIT binary patch literal 3345 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 LHtGzjea8L;wL@ga literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py new file mode 100644 index 0000000..607b945 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py @@ -0,0 +1,146 @@ +import hashlib +import os +from textwrap import dedent + +from ..cache import BaseCache +from ..controller import CacheController + +try: + FileNotFoundError +except NameError: + # py2.X + FileNotFoundError = (IOError, OSError) + + +def _secure_open_write(filename, fmode): + # We only want to write to this file, so open it in write only mode + flags = os.O_WRONLY + + # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only + # will open *new* files. + # We specify this because we want to ensure that the mode we pass is the + # mode of the file. + flags |= os.O_CREAT | os.O_EXCL + + # Do not follow symlinks to prevent someone from making a symlink that + # we follow and insecurely open a cache file. + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + + # On Windows we'll mark this file as binary + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + + # Before we open our file, we want to delete any existing file that is + # there + try: + os.remove(filename) + except (IOError, OSError): + # The file must not exist already, so we can just skip ahead to opening + pass + + # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a + # race condition happens between the os.remove and this line, that an + # error will be raised. Because we utilize a lockfile this should only + # happen if someone is attempting to attack us. + fd = os.open(filename, flags, fmode) + try: + return os.fdopen(fd, "wb") + + except: + # An error occurred wrapping our FD in a file object + os.close(fd) + raise + + +class FileCache(BaseCache): + + def __init__( + self, + directory, + forever=False, + filemode=0o0600, + dirmode=0o0700, + use_dir_lock=None, + lock_class=None, + ): + + if use_dir_lock is not None and lock_class is not None: + raise ValueError("Cannot use use_dir_lock and lock_class together") + + try: + from lockfile import LockFile + from lockfile.mkdirlockfile import MkdirLockFile + except ImportError: + notice = dedent( + """ + NOTE: In order to use the FileCache you must have + lockfile installed. You can install it via pip: + pip install lockfile + """ + ) + raise ImportError(notice) + + else: + if use_dir_lock: + lock_class = MkdirLockFile + + elif lock_class is None: + lock_class = LockFile + + self.directory = directory + self.forever = forever + self.filemode = filemode + self.dirmode = dirmode + self.lock_class = lock_class + + @staticmethod + def encode(x): + return hashlib.sha224(x.encode()).hexdigest() + + def _fn(self, name): + # NOTE: This method should not change as some may depend on it. + # See: https://github.com/ionrock/cachecontrol/issues/63 + hashed = self.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + name = self._fn(key) + try: + with open(name, "rb") as fh: + return fh.read() + + except FileNotFoundError: + return None + + def set(self, key, value): + name = self._fn(key) + + # Make sure the directory exists + try: + os.makedirs(os.path.dirname(name), self.dirmode) + except (IOError, OSError): + pass + + with self.lock_class(name) as lock: + # Write our actual file + with _secure_open_write(lock.path, self.filemode) as fh: + fh.write(value) + + def delete(self, key): + name = self._fn(key) + if not self.forever: + try: + os.remove(name) + except FileNotFoundError: + pass + + +def url_to_file_path(url, filecache): + """Return the file cache path based on the URL. + + This does not ensure the file exists! + """ + key = CacheController.cache_url(url) + return filecache._fn(key) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py new file mode 100644 index 0000000..ed705ce --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -0,0 +1,33 @@ +from __future__ import division + +from datetime import datetime +from pip._vendor.cachecontrol.cache import BaseCache + + +class RedisCache(BaseCache): + + def __init__(self, conn): + self.conn = conn + + def get(self, key): + return self.conn.get(key) + + def set(self, key, value, expires=None): + if not expires: + self.conn.set(key, value) + else: + expires = expires - datetime.utcnow() + self.conn.setex(key, int(expires.total_seconds()), value) + + def delete(self, key): + self.conn.delete(key) + + def clear(self): + """Helper for clearing all the keys in a database. Use with + caution!""" + for key in self.conn.keys(): + self.conn.delete(key) + + def close(self): + """Redis uses connection pooling, no need to close the connection.""" + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py new file mode 100644 index 0000000..33b5aed --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py @@ -0,0 +1,29 @@ +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + + +try: + import cPickle as pickle +except ImportError: + import pickle + + +# Handle the case where the requests module has been patched to not have +# urllib3 bundled as part of its source. +try: + from pip._vendor.requests.packages.urllib3.response import HTTPResponse +except ImportError: + from pip._vendor.urllib3.response import HTTPResponse + +try: + from pip._vendor.requests.packages.urllib3.util import is_fp_closed +except ImportError: + from pip._vendor.urllib3.util import is_fp_closed + +# Replicate some six behaviour +try: + text_type = unicode +except NameError: + text_type = str diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..dafe55c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,376 @@ +""" +The httplib2 algorithms ported for use with requests. +""" +import logging +import re +import calendar +import time +from email.utils import parsedate_tz + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .cache import DictCache +from .serialize import Serializer + + +logger = logging.getLogger(__name__) + +URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") + + +def parse_uri(uri): + """Parses a URI using the regex given in Appendix B of RFC 3986. + + (scheme, authority, path, query, fragment) = parse_uri(uri) + """ + groups = URI.match(uri).groups() + return (groups[1], groups[3], groups[4], groups[6], groups[8]) + + +class CacheController(object): + """An interface to see if request should cached or not. + """ + + def __init__( + self, cache=None, cache_etags=True, serializer=None, status_codes=None + ): + self.cache = DictCache() if cache is None else cache + self.cache_etags = cache_etags + self.serializer = serializer or Serializer() + self.cacheable_status_codes = status_codes or (200, 203, 300, 301) + + @classmethod + def _urlnorm(cls, uri): + """Normalize the URL to create a safe key for the cache""" + (scheme, authority, path, query, fragment) = parse_uri(uri) + if not scheme or not authority: + raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + + scheme = scheme.lower() + authority = authority.lower() + + if not path: + path = "/" + + # Could do syntax based normalization of the URI before + # computing the digest. See Section 6.2.2 of Std 66. + request_uri = query and "?".join([path, query]) or path + defrag_uri = scheme + "://" + authority + request_uri + + return defrag_uri + + @classmethod + def cache_url(cls, uri): + return cls._urlnorm(uri) + + def parse_cache_control(self, headers): + known_directives = { + # https://tools.ietf.org/html/rfc7234#section-5.2 + "max-age": (int, True), + "max-stale": (int, False), + "min-fresh": (int, True), + "no-cache": (None, False), + "no-store": (None, False), + "no-transform": (None, False), + "only-if-cached": (None, False), + "must-revalidate": (None, False), + "public": (None, False), + "private": (None, False), + "proxy-revalidate": (None, False), + "s-maxage": (int, True), + } + + cc_headers = headers.get("cache-control", headers.get("Cache-Control", "")) + + retval = {} + + for cc_directive in cc_headers.split(","): + if not cc_directive.strip(): + continue + + parts = cc_directive.split("=", 1) + directive = parts[0].strip() + + try: + typ, required = known_directives[directive] + except KeyError: + logger.debug("Ignoring unknown cache-control directive: %s", directive) + continue + + if not typ or not required: + retval[directive] = None + if typ: + try: + retval[directive] = typ(parts[1].strip()) + except IndexError: + if required: + logger.debug( + "Missing value for cache-control " "directive: %s", + directive, + ) + except ValueError: + logger.debug( + "Invalid value for cache-control directive " "%s, must be %s", + directive, + typ.__name__, + ) + + return retval + + def cached_request(self, request): + """ + Return a cached response if it exists in the cache, otherwise + return False. + """ + cache_url = self.cache_url(request.url) + logger.debug('Looking up "%s" in the cache', cache_url) + cc = self.parse_cache_control(request.headers) + + # Bail out if the request insists on fresh data + if "no-cache" in cc: + logger.debug('Request header has "no-cache", cache bypassed') + return False + + if "max-age" in cc and cc["max-age"] == 0: + logger.debug('Request header has "max_age" as 0, cache bypassed') + return False + + # Request allows serving from the cache, let's see if we find something + cache_data = self.cache.get(cache_url) + if cache_data is None: + logger.debug("No cache entry available") + return False + + # Check whether it can be deserialized + resp = self.serializer.loads(request, cache_data) + if not resp: + logger.warning("Cache entry deserialization failed, entry ignored") + return False + + # If we have a cached 301, return it immediately. We don't + # need to test our response for other headers b/c it is + # intrinsically "cacheable" as it is Permanent. + # See: + # https://tools.ietf.org/html/rfc7231#section-6.4.2 + # + # Client can try to refresh the value by repeating the request + # with cache busting headers as usual (ie no-cache). + if resp.status == 301: + msg = ( + 'Returning cached "301 Moved Permanently" response ' + "(ignoring date and etag information)" + ) + logger.debug(msg) + return resp + + headers = CaseInsensitiveDict(resp.headers) + if not headers or "date" not in headers: + if "etag" not in headers: + # Without date or etag, the cached response can never be used + # and should be deleted. + logger.debug("Purging cached response: no date or etag") + self.cache.delete(cache_url) + logger.debug("Ignoring cached response: no date") + return False + + now = time.time() + date = calendar.timegm(parsedate_tz(headers["date"])) + current_age = max(0, now - date) + logger.debug("Current age based on date: %i", current_age) + + # TODO: There is an assumption that the result will be a + # urllib3 response object. This may not be best since we + # could probably avoid instantiating or constructing the + # response until we know we need it. + resp_cc = self.parse_cache_control(headers) + + # determine freshness + freshness_lifetime = 0 + + # Check the max-age pragma in the cache control header + if "max-age" in resp_cc: + freshness_lifetime = resp_cc["max-age"] + logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime) + + # If there isn't a max-age, check for an expires header + elif "expires" in headers: + expires = parsedate_tz(headers["expires"]) + if expires is not None: + expire_time = calendar.timegm(expires) - date + freshness_lifetime = max(0, expire_time) + logger.debug("Freshness lifetime from expires: %i", freshness_lifetime) + + # Determine if we are setting freshness limit in the + # request. Note, this overrides what was in the response. + if "max-age" in cc: + freshness_lifetime = cc["max-age"] + logger.debug( + "Freshness lifetime from request max-age: %i", freshness_lifetime + ) + + if "min-fresh" in cc: + min_fresh = cc["min-fresh"] + # adjust our current age by our min fresh + current_age += min_fresh + logger.debug("Adjusted current age from min-fresh: %i", current_age) + + # Return entry if it is fresh enough + if freshness_lifetime > current_age: + logger.debug('The response is "fresh", returning cached response') + logger.debug("%i > %i", freshness_lifetime, current_age) + return resp + + # we're not fresh. If we don't have an Etag, clear it out + if "etag" not in headers: + logger.debug('The cached response is "stale" with no etag, purging') + self.cache.delete(cache_url) + + # return the original handler + return False + + def conditional_headers(self, request): + cache_url = self.cache_url(request.url) + resp = self.serializer.loads(request, self.cache.get(cache_url)) + new_headers = {} + + if resp: + headers = CaseInsensitiveDict(resp.headers) + + if "etag" in headers: + new_headers["If-None-Match"] = headers["ETag"] + + if "last-modified" in headers: + new_headers["If-Modified-Since"] = headers["Last-Modified"] + + return new_headers + + def cache_response(self, request, response, body=None, status_codes=None): + """ + Algorithm for caching requests. + + This assumes a requests Response object. + """ + # From httplib2: Don't cache 206's since we aren't going to + # handle byte range requests + cacheable_status_codes = status_codes or self.cacheable_status_codes + if response.status not in cacheable_status_codes: + logger.debug( + "Status code %s not in %s", response.status, cacheable_status_codes + ) + return + + response_headers = CaseInsensitiveDict(response.headers) + + # If we've been given a body, our response has a Content-Length, that + # Content-Length is valid then we can check to see if the body we've + # been given matches the expected size, and if it doesn't we'll just + # skip trying to cache it. + if ( + body is not None + and "content-length" in response_headers + and response_headers["content-length"].isdigit() + and int(response_headers["content-length"]) != len(body) + ): + return + + cc_req = self.parse_cache_control(request.headers) + cc = self.parse_cache_control(response_headers) + + cache_url = self.cache_url(request.url) + logger.debug('Updating cache with response from "%s"', cache_url) + + # Delete it from the cache if we happen to have it stored there + no_store = False + if "no-store" in cc: + no_store = True + logger.debug('Response header has "no-store"') + if "no-store" in cc_req: + no_store = True + logger.debug('Request header has "no-store"') + if no_store and self.cache.get(cache_url): + logger.debug('Purging existing cache entry to honor "no-store"') + self.cache.delete(cache_url) + if no_store: + return + + # https://tools.ietf.org/html/rfc7234#section-4.1: + # A Vary header field-value of "*" always fails to match. + # Storing such a response leads to a deserialization warning + # during cache lookup and is not allowed to ever be served, + # so storing it can be avoided. + if "*" in response_headers.get("vary", ""): + logger.debug('Response header has "Vary: *"') + return + + # If we've been given an etag, then keep the response + if self.cache_etags and "etag" in response_headers: + logger.debug("Caching due to etag") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # Add to the cache any 301s. We do this before looking that + # the Date headers. + elif response.status == 301: + logger.debug("Caching permanant redirect") + self.cache.set(cache_url, self.serializer.dumps(request, response)) + + # Add to the cache if the response headers demand it. If there + # is no date header then we can't do anything about expiring + # the cache. + elif "date" in response_headers: + # cache when there is a max-age > 0 + if "max-age" in cc and cc["max-age"] > 0: + logger.debug("Caching b/c date exists and max-age > 0") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # If the request can expire, it means we should cache it + # in the meantime. + elif "expires" in response_headers: + if response_headers["expires"]: + logger.debug("Caching b/c of expires header") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + def update_cached_response(self, request, response): + """On a 304 we will get a new set of headers that we want to + update our cached value with, assuming we have one. + + This should only ever be called when we've sent an ETag and + gotten a 304 as the response. + """ + cache_url = self.cache_url(request.url) + + cached_response = self.serializer.loads(request, self.cache.get(cache_url)) + + if not cached_response: + # we didn't have a cached response + return response + + # Lets update our headers with the headers from the new request: + # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1 + # + # The server isn't supposed to send headers that would make + # the cached body invalid. But... just in case, we'll be sure + # to strip out ones we know that might be problmatic due to + # typical assumptions. + excluded_headers = ["content-length"] + + cached_response.headers.update( + dict( + (k, v) + for k, v in response.headers.items() + if k.lower() not in excluded_headers + ) + ) + + # we want a 200 b/c we have content via the cache + cached_response.status = 200 + + # update our cache + self.cache.set(cache_url, self.serializer.dumps(request, cached_response)) + + return cached_response diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py new file mode 100644 index 0000000..30ed4c5 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py @@ -0,0 +1,80 @@ +from io import BytesIO + + +class CallbackFileWrapper(object): + """ + Small wrapper around a fp object which will tee everything read into a + buffer, and when that file is closed it will execute a callback with the + contents of that buffer. + + All attributes are proxied to the underlying file object. + + This class uses members with a double underscore (__) leading prefix so as + not to accidentally shadow an attribute. + """ + + def __init__(self, fp, callback): + self.__buf = BytesIO() + self.__fp = fp + self.__callback = callback + + def __getattr__(self, name): + # The vaguaries of garbage collection means that self.__fp is + # not always set. By using __getattribute__ and the private + # name[0] allows looking up the attribute value and raising an + # AttributeError when it doesn't exist. This stop thigns from + # infinitely recursing calls to getattr in the case where + # self.__fp hasn't been set. + # + # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers + fp = self.__getattribute__("_CallbackFileWrapper__fp") + return getattr(fp, name) + + def __is_fp_closed(self): + try: + return self.__fp.fp is None + + except AttributeError: + pass + + try: + return self.__fp.closed + + except AttributeError: + pass + + # We just don't cache it then. + # TODO: Add some logging here... + return False + + def _close(self): + if self.__callback: + self.__callback(self.__buf.getvalue()) + + # We assign this to None here, because otherwise we can get into + # really tricky problems where the CPython interpreter dead locks + # because the callback is holding a reference to something which + # has a __del__ method. Setting this to None breaks the cycle + # and allows the garbage collector to do it's thing normally. + self.__callback = None + + def read(self, amt=None): + data = self.__fp.read(amt) + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data + + def _safe_read(self, amt): + data = self.__fp._safe_read(amt) + if amt == 2 and data == b"\r\n": + # urllib executes this read to toss the CRLF at the end + # of the chunk. + return data + + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py new file mode 100644 index 0000000..6c0e979 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py @@ -0,0 +1,135 @@ +import calendar +import time + +from email.utils import formatdate, parsedate, parsedate_tz + +from datetime import datetime, timedelta + +TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" + + +def expire_after(delta, date=None): + date = date or datetime.utcnow() + return date + delta + + +def datetime_to_header(dt): + return formatdate(calendar.timegm(dt.timetuple())) + + +class BaseHeuristic(object): + + def warning(self, response): + """ + Return a valid 1xx warning header value describing the cache + adjustments. + + The response is provided too allow warnings like 113 + http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need + to explicitly say response is over 24 hours old. + """ + return '110 - "Response is Stale"' + + def update_headers(self, response): + """Update the response headers with any new headers. + + NOTE: This SHOULD always include some Warning header to + signify that the response was cached by the client, not + by way of the provided headers. + """ + return {} + + def apply(self, response): + updated_headers = self.update_headers(response) + + if updated_headers: + response.headers.update(updated_headers) + warning_header_value = self.warning(response) + if warning_header_value is not None: + response.headers.update({"Warning": warning_header_value}) + + return response + + +class OneDayCache(BaseHeuristic): + """ + Cache the response by providing an expires 1 day in the + future. + """ + + def update_headers(self, response): + headers = {} + + if "expires" not in response.headers: + date = parsedate(response.headers["date"]) + expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) + headers["expires"] = datetime_to_header(expires) + headers["cache-control"] = "public" + return headers + + +class ExpiresAfter(BaseHeuristic): + """ + Cache **all** requests for a defined time period. + """ + + def __init__(self, **kw): + self.delta = timedelta(**kw) + + def update_headers(self, response): + expires = expire_after(self.delta) + return {"expires": datetime_to_header(expires), "cache-control": "public"} + + def warning(self, response): + tmpl = "110 - Automatically cached for %s. Response might be stale" + return tmpl % self.delta + + +class LastModified(BaseHeuristic): + """ + If there is no Expires header already, fall back on Last-Modified + using the heuristic from + http://tools.ietf.org/html/rfc7234#section-4.2.2 + to calculate a reasonable value. + + Firefox also does something like this per + https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ + http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 + Unlike mozilla we limit this to 24-hr. + """ + cacheable_by_default_statuses = { + 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 + } + + def update_headers(self, resp): + headers = resp.headers + + if "expires" in headers: + return {} + + if "cache-control" in headers and headers["cache-control"] != "public": + return {} + + if resp.status not in self.cacheable_by_default_statuses: + return {} + + if "date" not in headers or "last-modified" not in headers: + return {} + + date = calendar.timegm(parsedate_tz(headers["date"])) + last_modified = parsedate(headers["last-modified"]) + if date is None or last_modified is None: + return {} + + now = time.time() + current_age = max(0, now - date) + delta = date - calendar.timegm(last_modified) + freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) + if freshness_lifetime <= current_age: + return {} + + expires = date + freshness_lifetime + return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} + + def warning(self, resp): + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..3b6ec2d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,188 @@ +import base64 +import io +import json +import zlib + +from pip._vendor import msgpack +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .compat import HTTPResponse, pickle, text_type + + +def _b64_decode_bytes(b): + return base64.b64decode(b.encode("ascii")) + + +def _b64_decode_str(s): + return _b64_decode_bytes(s).decode("utf8") + + +class Serializer(object): + + def dumps(self, request, response, body=None): + response_headers = CaseInsensitiveDict(response.headers) + + if body is None: + body = response.read(decode_content=False) + + # NOTE: 99% sure this is dead code. I'm only leaving it + # here b/c I don't have a test yet to prove + # it. Basically, before using + # `cachecontrol.filewrapper.CallbackFileWrapper`, + # this made an effort to reset the file handle. The + # `CallbackFileWrapper` short circuits this code by + # setting the body as the content is consumed, the + # result being a `body` argument is *always* passed + # into cache_response, and in turn, + # `Serializer.dump`. + response._fp = io.BytesIO(body) + + # NOTE: This is all a bit weird, but it's really important that on + # Python 2.x these objects are unicode and not str, even when + # they contain only ascii. The problem here is that msgpack + # understands the difference between unicode and bytes and we + # have it set to differentiate between them, however Python 2 + # doesn't know the difference. Forcing these to unicode will be + # enough to have msgpack know the difference. + data = { + u"response": { + u"body": body, + u"headers": dict( + (text_type(k), text_type(v)) for k, v in response.headers.items() + ), + u"status": response.status, + u"version": response.version, + u"reason": text_type(response.reason), + u"strict": response.strict, + u"decode_content": response.decode_content, + } + } + + # Construct our vary headers + data[u"vary"] = {} + if u"vary" in response_headers: + varied_headers = response_headers[u"vary"].split(",") + for header in varied_headers: + header = text_type(header).strip() + header_value = request.headers.get(header, None) + if header_value is not None: + header_value = text_type(header_value) + data[u"vary"][header] = header_value + + return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)]) + + def loads(self, request, data): + # Short circuit if we've been given an empty set of data + if not data: + return + + # Determine what version of the serializer the data was serialized + # with + try: + ver, data = data.split(b",", 1) + except ValueError: + ver = b"cc=0" + + # Make sure that our "ver" is actually a version and isn't a false + # positive from a , being in the data stream. + if ver[:3] != b"cc=": + data = ver + data + ver = b"cc=0" + + # Get the version number out of the cc=N + ver = ver.split(b"=", 1)[-1].decode("ascii") + + # Dispatch to the actual load method for the given version + try: + return getattr(self, "_loads_v{}".format(ver))(request, data) + + except AttributeError: + # This is a version we don't have a loads function for, so we'll + # just treat it as a miss and return None + return + + def prepare_response(self, request, cached): + """Verify our vary headers match and construct a real urllib3 + HTTPResponse object. + """ + # Special case the '*' Vary value as it means we cannot actually + # determine if the cached response is suitable for this request. + # This case is also handled in the controller code when creating + # a cache entry, but is left here for backwards compatibility. + if "*" in cached.get("vary", {}): + return + + # Ensure that the Vary headers for the cached response match our + # request + for header, value in cached.get("vary", {}).items(): + if request.headers.get(header, None) != value: + return + + body_raw = cached["response"].pop("body") + + headers = CaseInsensitiveDict(data=cached["response"]["headers"]) + if headers.get("transfer-encoding", "") == "chunked": + headers.pop("transfer-encoding") + + cached["response"]["headers"] = headers + + try: + body = io.BytesIO(body_raw) + except TypeError: + # This can happen if cachecontrol serialized to v1 format (pickle) + # using Python 2. A Python 2 str(byte string) will be unpickled as + # a Python 3 str (unicode string), which will cause the above to + # fail with: + # + # TypeError: 'str' does not support the buffer interface + body = io.BytesIO(body_raw.encode("utf8")) + + return HTTPResponse(body=body, preload_content=False, **cached["response"]) + + def _loads_v0(self, request, data): + # The original legacy cache data. This doesn't contain enough + # information to construct everything we need, so we'll treat this as + # a miss. + return + + def _loads_v1(self, request, data): + try: + cached = pickle.loads(data) + except ValueError: + return + + return self.prepare_response(request, cached) + + def _loads_v2(self, request, data): + try: + cached = json.loads(zlib.decompress(data).decode("utf8")) + except (ValueError, zlib.error): + return + + # We need to decode the items that we've base64 encoded + cached["response"]["body"] = _b64_decode_bytes(cached["response"]["body"]) + cached["response"]["headers"] = dict( + (_b64_decode_str(k), _b64_decode_str(v)) + for k, v in cached["response"]["headers"].items() + ) + cached["response"]["reason"] = _b64_decode_str(cached["response"]["reason"]) + cached["vary"] = dict( + (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) + for k, v in cached["vary"].items() + ) + + return self.prepare_response(request, cached) + + def _loads_v3(self, request, data): + # Due to Python 2 encoding issues, it's impossible to know for sure + # exactly how to load v3 entries, thus we'll treat these as a miss so + # that they get rewritten out as v4 entries. + return + + def _loads_v4(self, request, data): + try: + cached = msgpack.loads(data, raw=False) + except ValueError: + return + + return self.prepare_response(request, cached) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..d8e6fc6 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py @@ -0,0 +1,29 @@ +from .adapter import CacheControlAdapter +from .cache import DictCache + + +def CacheControl( + sess, + cache=None, + cache_etags=True, + serializer=None, + heuristic=None, + controller_class=None, + adapter_class=None, + cacheable_methods=None, +): + + cache = DictCache() if cache is None else cache + adapter_class = adapter_class or CacheControlAdapter + adapter = adapter_class( + cache, + cache_etags=cache_etags, + serializer=serializer, + heuristic=heuristic, + controller_class=controller_class, + cacheable_methods=cacheable_methods, + ) + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + return sess diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py new file mode 100644 index 0000000..5d52a62 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py @@ -0,0 +1,3 @@ +from .core import contents, where + +__version__ = "2020.06.20" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py new file mode 100644 index 0000000..0037634 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from pip._vendor.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/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c4191331872a15b51d54860568d094aca1b1f9b GIT binary patch literal 311 zcmYjMK~BRk5Okb?s-j99`9coKMyXT@2~`|8b3t5kv5dVmripEAw@_Zd7kC0+%9Rse zfD5*IVWpkX>`0?ozF5pjoR942>5S6sI>&;>tLw>~-dn!^HOo$;>iVz5aaLppG#&Js{Dndh)3(gctCIFJ!#&I)WJc+C>}5VssEIJXYx8qA|L34?*y7@zGm~B#hMRd zE{0-Wa0YBU`N`%bl&eY?dZJ5RJ!HMy?vY+Yy%P8d-hjAg_nb4^P+dINntx@}y+7p6 zK2JreduJoq7*ct?1cW=$_IFL&zQ|384xx*Z5UX+N4_H!Yd4r-g@I_-4$5v zaMlAxb8b+OKNbY(@DvxDfku4EuQ63)7p8rsbw?)SU1~-(^N=PZmL83bzZf27s|{Y#EVg#3|$>r!Cw zHL8DtMv(9)=q6!x&j?GV0{&q zx-!-Wxmw9!oUK-_=^b}aH8Q<4YxTNvP?_MX#@V24@U!wyEQjn*S>&w@%Qylpl?eJp zXwt@Y$uQVM^`D>#$iard$AKmu&X{B&3+#elk~1oC7c0p2Pwup)or&k#DhIP|HE+td zaHe^jh`k4U4}X}8?;p&+jm4o3q60F%THl-h95Wvvj)XwS8iaUFAT_HqZ_ewVyar!4 zOKIhyvr5+AxMs6X<<*VT^)tDPKfC%xtHJck?Qzf^6M;L@O{n{DSKH_H$~;No(%H{u zU)0_Nz1zy>sXWxa#$xJiG4-|>OaL1D6Z2n|8uaZ%Pnr}`6yuUk7);QQx%7=OO(O70 zI6CQIN9cWY0txgChw79m_A2}B{g>HkcEK*mBf^ksj^9~Ek=9R%pWW-mh?2)?n0l%s zh1WC|+BQxZdpOM^cdfR4ex8~-<#8zbsfl^VG2Ftukt-SGTRvfoi;$q!s6OE&^c~u* zG%2_eicZ??D)$yzB%=RH2#db62fuS3R%7B$?9Sb22%t{Lp z`H?erN+Uh^FPy~U=crpa6ZS78xIuE%4#^F4a5!-&Ri^l>472C^ak%YQ2LpES4BSLB z?d!UhuA8ewK`gHRBVS_V6J|o;BeZxaJun?3pF+7t2zU?Uc*m?;2LUH&2HLv~G`g=9 z2Dsf32*czhtbJp~-*s0W0yya2{(mI*`?h_R;YI&08a^l(qqi8RoJ~HM+#C*o7ckWmFd52l zfl55Ur897_#ZKXt-oUGUw`i6Az%PSA0NBe~pV1)9`~%bpeqx|^9OJ(I@W`5-4mMjy zJU?er(PEEjmTDm(;8v2@wOsHdF~R%(yMxzn_EXDV538sJ^Ce963XDc`r18>G+qLI_ z_dvTDzVdWyj+S_Zm-rLrp~e<1koK2of#>)tn4=ZO2)uBGK&5l}Eg~=)IYdtwVLTOC z&c{TIYCHzToE*1;TxIhT+77ZqF=%ZNBW0jJis&Z8{J@@vXNG8TH5eB54i z+9t|X&XuMh#I*l~m{xt7X&*w0zLY{5eA@8nZ^T$;T4^B^gI5YI$4WL~ zcKvqWk=%mFUti)Q1rHdf6UymW@QlWLBAu3uYgiO4KBHaubQu&O^JclS_3EUO!qyWv zu*>*)T;#_VP6XfSJ&jeaS+}C;DIGHvS9ujfv+ztvdzu6#agyXb*GbZ=X2x%%-M4*m z2Mh`#9AO{(e?m8eZ-nJtSlx%K@l#P&R7=}i(gkC-rt9Wh7uZ}xaNV8KwxEPR2GvRG b+HV+po(Z3oBAXWMxjjbN*iC#JZ@Pa0ppyLR literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2284d54b9881c39df7c40d8991016f67c1552f83 GIT binary patch literal 27214 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 PZhgb=HcE`>F{1q+Tq?WX literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52c469b62ff6f0a2b4fd2c1ff09dcf0f31009cb7 GIT binary patch literal 1169 zcma)5&2AGh5VrR(+bAieRe=L1I7K4e1N4A|5Hvv*5-CU#m%Uid+9XT&ueP@$Rd1!-qOg?f~%=C?^HgM3U)R}xrnp*)NmswJX*w=1na%Mc)mN1wNde`G;yB2%y_cY zv4*|=-s}BcHl47^+wrS65d`>S*jJi<0zi_G3KCl9SxBXTe^U93#aV0^Q|M5+Xj_YFKA27w)}z`MEfyF@8cw9DTqU(Mm$Y)U zOg_N6##oeatr@!{KR*ZWwNyHY7I0vmXMzXgJStO}88GHDIO0QChVm2S5;x`oilYrH zRQ^#$CTP^5%#MR3o}s$Pv&TD6105SVEO>Ot=TZknTmN1w* zqD&C?7&|I?((Kf{RX*?tB{EC31zy!Qh^l{8=}0CCw9d`_*YOu5q3*5+05m>$pY}Y9 zc5RE+c5K31>FkPvrcMeusy3U})A$tA<|)>VS#8vnZS`T(njU!3(5Kk9wd+YoAs_$% literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f40dfe4db52ed46d0f1c337397806651045aa2bc GIT binary patch literal 6255 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 Z!Oc6h{J#`0qT6K%pAs^bHmmcx_YY+D^~nGL literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65b5316d03f4c6a265df266a92c3ec16978e2817 GIT binary patch literal 3518 zcmai1&2JmW6`%beMbVV($g$HXX)$g35jK(7B0zx{u47krV*`R(SPBZzbyv%sA-UFa zmzo*ckwBpW>>m3E^dT#B;PuE5!uw{PCpyx*Ir zaA~Q|@ce1-KjFWwGWHEEPCq^txAD_I!62A$64ox>PTLW#@Dg|6wLOP@$%HQ|FPNxE z|GC$$2xpr$tFN((ZM}9dHl4ijYn654w6|?SBP05B`th*1jh{|2NCxq&?Fy&uiEF|W z{tKrK1)}Hg+&N#y#E^zcNmngE(JQ8w@cQ>3TVH9ZbSvtIY1qqB5w`AT(P$u317apy`{5d1Yx3)%RB=Hb z9vG=jFjUzS8JSkGT$Ab3RuXq;+0W9?)<19Q*vPeE8102UsawN%*b1J?RAh>@DItx0 zYiS?}FIaT}}UY`r+`jjoAOqf12r-<5UHZU>l2}RGP=&1?_ zO;1s{^ToUOcJ6KLJlOhDk&ai6B6b^*Vpt}wijpwyQ%tozH0SW@ycPEQTPBbh)&$D+ zQgtEs9gxndX;3tv$R1a#)aD+p&TBi(_Er}C52a1ldY5~hQTMEFzbmEqtkmfouFUJC z&lfvLu^Y1|Wpo`IO?94=6`Qp`D>b=iV`;x%tjflcN`Sl7gAn^%9MWaLAc(TG8w;66 z@&?$aNBH5qgY%4U>8GTzXSsrjVJD72ks7m?-Vq#!HQtu4;mgO5*xK(OH$V9&9Rml@ zTqSc)k2+09y$jOWuwN^18}#E|Kj;8XL6#;5KL@8KN140q9DX!!=(#)Q>?K?2M6A8P z#l*1Z?8q_B3924!c8}b#dzU@FedLXu6L0KMx&4FtK6~jn3@fZ@b>xrz6aOXCn`2-2 zdrWPL3TU(g#mHg%*T=RLk3I(|>U{G2Msgv$aVoj#D~a)7C^gTzoV?RFO1>}|_QQ@e zH@HlDNv!*PO40mLxORAFt^K%Uj#Cu3G&FIRhRF>+x8wweFaiTM{`Gk6Z-1HX8-!|4 zDyVoW6{-;mBbSMm{L$|U4HNcK&rV-L_;6%6AkvkJM`6l43f(PI<^t zF5|+rXPFWjZYS{ol}?o6ItRS4K(F(ieyn+Slt$zP$BsQs;)>IR?+MW$#q>inQ({V4 zi43*j8RB|@_v`!)gphf0Z?p;BN^&KKNf=3iqak+?okVhtIxTsq zqd1;LcD-J1Ec~#y4G)LIghBv6$N?RxcxW_#){mn;7a318!$%1By~zpBPHMfBr+K|3%;fAZV`f>Yp;je%3) zy~iwOE8_~V>Z+T>pEj_nV0B+qf%%G90E$<~l@kxBUd29d&sWD|S04k_Bnika>i{UC zW-Ee~e&p@1g7aj`DHdm(sDJhF?@y55E2u` z4iseF>0F(pODSwx7LJtRuS_lx9&hQmTa7tqagHsU-zy#QC6^R+mm@`cv4H>Spkag)X~JzK;jk=>I0(xkOu1TPE{M~!WFffif;ao@Y59F zn(MpuO3icO*{RXT`unb9c@b^uJ&Fogk?Clli|)3Xb*M47Oy_CJYc~sjZ_&BhF1zN` H5Xk=mdg6~g literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abe92c66d6b21dda398062a8718cff1d8939c2d4 GIT binary patch literal 2945 zcmb7G%Wm676djU!(6X$id7uf}6w@w>2#uuxSp*G=HffS})3}A5bfFC~=8Pmd6sgRN zlsIr!$rp4{pu1$5U*fi_{6f2E&mD@EWvc~BgQIz#d*|G9FD#v!ttKd~Is7Y^+bsO)oo9(>!(009| z{ZPc?BvX4124w1>Zl! zf>BRrDvK2#<{EQTp=}b+QlU9!9dM6-pKAxE08*Z^W5uPy1uaf#6{}>f%yphv{wzsT zK2%C_I~I1aJLd{kVKcVa=&)AZ5_6HHVn0>ad7i2A6iy3nlZoo_M=-Z~ARdcsqzo*Y z6L9;&z;K??G@oiUPx6^roD>AiInRNi zaZO_q$mCd@{Mk5xC}Tt*&NC|zG)qL2i>Ki#sZ+$U#VHlerAj~I(mhNDYW0bkJiV0ZVY-HO>V;3tUvFHRAobQ$~8rub@_I*OO+xxCG~tDMEk zps`bTD@j{I-mh}ok))o-=?o$xX3xS^r}$9k6N-X?STrmk58V_3hm6U3?r7|z@>LJ@ z1HAX}nj2`2ydxH&@}l)z>q~}yU~6ycw(*wcn@8+T_?CI7&?tZYj&_B2qi94EBtlwr zqIjltL;{Eq#cE9Qi&B*po8`KbchOAq(Fnz}*cd7MBl=1gVH9OzqN1p1&?;u?$a7`8 zeo-?j9TvWsykq(t-2L;{{$r!G=~Fopqdb$Me?O0B6Qmm^bJc$;w(+yAo=;JA%jqdR zGd7y)`~dZ{Uka@){Q$#Vg{V&+)N1#uyy214!cv zL~@0_^fB_Re-J$J^$m$pRCDVA)_uGzs?!cPSG&S4V)i5UGH~NM)@dI^cEoDzpe7r) za8-vupY-NVu^Fern8?yM$tkSpR0=95i*~tVh*F#DV$(%ov7fHlZRk3F94(F~eUXL@ zYMQiOb5s>xT(q249bD0J;&ospfh04>V$sUfa~rv(?=Wcyj|CmCjo6;^JD&e{%|GXL zycbv3(7QZ)CdL$J7i02`_}OFn0=iw#IjS$Af7+J|NEeq!5PC$!AhUkPzAD0BtZ`w@ zku$y=>y7?XbFo6KU78m)N=gXKvAWKu%g%sHiISygkraC;9Q*R$99whl|9BUs_8sRyO3i0ziV!6RWyc;m(utE}j176T&7Vq2CVv0T*!_$p8QV literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdecddb66a9a546e7b6a268bb2b5fdfd9167a0d3 GIT binary patch literal 390 zcmYjLJ5B>J5cRHilTDN~2yq5dY%Gxq2_XtZiI9MTZC1{Dk~kmR*iMwa0C(UBxsqF| zTmc%!q+v$$p57bj&G6x{Pq02$KU`&md`0oUQWSSME`lH;S(AzYStcS~5ql|Um5QvQ zkU>66s}AHG=2;10wNVr+!(5BBOXk2MRf_*^T& z*}ZPI8&Doi4eXtlNG0qISb4HdO!2j`dI^nZCs~5pv9+8=xzK7dzGhB(C=G8`d=8Eo sX;{4jCA5v#tN@P(HirALX+v?d(PF!X+Y9{4ogoyopJdoY(n(MJ3v?20UjP6A literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7357323c5de08ea3974cdda6483be69acaafa9c4 GIT binary patch literal 1176 zcma)5J8u**5VrSqn{as$2m*-;suNtcAWbC1>n@@Q7a~q+wy}I`=WfZqV*4U4N_UY` z@C)cdnZL9x6@LL5W_A;0BZP!kJDwTaGvjYQ^QEOl0^{4^Z~nSP$PYA{!vW(B{JIZ9 z5J3}?(vVWrt;9<0(5`elaZ)#QE8R)Fv=z1}c~68ZyfY#^>CWxY7p)=b_&+Ix>d^Ah z_I_{YQ$N;5#iOZ-^XzTLlbMb+EUxu(5ohC};YJR4G>J0_-UoweV&Zo*BYP9BhSKb* zd?XdDEcf=dHn-Stzy=@t+aDrGackIDmfi$Kl8_1#TIb||3M#BKKeVMIY~h@dISpOV ztGYq=&WS@pth-g-sdCgQBfP}*Kphj{ub+c3#L!bRw=S&jgc4&LXAU-VjW;JSw@%50 zk1K*eV7l8`C|x}*q*5LnE`7%0EH;cO45(c6EkqR_OsC31dtEeeV;q4yk*ai+)Y4qi z($O+GfO&zjDB)T&c1eDH4feHEI*2B4V4P=y2mL&nrZO|&%w=%IyD)X-7pNt!-35TH z4J%asSw<$P&92OjgCriIImxpZ8(V>njqDaYI^<)igCZ^hb}TcIE6k>ZG(l8Ahae6# zY@?VdAFlBfevJyD%XHE9>B;>D#v9i-`~@}w-ovvEaMhLp<>t*P&B@K zpSFDqfVXJnM0tC&T=IkT|rj2R$)s@54;+Umgl+Wg+U(V0xN=CRB{3264OD}m0SebU?#|>l1m_$nFX?? zWF6!Uwh8j4k`1=i(<*mn!m5WMiO)$g=ERQDiG&_a2o;>9k~5G*oQ}qEnkW^2Oak7Kb}-9A8zcYkT!x({Zx0-D|G8ht9#F*Lc*of7iq&EUegH zzt&DAoTmmRi|8nh7_GHpKb_zmYJSXXPw5W4cKFXzE`r7I^b+opcvBHSh8xrt;tr24 zYGH7O;y8}Jt$tUNLBe;IZj!ZWFs*qPJYuoH-Oz_MG@7hVF9lxL1d_ z>U8b8n;CA$^_mZlvO>Mx&Wz0hN1#NQcTOCN#pab;XcpBoKXZrOdi*>~K7%-rvsx-HM?XS&3-0tzGDUW>K4xaEZ7{@W2 z{dBu&!dIGDN+>~up+4j^Igi6Ieg<)-AQ|xaB8XE-;s{CyqW2p{n&ZKJjN?_qK2j4% zVy?0rfJc6|yDNB-ib(E41>gjr48sw2zjhWMV1E>g0I)xZ)BzCks^Tszqr$9`%L;xd zuZ0{YU$#Bsi3ghA)dJt+_88n+VfMwkfvW3YxE1Th*f(AKpx$?Wv@`S6Z97MH`%$JZ z-{wiD2N(i#8-7TooNz#MCccDiubolV4}}t+!NfN}65(P6+<+4Kd`{q;5A<3?OYQ;5 z&wY?701Ir%m8@z>gytFev_vP|!#q z5|`!uKe+e&Uu6Fv`y1IyU~Z*+qqoWcPWC>aek-%u@bVe}=SP{|dvx6Bw6jvnIRNM~ zmV4jmBuv3NkVL8*UJv~SuVGaoGeN`64^ Ps$Gi4z4E=gK*0Y8cifLe literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2238d81d56eed229f92f2bc79f815acb990b1d63 GIT binary patch literal 2668 zcma)7OK%)S5bmCtot=H!S)AY?5{3YWMc51B0ti7Od!0Cm*Ou2oXe88(r+Xjv?CiRG zHpHv-$(AA{xC19!;*v{#0l#E!h*M77;X+lQRck5gI+yGc|99RxjgadbVfxvR+optkCJ@yj(Bu z<$DFM&?|aHO70P!;r0>XwxEZmSK`?wsX8wyY+t2GX{qDOrieFWe_KcpRF?X@8?~FU zAB#0V=yW4dHIwoqKWY#Cwpi(gv5-j-^`~k{aiXXfRjs6O-($@+wzj@pf0R^Ko9nkf zynTDAaas0Yq$9Bk?=9*UPjnF z%bg=~NWCoZIiA(7S!t5mIdLyTVu9aNm_8 z=(nTp$lnfyD^%bQgbT}6r`w8MCH97}9M6&hRKu>|Za;G4zTT?KMHF{i`)=G33RX`K zTGX?Rz|c-h3G(Ph6S_JE5|fyY$)T}hj47u-0@B)KOqe+)J64?8vF{MM47_=qjh#4m zNKZiH^gn<4kGciW{q}UOcww#tbK}#wayrNHq!~?THNB!~WlWA=J0XZ^2#C=DSQbJ6 zAZuy#Rp*k78V-b%1-O6WFxHK_F=I(Cbud#sS%evhb^@U9uQuwlHITPV^F<%m!DNLm#BOx?#)krKQ!j`_@bDHEB3qoHh#-5YEzOOx2LaG{U7y0de z#QoZGKN$8PxFGC{+Md4v!-9AUws%wUU_TZrW&pSyfLSg5T@cZeTG-vj-%daJc=6Mk z>c(PW;0L>KCsiAC2Q~HtJkXbTngmN~fH~L&iooJvUzWk-_o1sdfe>1uHZ9Uw_>f|^ zC*SD$tI4uB4M%__X4(*T=xq(V2f$2WcWhvw5g5zloB0Dert0S29EXuvW17zH&BYni zI0T?FTSFwPsWSn{Ry8L}u&D-woJS67D?~62T4IJGl5;4&isTxQsv)lUt^Ym~ z{f73+Jf}@~x1eJtn~nC1d%ut7Ui|rG8#Hv2Z^Q7fw<)(p47nDyu)GCg4fM1l3538h zMsHp6$3^`MRWAdJvFm933e1=S++aA1y2Sst4^2tOE)jj?e;53I2jn>*I7 zAv*v`lK}uNvXkM)C&VBOL7Cy^E|tH;_D&YS>3}3>K;W@A3G9UH^RT`Q8U?Lk9v4rr z?v>!QGK@2?uGzCt0{*fZD-tpE5;{CgDOPU+pX4L)G)^mvL~tdU`sU{PW{Pn%G_f1& zTWqZ^U)Qr~4YF6anjlM@mG#GsWwyGN%%?uXXDQu{_0l&#O_XqgqZ%lYC68sKm5QDw8K4oip{U zi4z$S4SPzzP!i)ciK0*eJYJ_Sl75Hu`;%6-v+JEp+Rsy8e1Rf`?^qG4)Eu3+{sFBY Bf!hE8 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e5679af9dd59ead78dcfabcd04ed55758e60fec GIT binary patch literal 7117 zcmeHLOK;mo5GG}cddsgQ%^@&+=pibgFjYAL6Uob z{)Gbl1N}$AUUT9vXc1pJvwVnRXd4U+3veZ3AM@nQaP|{HQmd6E`2G3joYWhV^cjix zuLHzW7|y2^Num;!$I?XBWm%#*J;&tND=-v_b9^YsO&NLMrHxQm_@IKKsHad>%z2s* zxV}uGa0$t5M*VmU(SGFwu@`p%-eqfA zUP)eL??<2TC^C-oAZw9-oOi+gE88pdR)$aYGOg+rR-!dlW))fws#2w^dJU|vt_oHw z%IJ!HvJw~3kw5?Whh9_b<#$JzdALR%Lfc>s?--X~WrMn*j_8eHN3YX0eU;VdI;*o) zxz4`02X6wn1Ene6~Ky_F{ z<(0vhIF8|xS7XMjJ~1Q%*RpxVLBtJ2^}#8zjWII=S~1Cl@#2`6Cuig&z-0{N@-S{h zdOf&YSa>oK{@h5_ZULN&{##Cxy{y3^YS1WUI;dU=5avVWMen%TdfaL?caQwH+v`Em zSqpsQ!OnuBUK&`Y@xC#ZOlHLhxFZ1dX1s)}HB`ob(M*r-Rx8gsXSrJhjQr2hyHU=5 zx;*@17=zNim%O+~rUYMduM<=g;E|oo0;=|f02<+1_DWro#S$?y{$VVkbP1*Sma(8d zh+*S?m#h1h&4_u_xXbg*JexQ+e%s<|M>lp4dOY9j?{xb--|lvgLHR|$XB@V99;CfK zUq_|RPS-eS9~u3fX9sOwLCK+Uc>KJ5z-#b6i?6f0N5WTiMuPGE8I|y{F_V1dYVYNo zD=nD0cQSEoO#7WQVQI2=Q)Z8R_Vm5W9M_oI)@wF!HJ@xU^Q|@>y+ZQTGM_y9NpnUn zYfj1F4SZg4wCQN78E=_MEgSD3^g~p@0AY literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddfbf8459f31a5a4cc13e2973b64eaf4e550ca2c GIT binary patch literal 2482 zcma(TO-~y~bY}L0wONcqLIP1$5ve&?DmYTBs+FRuDbRc<7=+ECma6HpGnh5|VP@A6 zSk4Km9D3=sH#qc=Yk$rh+f!~mw}q znccB=+= zI2H)6RTYnzH#jbtU1(k0BB1xAD(q*s69u-uuQ?F|o+!8k6wseFe z+*5KwyDs47<$zC#im0BFt|z924|C-eu}QZImYC7gMt}LT5Y2M=A&$y%T z8v(9<3M0ofByg?KjftpOG4}n>Z7@DoFAoU{!Ngi47tSp|+bU%nRYuD@RoX|(fV+4a zB=P|sX8;HrElg^h-1NT`YwuYw2vP`y53Ba#AngsoRT#{dYOh)TazkkDg!h6E!xSUW zy!8uswXr-sf!`YW;g6RLY|qZDF&(o-a!y}bW5|rYbHG#!R@}2G0a?=bU{uEJ97(Dp5XJGdp@N4h!XMyh<`So-1(jGIxfb;tpQhyP0u75*V4{@G5yNT4; zK8JD%*sVz#bd)>bhQeAP2=9=eP{QQ?53IszD(nkw%xIg3o8CTPzfkqSNWODUO8ji$ z9JMbV*hpW#2RQQ(9ZqX>?|!}ne`P)_?MKBDWOb&Nf?{bGQV!PMpv4u2)|PfF5ck+%1w6bd>S zs5e*9m9_QG`fBHC>#?5gWd)aMHtg+l5fp*08JStS7YUh$QqQ%YY;Jc}cRIZDWP2Tm z=W7i|&7m|84FPvlnnsW;7j@CJCl7DK(GJ&hN;Tl%moV~&00^zKx~VJGw>I;rYjA{E zYihJk=hy;eu!hjJYOF?m>f>HEs!t{%KJi-ep|x?CO!ycO@GLo_5SbX^%wViAnQ+3c zs*ou=%dow)mz4xZ4Xb=DJkaz*4)dP&FUr25-hU(t_#Q@%9fFwWjP6ce_qvUt>p-W$ zBEvbjk0+TJ#)$iz9}a`KTrmaNlt1l3R6++Rjwd1q4XaSGp1Ei#p^Rf_T~`h;mDChb z>~5dH$g!Gw)SvQgMynPM@1Ci`twv2dG94zliODj0XOrEFYFDO(!WyjSO3$0nO3MFA zOuvNohn;1jJ(eBQ?GM7VI4WDD3nFvqrPy1`@_%*Tt4i!WEN${Nc9R@CQyl}L{8|43 DogQEJ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2402404ed53e86f175f30b98e7d29393e89999fb GIT binary patch literal 12098 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 Kq)nYR&Hn)+!ee3p literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34abb6dbddb03944f61f7550bcad0124de44b78d GIT binary patch literal 1177 zcma)5&2AGh5VrR(+mM!06$BC|I7K4e16+_00+NbDNkNTz*^A|@-DKPSNo{XMqTW)e z2cCdiIre=x?UfU+02gLmF93jc>}*5 zfDlB`grqd26tR_9sU6xCwi73HL$|_C;-#&yMag?2T;ZJ&;Yqr*LtnH;r0f5r;Hpc@ z2g4#U@w>T^{V7)?X+EiZEETM5^m7qslab*@4tX??=*b03b<71qrQlazF(Y)|nsL(h;_B&d8F6F5oI} zz}`7=NQim2$~#qzI?D(zaXk=Y0Q~hc5QZ3fN|x4z^^H(sY~w6JXRh&<1m@N$x$tpC z5O7R)yDO!ui=_(bYp7S959A~TUIjHZM%fh?jU1%9B$ zTeG?H;Tlij*9Zt*qiyQb#b$%!t!p0sA{zzHhX)cNcafh+vf+zJ##a-zGmry4&R~z6~ZGtFQugV?CB!T|9Az%%+APZHCJprKc z;rq1XS+s3iw6bF--c83>6x6j+NK&~`_n*cmnbuFT>dtb#DsQ71>;Ck>i-u;!=B->e Wa=EF9|Ej*=>&KW7HbRG1pZy2UJR;)& literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16a525630c7db3561d8b1a53221bf3315bd969d2 GIT binary patch literal 27218 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9fb318754c75002e3c2fca0219bf2f483ce1508 GIT binary patch literal 1177 zcma)5&2AGh5VrR(+mM!06$BC&I1!0-4{$+32uLa}q#&hLrM+0r+D(@3PilKx67`l! zJ@5qF%CXPGZLgeo1-LM?+o-7!5?<~2o3TAJ{$|WqSDOUJ*TdiZyGO_mRF;PW%4_&_ z4}>6sCM2aHrHHM>O6}0Du$?%m8@d&C5-)9p4NBe-;R^4J2v5?v9r~g%AZ`CA1y^la z-s=~MiQi6*?2fq_Nb^zUL#be8qnnF38x0IMvd^P&oJp`g*xu{z?)PGCR6Hz9oM&$` zo=kPDVR5ZmWP?8If9P$$k08X2VP9$bIRHsQDoALZlLIQKu+IF@mX5H6b4KPgbOBd+ z1NP2|Lqg2EQQoRz)LBM&iN!#S0r1yPK^S7_DVbXr);B_lv5hkaow>%F6PO#Px4Q(H* zkNGiHHW^EEQ6z|4j2#s`saGoQN*}m{I+>~30JmxrM7erZ?novH^v^W`Yq$kjs9Nk1 z0F4jdr!CK-P1~ZC9Xs)MI=rHwu9ZTP%8k1JG(O3+ev(ypmg`k{mzuHePY=9kXjW|A Z%Jot%*A?+!))##B5EH^iXwmAk{s7&MBT)bV literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85a13795673eaf12bd3040aadaeada8b46f49e1b GIT binary patch literal 19142 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 Un-adRANb}m7KCJI(dy^?0Wvrt*Z=?k literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76e041b1ec4a36464da1baff0171c3e885f26e7d GIT binary patch literal 3054 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3eafefae3de3a64cecbd98a70fbb0a9fc88e0dde GIT binary patch literal 37643 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 U2>I6M5`s^~-mjiIf9mo70j@|ltpET3 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92f796617c5611472a8a183f521e4b0a000209aa GIT binary patch literal 23667 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 V+gUh&-nM_XELyV7o;QEXe*v|T&C~z@ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48fe1c83c47e4af5f46f5797d8788834bcd25028 GIT binary patch literal 29131 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 D7;wNE literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9bc08a85572e5f2b4b3e5e882fd520edcaf1387 GIT binary patch literal 23625 zcmeI4+ml>X6~;Ru2_XU!Q6ygRPQ+n^7z2u2L%0|cAaV=n&`cjDlT5ms-JKhtsQ7H@ zqg7VHJa7+y7iyJNMRqkG@euuXD81+jMkOZ}ZX3Rj1nAyP~(Hx3#yecjd3Q z9lfI3^j2j1TahijtB!Pb@3^_MsnePKyRJ{Q(HbWIdMA1FJKbrdTAVxir{u4g8BG2m zsV&pMyB|JzveujQ1jhtF5WFO)1Y^O=f`Q=of>VMQ1kVay5j-V0EI2J#790^gAV`8U zf(5~%;H2OWH8gK%^00UZ1dj`z5ZonrQSg1ibAtVXvx2VRF~QS<2L<~ChXfu!Dj_~1h)uw3qCJ+UU0KO--*e*;A?`tf-ei|uS;k0ZNXOr zUl4p6RqjlBA22^gxrIWzsE4dV(pQpdNS9y-K zcQENyOM1alys-OZ>BTAcH|zT{YrVKl@v`6DI7ji;>HAV1Kg#s;=xvmH`O)fK!XSQPXGmEdK;lHizNS#Vr%La-t@Dd-CZf}!9Q zK@yAvW5FrGX~7x6S;0BMtAf`AuM2)C_>tfZ!H)$$5&Tr}ra(KrxpQ6pO{|Sh`q^bz z`wrr%#`6dWw*|xnF%kMlg#}v5XQf*_aI=8?jt%#4P*%j=$#9ieoXG zt;8zWU+(^)Vr4m(gVsFO`zDZ^gP~cDHAIEaY=MBSQ%*YAuFKEwOdB;z)xy(qiV)RV1h&L5!h; zT-=`}C^ONbOD{#rx+LOUK0(Y8i5x^5@WRb?PpwgGIY{vm8>PkM(^s?*ZKg0Zqhi_6 zB$yWpS+jDKh?xq!ur>t5s5H*xbPvU1B(PWsG30ZW@i=4SSh*1*Rs?3FHuv@*kXs!p z)MAmv;_W7e)m#g2h86?ntT-sQp@`8gu32R;-D68OCDf#(>na4VyeD+{tjQ3@Ngv z($)=uw&WS|*jk7(AX_8Gxv>{&i`_%9O`23>p^*ImTWrWI=3sp0h!_jQH5ID}7jg&$ za2gdG!ir%Si}C3m3bC-*Us%+#7c5k)4A$kh zb4g&?ktEE5$Nq9}l*Nkukw7_)G}qcN*T{&%BW7eMSUE~AM#WT?NwLXLiEIw+3{xRX zB6mxIX9=t2@JN`;m({YIO9d8X@DQMb_h=O-D(YDqIo?`Ck!J_RGlF8L<;oDvNlg{w z*5%$+ld@iNZRENS709MyXW&vn45?xkatjJ%lr5HX8G-$^I4zdLo~c03PGg;rjj|D4 zhQVT!F%gO4fI(@&D+*YGWjA*ayc{`VG-Ul*g}Kc~TBml0?4Y6)t)Uk&(<7U+a;Mpm zwg^;6gi4DY> zP_Rrsm#|pzDFhENhS*DPyp@gR6DD7NBaMcZf2Y*AtIw1(A!5@cHA3}Qw>K4%pL zrc%j~i^If`#Vk!WQ<}4=L6pVe zxTp{-_E)4>qUc7#vYbW&i!4?yn~PCl7MGAj2%usvJS@n>%xXcgiVf>FU&7|>`VGC# z=i2i%H#b&La*!hvYoQFeXvH>iE6K%jD$zy@@@(48b`cSH>6T7#q?p!?9O$+0}!XWjkv~aRJT-`ZL)ne7F`ioWVkJIwm)f>j=hRciH zm4)G8v^-uuT`i>UczLjT{fVj?E>!)cmF3Z~g_UYNuF~q&k{ngzg?{&BwYq(!+h01> zU8-Ka`pj~_H#jqzyY2S9dk*d1x_aZmTBN^k;lEqhRa3>vq^^z557wfO4SLne>U9@H zB6=aR^O+09gnrveztnF)6rBJ0{8OVUjppl9``x8Mzt^4LKUh3hz+9M4~H=c@kc`IY6Db?Hd3<@znVw!G2Vx_Q&Z Pzs_&lanati`TTzXqwg*k literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6255f962d78c2efd4d7798a643c96a611de4126 GIT binary patch literal 22252 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 gyAw_b=Il6U*E#3)&7D1Kb%7 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95dee4a732babcad92f8164c2cc65d387c9e5f7a GIT binary patch literal 22231 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cebdff6e55b4c5fbe3226c262d6fca4cc48bc27 GIT binary patch literal 2990 zcmc&$&2Jk;6rY*>@Ora$(h}N+7AQi3S|YV7K(tatQ@J02+N9u=!>Taddd7~^^{z8J zF4Q;|5~&v?xN>Wcz3@lyXY2{Y0p$zxR>3bBLJAen9*{N_RIqI?w1g$>Z4%nj5sq-T$rcS=;7h_Yd`Xmr4|-3`hyZ@e z4~R`dA7Wq0gKLrQCTEJ6x6s$Ufjr)fU-lLdO*9>mE%uN-BG`#Wgj3 zuDQ9B4f;~40Jh~G=iQ{MInU>K9A%ofAkTOU*etK`wvrpXl@1auRqiO6N&T4Sb|&LC z{FZrP$oXUPK2j;b&2P4R?17asR%SaAjYm z>$0Wm#d1+5_v&%?4ldWyRDII#eUSf8+Bw>?|1ukeorPMRd|~cQrBYx6dVM_ zak_tBRpD(BVuM*vXqCD!hc8d!d3x{V2Lju`%_o6qgc5?;p_> z*_0FDYOSn(q-J4H9$fkC@~6CUag{IMysqXzq_8g4AtZ;99C=EP?kPIJWSH9od=2RE z@RYX({C~KLd0((93`Cr1hGGH-*iy+ELz4J^auvlCVu9nw}FA2CL{WYZqgCGM85cB zlR@^g1+r;rHX`emwjos3Q34WsWD9zisU_{KyAa9}B^x2wcq7{ZyIaW2r%w<-2j>4FGgCZJ5%TD?l`h>#2}& zkr(C0wTsQBNeZmo++AK-<<~FgF3+%yn@oZ7M84*zm(aw!14ROAmD5)4V>ouJ)5hZ5 zmq)qXmQrZgmmLE_s82nZK4pKo3^|(~rXCv}nu;;*nTjp?hM&Rv7BGMc^p|x8IL={~ zX+Ue;-ZJ!LDW%pgB6# zQ4DaUNxLg#(vtgus&*jt$Leq4m|e&MYBPk*Wk&!u8(_07j}DJdK{ma+HiLZ>3kmwD z&v`EugBbaM^Nm3i7aG%)Obf_8=v2@K^?mgUKH>zDQ%K$c0)4j#aZAQA=M$d8f1^a1 zeu2IFHhdZO7LNw5=P>G9_<2>+*_UgF^2%=Y7JbpQpxlwkpqCZkEx_j7x_Rk}!feT{ zi_Hd-CXmMGNLFx(TysU81QYcdlGl--2NSNDeul2)-udGBAHE8!46`So0kfG6$u~8v NIr^XGFYeoRc0S$!pu4e2~8ySyyKnyn3c6CL ztV&I3Wj9g{slQX%wp3MSLL9|ur=cS)P53x!@5ZULTpNyDYG?zt{Cgmhw5T90CTPnM zH=dInYV{Uct{r>AKMGo&^u?42j>sWxO~JSzigp}`X%QZgRzZ|R8UBmUiA!43VCjsx z^>~oz`0E2LFWEY*6f4lKUbIW~HnZkU2#%f8o__=bO*9>mL)K*@D(KJFbtA$ZL4dk~ z3D#jS@&o}hoH{sbI*HN_--#0~)k~CG^^KPg`cf$mI1_Llr?KYTEQmPQD&8LGI7@ky zM#;fYDpP1@0({H!o+3; zlcgf6t!C{(Po^3sGg;e@mf&wmJ_R%4$#frl1+x1p>&mvSP1q%wKB*<~HnO`}`pNSB zS{`e;)Q{SGQAg&re%!C|Co&b8LTyS&UF&V*D3=;!L4GXv4^#n+`~Z3$03mdi`tS|k zzB&%eXC7%|zMVB3pNHZBFlV7SW@AFZ{ar`hMS9M=Y8iNU!V3u3h0w6NngSVBU!g^l zNO($u(P|ne5JxHm@`5Tr5>aI&Ge~YCnFX>0J;yzSmf<=@+lOynb15tQwHx8BR!|O_ zp~;Vch6d=YLqzB5nO^Vp6N&MWVoj+`sX5p-G2`486P~)8r%_LShQe$Lv*CN!m~wId z|FcNVquvE1S6L8Xjt?Lq?qAS;U3Aa(UIH3I0Rv)v!N4mvy7UmfRr15{&j3_icI=Gk zh}|J$`pg+Q+U*|IFWj z`OwOr8Ix!32tW)ozX8BO7tG%j&IA0PJ)T2uoQtf@ zxvk5#mYD!@3?2i0a3K%SW!4r`I(=Q*GD+arzpk}q(N;u6@VjU;md1d(ZrKk5MvD&i w;4NEX>+trKEff2LptO91J27dAOHXW literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f39600826cb0b12fd83e5a50f908dd3540b7bdd8 GIT binary patch literal 1166 zcmZ8gOK;Oa5Z<+&mz}gUJj986kXnltq*g)*wS|fzs*1Ei@@3`No7C;vad(|UQ4UC{ z)Dz;&4UYXwD{h?l2M8|AtV8-(YsTLAwQ8^T@H}vVCoaN zn8Z|&lm?U{wuF`1fo-rYoU{^D40c2{tpzoME25q@f`-9W;ig{T8C(<1v=y`ru8X;J zKA1PSA#SA$!2%^)Bz9x(jKm(N6FX?f%`WM*eo-i+Lv?%OAe3ERY{}s$-<882mk@RL z_g=1EvhMb)?u>aa_BXb7XSBJszP$2idB*Yc>$jJ5V|VTG+KjsE$>7P1;oiM>C5@n- zYYPX4OFaO`NkC%~Sg~_X`ZTs;`>YYz=foj_1MO7w{G0WS?v*BXBFp?;I>dD_8%%A2 zD@Z}dWMUm!V;a-XAVXnKNa280z^Y(1usT=+%$<-yeN31)Cf}Oh2@V*tT01Z*PlVZk z^%GIRO4_Z64Qj}F2t91OEK^f!mmDCJoJ!6EmL!$o*4@()l@ zQik)>f~$f-mO~yDen}U3cH)bqhxA~Wtt_qiDk=D49!5vufGa;wa-W^>EFMbCCSzXs zX)jU(JTqm*OZlmsgCZY-se9lEZCeh#_WZ88AMf9gY3cunI&=qAgo$Ng${EuxW9cv+ z3B*mtjz^&=znC<-HJwDng@F0qfrO=NBf5(YCFp=DBR(~Jr*m7ku>~0JmFgvK(e9|| zua@l?!L+Lm6EzK>c-rHm=y2MxQBpStJv{x>O{lBcbR?${6j^&w4tHQ0LB+M(gKIDi lp!(*g;{7y&N5PbPr7zPV_ND7j%fURud!ex3CcW|4{{VYGDJ}p2 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a9f6804d9ae18b88d953fcc4973911ea1d81327 GIT binary patch literal 15753 zcmeHNdv6m*5cef_ekIO3jtO}IA;f@jfl`4~RVBeZ6F_mIO81XtylWCj?9_HBx2*Fm`&D-%fdB~*WjC;==!y~`eR>~NUi7n{3O@p^Mn8?w8N3SdZAcgjUOj2;*c{kf=@PI-1q8U5)x&2Dg{@q2^;y z^KtjsQEeG_6c3jt)9Q2-@9I%9pbyXp8KhA_3k!3}ti_!??mg2OFU+8f% zsZY^K5~p!GMW%2@36juH*)kno+oHz4<2JeHA-6e?>Uf9C=M+y}H#?B$=HO^g&1N-C zPU)xVDK@KVdYVk*tY*k*ebzmzx;S!6=52WfywB-g%?W*u%;@uUmc`y2ohP%Xd4bI7 zXNX4P%pS-gY|lvZaQC?ePfeoM#c{lk`A8Ez))IO&G7l8gY2z(UE``--fAOY%mMrM! z=o!X+mYySL(C$TM_jyFbS=ouz8nXuG0rSHe`qqeDf?Xheo?R{)=i-)HG(PIcv?F#BNAs$X>A16Vf`U8jNndbcn!I)h-OU;KZ3;H+v=lXDkFCD2WX zN<&@Qd440Q83Tpge6^aZ<)7_RW6P&^`=m%wj_@-<~W?^vf{V*LGos0XE_k zZ3}2$r9hbM&6fK#fPVH52ewNv=it_bs7pp&N!Q7@u49<+8v>zg!W%|ITYY-#%Ujm* z^>{LY*oPg;LjfIPeOND_{XTFk6_L|T3$ryQi*BSe#EPBdx7_Ku1#e*L>CFO`I~kOp zyD*9{p1|tEU}kGI_3-I2KI?z(XU~QwE1&S87fHRjk0h&FND2*EaPx_xA>T<5(2SE% z^qBBVN$hXOjp`mi?d@xA0Q5jowo@TtBV0nM?PTADw$ zh_mRYx-FuSs9Ypuzt!aJEv$JmBp>gj&4C_-cPfYB*C~2XNY#G$JkOTxhf?#6Ao6Jf&X$+wxk`Ht1M>e$|XyAx-_O%5B{2;N({cKzzL&og5-bWMi@KSj2lA|+p; z#~a?k1am`{lOPB){8lF`-SrwtC;YDEc^@j+t4kaZyC0X?VKO4w?VVyR_wYg1%9jqs zbwq7GhN1F%JyA7S$z9=7Ie|qLS!gmBaWp%o-doz5*!wr1XQ2+lP zwA#5r9)`Cz@20MPwUVjlbeQm8Mx1jk?-})rp@J6TgU(KU#lG35%#Jb{z zW^cAc_Ug@-Aov}Y_$;ga)+nTQ0UKjoa{$KT%?fi6_+r44;DreuGSML6wJD&Y4v?1| zaUD(tlW1YMO7SUTQG|%I7$%s0s6l$Z`8b&}G+H{?t5)FiaU;C);NFeg>RQ&&G8-FD zHViGhd3$5i2tL@%=GHSt2s|^7HVm$o1&#x z>D`@Y(A_SVu3x#8uI|)mYCm6inSV~J>HVGkbnX=`k#YsJRS2!6_nsB1)x9hGZ;bHQ zdu4L4OTU?6R#oq?JMW;XF3u?n%EI4jQ0?ne?CY;!Se;x@mc{jlIv~*(6M$3Zl)3k6 zSf(+V|EQWgY9Ro#@J@}$RI~%+0d+{CE+Mtv%EdUdA)$=nmH1PQO1Q-dMxRzD7(K2W fMW0bda2_xhuy5L~XY3mE!Z_4GW7qHMtXs>!9V@{7 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f176e11d3cd45493b9805cebabc051597da39a67 GIT binary patch literal 3052 zcmZ`*&2JmW6`$D;a!FCNB&&85Ck~4yZHPu@(GLVJ+$xDg%L-)5Fy$g3S}ayOBWlUj zE;YNfBb7Q?K#uJ-JxNDjf}Z-v%&A3AzO+5JDBRzhl`JWaW-)K)dw%oYo8OyYdb-Mx zz8?NJSh&I1w^X^998^9*&VC6YnBXznWq?pH%E(}>&m^c zOhUOH4P_kdq^St8)}=%MDP>y_lJ&S?JxlPOEv%j+?4Bze0J;7Z+vcJyyrW95Y|0f; zJ@R^Nw84zS#x9Sbnyj%S$JE z`3Vq27{~hCCjE1%|Cg^Ry;!>63D1CH5RGB>Y0f@JA!j+Cu;7IA>1jWcdw!UX6IduiU!LYcOpWK25Ej%`e_PQ=D!WQ% zGH*KC$z;5(-C{%U^?p+qwtK;W#1FA$=KC+$fBw{Zl3}M77D|GCnuwsal7{15ndE3p zWos{3#BWi4iL*sT^9WV|V&EUhFmDyrMVUNp#nBd3chck+ckZ^bD3^<)ARGpLnYBjI zsO3MEiAWXghU4a~Y>P?&U+Ch_XkS&JzMmjx^AL>JdC9`Z&%cAl6~1uiOy?mv|5>8) zYqS3vmCI%q#_YoC^V~UhPuP56baI2TAn6$iqr=R0=jAJ`@RHVUR+WdJF}Iqd(liMd zLeKSO?kD42KP>K8Br{c~-fJXgNX$aK;OaV+Z<1Ib@iqxs>{rNH4FVd#>1XKt*1@}1 zo!mIU8^^XVeVG<|2V%koOw~yac$aFpunRbpIhfVawNpzN-)!27HdpVFAhJ@oNf?ah zK&f{LIU|>6fazHW?_UM+;_`1GB?B3xw9T7Hk8QzEfcgxp7Y9jy46kZB1c1B(4Y`ke5$u(obhds-Dm+W%nRHSEbO661prpiX9%R5LfOrU*-6rK46@#u9*rAo% z6aJj9us{6m_tv3(=zwG#w7U~GcLwf|sV^pAr$I^BA2SP-GjXv(8SQ^J?cQm-ht|ra zp6Y22$Y?N?{q?LLKJ4CPhafTF!%h<$yOomL#k%C-gUXxzXsu03E?YoA`50H`9#(<> zSN48p)^V{$86_*99@K}{pScpK(azU~PEk9aIbnz1gmBK9wtFbe9bXrvNqOR(*qmW5 zbmjjb`Ou8IF=VIf(5Nzv!i*ayjv0BAMxqW~HocC%K9q9gON4uk8Rv-9}+w1=3gU9XegN^kST_;95Ke)d2XrsHfx%NA3 z@L=Qd=JJ!x3xi8>+V4ZTu8>>fzV_~KJn5|XYnzB=1@v2;_Ufls&hBh%`VZU9iXk`c zZ6R7D&|usqT{)UJtkiYVULMOV+UeTQYVvn{1~h><2}GhW5#fDfyD#`a28Lm-Q;Alw^ zSND;T{=KAck4~L1;J`JA#=3h(AauHKZbTP5VJF{%T+Z1PvM>Va& z9$bA#!RjLnnILC)iz41QMxV|ri+j-83~7%a{P-$eUG}S{b-&ZRMz0_9?lGoif* Z_vdl&buiT4z2cz1eV2Bn2{k@%{~!4G>o@=a literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73b54dc732b07a1add21fd47fc77cd67af1565e6 GIT binary patch literal 1662 zcmaJ>%WvaE7@x5n=jA+`b_)X0YXoiBT~ZbaA=Dzu3U-B*BBZ%kjy;=rJ9e@&PT8XD zDJ$)fGZKeZ;CJE0?{M z1S8UK{7xaoKGoghX(UIyIFYl({98F2a|zZvqcoogesodrwSVpMb?^H$zk6?Z_xM7l zLZtDdS;B>`eLYJbNGorB6BTJb+ze0d!6B?8LDcXJ{%q1`|xxcrFJ7I-^LEV;$z8wxV8pX zy=h0+?sK}3XQ`T+s2JL}vD~$$-T{FVMiauESL6vzXyUxAF!vSl2=k!#u5OKvjz_mT z9^){s7m^6bMeu{_SAc>PbV*jurE^6nDcr(aQNWTx-(cBb#bDK7&0yVNV6b7ZX|T1T zlVC~0cF|dp7x=YqLYF{~7mkq~BfCp_xwUkb?v+Eyl00o+_Lda&iF*a4UXsgg%qSsP z)Z;Ht`#ZX%7IQ9T7t+?1Fii8b2t$dbNUTei5PXC(LIt6UfIpV3BLoQ80NF(70O<1E zFjL7knp$Y;C|-mak}q64wa=dyJWq5`22+yIKnZ(^>!i#|# z$7+KAsV(?mej$6%iVxsc9{~{BbbR`ss|1!m{H{+u>iz9Ogaa7R8a#dX$K5x$cyK+n zehDX-M;Q-8T??_-3xRSFhR>i=)?+fzjdhb^E(FZ`J;=qvyD04;ASK?1TR8v~YV^nb zw{;h*gK^y|Ok#*`LAlmt%QY<09TDY|czyPm@&7-ubQ52g;6Y-eZ-}P5=ow$19oBn8 zKHWfX0Y`;(AQyEDZI(9HYM2;U%Q{PBsBW8=?Qvlvk;bOxTg@?T+QazDrw|3*{S5o1 SuvGyK=$_lAZJ3m~+Fl+W&KOq_5Uk(hQVMc9ap*zB4bHX|#xyS9d{$cancl7{WbjmzD#hMmZZ=el!o zrCXuo5#c57UJ>pJdTDm6yxb;r?}h@KI#nxs!zd4bJkQ1Uu`k;qeT2&^*HKPEey7QTnUG@wBLAsQauJMW-QBJvW<#tUCo7@7}5;nKFb44y` z*8y0o4RDEO?F zBsX*G686l_ol631DR=c+9+9z!H#ljNH?&osQ+75S2r0`zM7fNGNtiPxk+5{|tQ=@V zvn-Yv1ZtVjn3wrz-wSweBW@um%=_Q5vZcsG{)Q?m6VVZ@WyXTY&oai|khi}#o@YX4 zjo=vQ^wNa;jb<7QW0B-=n2N@!zX{8xI0F&G;&=eTkmYP3)4mAuMzP%#$%{r59^&>m zO}^gxu91bg*c|x5iQf}hV-OA+?1e~pDv_JyB5!2Hoq z3%?5;HiZkersnyuFFpYZcYQG;eY5Zi+FT;_x+Uje(KeS02+iYk52q!ZmT_9a39(L!BIWVppE#*G(2Yv#PP-vzVy~|L!k)QO2;46&gDc4!sPw|142&IA# zqpk!)tJ?y_we9I8{F>yKw=WG)-x!-CIx^PDHGOH0AT#>*i6Nf>#u=A5WJ%wJQ63rB z7G#dspM!jX!t|P+x~CO=zY6#JfT_KQnFmZo^H{hhFRhWm4LJF01lhliroYF{2e>Yd zm-NmWS!)3@{uxOkw|0}&Xt!y`DGcFZHe)z?45&y{R8Dh zX|E@wvbi`M_9VuiT*nFBtDM$;hwbesr+BHH-A)_#?oa#ATTQmxQFgoY@SvlXdJxsY zP)aBv3lZ zNmBF<4xKWe0syWiV+!GmsbFZx$aTtG8q>OA6&0!h*9)_mv42)K3#CC%-BcN@N2)UE z4|?~1U6H`gFfwc{1a$J~#rll2cP$Ow2|5ziD8|5vEKd0_Lb$@%>Cle~jIP|eP%0Oq z6gowIJ`+D^UWt5Fb<%nQ5k=7AZrz}3t}eIO1wV(8Vcm6UrBtyD>X|s)&vd~Xt>Y;!{v*+C6uKfdoSCj!G*YKO2$KA)=$sCa%%H{UmTQY-7-u^2 fb&eE`;#OV_p}fj{P}t;q>^m8@FbIOlZ^8T*V98_F literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c208b933adec6936855e94d36a5cfb0e7030095 GIT binary patch literal 5866 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91aa7647eb6be1939560163912ac428ad0d9948e GIT binary patch literal 474 zcmYjOJx{|h5KU-GQBYUr8|ly(sVr0oRj3RsMWVpaC353yW185;c1qhnz;9rKk(qy! zm4Cnp3r;^IPP%t`=acT;Gi^3Q&&BKPmwvnYRgwQubEN0e964a#$YcJ z8mkSxR;u*iL~d#)>90Iz(!jm?L%Ll-siLaCJMpz8QRY zdf(ZwhMyO5Z`=jM?Gbb;1*4=dlOji9T~i`?qHR~&xKOD1rmdD1jg6I-kC<4pp=}g% xlJRjxQz<&p88O^qdv_dBToETqFsZ9;l|^K$(HT=7oNs);@T-3c90ZNP{{z;hnSKBO literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py new file mode 100644 index 0000000..38f3251 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py @@ -0,0 +1,386 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +#Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 + +BIG5_CHAR_TO_FREQ_ORDER = ( + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 +) + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py new file mode 100644 index 0000000..98f9970 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import Big5DistributionAnalysis +from .mbcssm import BIG5_SM_MODEL + + +class Big5Prober(MultiByteCharSetProber): + def __init__(self): + super(Big5Prober, self).__init__() + self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) + self.distribution_analyzer = Big5DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "Big5" + + @property + def language(self): + return "Chinese" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py new file mode 100644 index 0000000..c0395f4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .euctwfreq import (EUCTW_CHAR_TO_FREQ_ORDER, EUCTW_TABLE_SIZE, + EUCTW_TYPICAL_DISTRIBUTION_RATIO) +from .euckrfreq import (EUCKR_CHAR_TO_FREQ_ORDER, EUCKR_TABLE_SIZE, + EUCKR_TYPICAL_DISTRIBUTION_RATIO) +from .gb2312freq import (GB2312_CHAR_TO_FREQ_ORDER, GB2312_TABLE_SIZE, + GB2312_TYPICAL_DISTRIBUTION_RATIO) +from .big5freq import (BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, + BIG5_TYPICAL_DISTRIBUTION_RATIO) +from .jisfreq import (JIS_CHAR_TO_FREQ_ORDER, JIS_TABLE_SIZE, + JIS_TYPICAL_DISTRIBUTION_RATIO) + + +class CharDistributionAnalysis(object): + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + MINIMUM_DATA_THRESHOLD = 3 + + def __init__(self): + # Mapping table to get frequency order from char order (get from + # GetOrder()) + self._char_to_freq_order = None + self._table_size = None # Size of above table + # This is a constant value which varies from language to language, + # used in calculating confidence. See + # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html + # for further detail. + self.typical_distribution_ratio = None + self._done = None + self._total_chars = None + self._freq_chars = None + self.reset() + + def reset(self): + """reset analyser, clear any state""" + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + self._total_chars = 0 # Total characters encountered + # The number of characters whose frequency order is less than 512 + self._freq_chars = 0 + + def feed(self, char, char_len): + """feed a character with known length""" + if char_len == 2: + # we only care about 2-bytes character in our distribution analysis + order = self.get_order(char) + else: + order = -1 + if order >= 0: + self._total_chars += 1 + # order is valid + if order < self._table_size: + if 512 > self._char_to_freq_order[order]: + self._freq_chars += 1 + + def get_confidence(self): + """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, + # return negative answer + if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: + return self.SURE_NO + + if self._total_chars != self._freq_chars: + r = (self._freq_chars / ((self._total_chars - self._freq_chars) + * self.typical_distribution_ratio)) + if r < self.SURE_YES: + return r + + # normalize confidence (we don't want to be 100% sure) + return self.SURE_YES + + def got_enough_data(self): + # It is not necessary to receive all data to draw conclusion. + # For charset detection, certain amount of data is enough + return self._total_chars > self.ENOUGH_DATA_THRESHOLD + + def get_order(self, byte_str): + # We do not handle characters based on the original encoding string, + # but convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency + # table. + return -1 + + +class EUCTWDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCTWDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER + self._table_size = EUCTW_TABLE_SIZE + self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xC4: + return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 + else: + return -1 + + +class EUCKRDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCKRDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xB0: + return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 + else: + return -1 + + +class GB2312DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(GB2312DistributionAnalysis, self).__init__() + self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER + self._table_size = GB2312_TABLE_SIZE + self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0xB0) and (second_char >= 0xA1): + return 94 * (first_char - 0xB0) + second_char - 0xA1 + else: + return -1 + + +class Big5DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(Big5DistributionAnalysis, self).__init__() + self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER + self._table_size = BIG5_TABLE_SIZE + self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if first_char >= 0xA4: + if second_char >= 0xA1: + return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 + else: + return 157 * (first_char - 0xA4) + second_char - 0x40 + else: + return -1 + + +class SJISDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(SJISDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0x81) and (first_char <= 0x9F): + order = 188 * (first_char - 0x81) + elif (first_char >= 0xE0) and (first_char <= 0xEF): + order = 188 * (first_char - 0xE0 + 31) + else: + return -1 + order = order + second_char - 0x40 + if second_char > 0x7F: + order = -1 + return order + + +class EUCJPDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCJPDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + char = byte_str[0] + if char >= 0xA0: + return 94 * (char - 0xA1) + byte_str[1] - 0xa1 + else: + return -1 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py new file mode 100644 index 0000000..8b3738e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -0,0 +1,106 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import ProbingState +from .charsetprober import CharSetProber + + +class CharSetGroupProber(CharSetProber): + def __init__(self, lang_filter=None): + super(CharSetGroupProber, self).__init__(lang_filter=lang_filter) + self._active_num = 0 + self.probers = [] + self._best_guess_prober = None + + def reset(self): + super(CharSetGroupProber, self).reset() + self._active_num = 0 + for prober in self.probers: + if prober: + prober.reset() + prober.active = True + self._active_num += 1 + self._best_guess_prober = None + + @property + def charset_name(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.charset_name + + @property + def language(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.language + + def feed(self, byte_str): + for prober in self.probers: + if not prober: + continue + if not prober.active: + continue + state = prober.feed(byte_str) + if not state: + continue + if state == ProbingState.FOUND_IT: + self._best_guess_prober = prober + return self.state + elif state == ProbingState.NOT_ME: + prober.active = False + self._active_num -= 1 + if self._active_num <= 0: + self._state = ProbingState.NOT_ME + return self.state + return self.state + + def get_confidence(self): + state = self.state + if state == ProbingState.FOUND_IT: + return 0.99 + elif state == ProbingState.NOT_ME: + return 0.01 + best_conf = 0.0 + self._best_guess_prober = None + for prober in self.probers: + if not prober: + continue + if not prober.active: + self.logger.debug('%s not active', prober.charset_name) + continue + conf = prober.get_confidence() + self.logger.debug('%s %s confidence = %s', prober.charset_name, prober.language, conf) + if best_conf < conf: + best_conf = conf + self._best_guess_prober = prober + if not self._best_guess_prober: + return 0.0 + return best_conf diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py new file mode 100644 index 0000000..eac4e59 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging +import re + +from .enums import ProbingState + + +class CharSetProber(object): + + SHORTCUT_THRESHOLD = 0.95 + + def __init__(self, lang_filter=None): + self._state = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + + def reset(self): + self._state = ProbingState.DETECTING + + @property + def charset_name(self): + return None + + def feed(self, buf): + pass + + @property + def state(self): + return self._state + + def get_confidence(self): + return 0.0 + + @staticmethod + def filter_high_byte_only(buf): + buf = re.sub(b'([\x00-\x7F])+', b' ', buf) + return buf + + @staticmethod + def filter_international_words(buf): + """ + We define three types of bytes: + alphabet: english alphabets [a-zA-Z] + international: international characters [\x80-\xFF] + marker: everything else [^a-zA-Z\x80-\xFF] + + The input buffer can be thought to contain a series of words delimited + by markers. This function works to filter all words that contain at + least one international character. All contiguous sequences of markers + are replaced by a single space ascii character. + + This filter applies to all scripts which do not use English characters. + """ + filtered = bytearray() + + # This regex expression filters out only words that have at-least one + # international character. The word may include one marker character at + # the end. + words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', + buf) + + for word in words: + filtered.extend(word[:-1]) + + # If the last character in the word is a marker, replace it with a + # space as markers shouldn't affect our analysis (they are used + # similarly across all languages and may thus have similar + # frequencies). + last_char = word[-1:] + if not last_char.isalpha() and last_char < b'\x80': + last_char = b' ' + filtered.extend(last_char) + + return filtered + + @staticmethod + def filter_with_english_letters(buf): + """ + Returns a copy of ``buf`` that retains only the sequences of English + alphabet and high byte characters that are not between <> characters. + Also retains English alphabet and high byte characters immediately + before occurrences of >. + + This filter can be applied to all scripts which contain both English + characters and extended ASCII characters, but is currently only used by + ``Latin1Prober``. + """ + filtered = bytearray() + in_tag = False + prev = 0 + + for curr in range(len(buf)): + # Slice here to get bytes instead of an int with Python 3 + buf_char = buf[curr:curr + 1] + # Check if we're coming out of or entering an HTML tag + if buf_char == b'>': + in_tag = False + elif buf_char == b'<': + in_tag = True + + # If current character is not extended-ASCII and not alphabetic... + if buf_char < b'\x80' and not buf_char.isalpha(): + # ...and we're not in a tag + if curr > prev and not in_tag: + # Keep everything after last non-extended-ASCII, + # non-alphabetic character + filtered.extend(buf[prev:curr]) + # Output a space to delimit stretch we kept + filtered.extend(b' ') + prev = curr + 1 + + # If we're not in a tag... + if not in_tag: + # Keep everything after last non-extended-ASCII, non-alphabetic + # character + filtered.extend(buf[prev:]) + + return filtered diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py @@ -0,0 +1 @@ + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..518d15fb1ac7a9782447a7b02076ec43fb148212 GIT binary patch literal 231 zcmYk0F$%&k7==@C5TOTgNdLh}5OHvJ5$6z-pEkBlVv-hn2QT0eyppR&aMQ(v4*KA| z$NQH5=XpLOQMdZTv)Iox{*~Y++D~FgvM2LwcPB34U%any@aQ~&yv7fN@gPJv8c=W@YrESb|ppspX>PC3>*R0_#yrpEE-EvQsMw6f*^sFAO?(uV9N%~#2Gc&O>%bU zrS9ggm*So_$0+yQmdGi8D2KlKl)sR~P`;WW*A(P~hhR@Xs;j%IzWQqT!9nEU`TOaA z`M=Qnh6cNjkHK&7*9whrMvicWm%5oZ@-lzqXTd0NY0gg@qek@(MX092}1yiytlpzsj<}y)CNFygkib&>@LL~W2 zN73)T=2@A_;V_CAp1W)^=c*QDy2xaulR5=-C>m9A+od5pPjXS5>)wNhA3Vgs%-N*K zr-{HBlHKora3g-BV92(o`sZ_-ol!+sjn2pH^Y)bNSVc1j^ z8Pf))^rPtC2#wSA?JYjmMY=FDPBL|%Drxy*7e>Rh+2D> z?kW)8ZAYcTzm8U$5$1y1&KlSd4VqoM=!HN^bNOxM@lT~$sGM=Q2iDIJdkyf6sf$38 zTgQA1xNM=oiYbJvXcwx|kS5yDY^^}U!vv6F$raC7n&eUs*%PR$y37=8CJ`Y_?5OP5 zd(1_-ksGp)$>(%3RF5x=)VpePp2;CQMoY_^+W=5$3g99(G+Ujwqqp9JTUK*6OlCQf z!M3*7SibxG{^gK8e{lI4;+{Xew9$8PU3Mx-;7>`tNX>C~Uo{a|nU2?iC>b<6BQa{q--v&ac& z?IH)glYM02`^Y~3y1jPPnK=+`=;(-p7oO{w19NMIwUfgu?+Tgu>`$UYe7pRnLen3> zqX-`mm}Nw%6duOatoDD!vIwyT?1@d0DqMkIUriN=pAFe3n{2w-d;}I`3DT>SlVqAm zvEk|MSv@+d&L7IVn+F7W4DcPXr%wT^K575sgW*?CpRz==g@&Fa2iiDpL3@T(?$vkL z$>?fr`NfkTT0@Z6I|bzCGOu*D_|`zhjj($T2GCw`+hgD!Ca=L4ajoa$YHy-&5iJKuh-k~ylrke z%l6%S$dy@FGvt5Qvmvj@M*R%1T>j(BVu6rLrWScl^tn?YQC2`^5I&RXMV~ze5oq?& zlTSbCJ!0lOK>_47C04*7^5Sfq^8B<060Fr=E1qs>6A+lPi(;Yb^44dFHHPe*=Z29@ zM|6qZ3-o$49l_xN@yv1e4cmgf;Arqt=eHZ6KBl3vZ7xKN)e5|}MbUTy7gfBa-h{2H zg~o>C#dIo_dW&YLwArZAM_aY}X(f_qZp?*kRF&0+D2domCAoSV0u%#CpV_e5sj88- z?zG~xx*_JULB^B(m*{EowX+wv-pkMrUC)c$H~$wlz$h<6FT$wh27d5u7^q*+zE&L5 zPAb*arMvEN_s}-uSQL{uwo!~~kNPL$SW%%?J~c!+>OE@6*wK$bc>N9G^*2GS>>a)H zJu7`s06wj1lE3?X0{E(^KNae)r$SY=qjt*n(!}$)6EXWvQ@Nt`tE>= self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py new file mode 100644 index 0000000..b68078c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py @@ -0,0 +1,195 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio + +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +EUCKR_CHAR_TO_FREQ_ORDER = ( + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +) + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py new file mode 100644 index 0000000..345a060 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCKRDistributionAnalysis +from .mbcssm import EUCKR_SM_MODEL + + +class EUCKRProber(MultiByteCharSetProber): + def __init__(self): + super(EUCKRProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-KR" + + @property + def language(self): + return "Korean" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py new file mode 100644 index 0000000..ed7a995 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py @@ -0,0 +1,387 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table , +EUCTW_TABLE_SIZE = 5376 + +EUCTW_CHAR_TO_FREQ_ORDER = ( + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +) + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py new file mode 100644 index 0000000..35669cc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCTWDistributionAnalysis +from .mbcssm import EUCTW_SM_MODEL + +class EUCTWProber(MultiByteCharSetProber): + def __init__(self): + super(EUCTWProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) + self.distribution_analyzer = EUCTWDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-TW" + + @property + def language(self): + return "Taiwan" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py new file mode 100644 index 0000000..697837b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py @@ -0,0 +1,283 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +GB2312_CHAR_TO_FREQ_ORDER = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py new file mode 100644 index 0000000..8446d2d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import GB2312DistributionAnalysis +from .mbcssm import GB2312_SM_MODEL + +class GB2312Prober(MultiByteCharSetProber): + def __init__(self): + super(GB2312Prober, self).__init__() + self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) + self.distribution_analyzer = GB2312DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "GB2312" + + @property + def language(self): + return "Chinese" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py new file mode 100644 index 0000000..b0e1bf4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -0,0 +1,292 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + +class HebrewProber(CharSetProber): + # windows-1255 / ISO-8859-8 code points of interest + FINAL_KAF = 0xea + NORMAL_KAF = 0xeb + FINAL_MEM = 0xed + NORMAL_MEM = 0xee + FINAL_NUN = 0xef + NORMAL_NUN = 0xf0 + FINAL_PE = 0xf3 + NORMAL_PE = 0xf4 + FINAL_TSADI = 0xf5 + NORMAL_TSADI = 0xf6 + + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score + # distance. + MIN_FINAL_CHAR_DISTANCE = 5 + + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score + # distance. + MIN_MODEL_DISTANCE = 0.01 + + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" + + def __init__(self): + super(HebrewProber, self).__init__() + self._final_char_logical_score = None + self._final_char_visual_score = None + self._prev = None + self._before_prev = None + self._logical_prober = None + self._visual_prober = None + self.reset() + + def reset(self): + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate + # a word delimiter at the beginning of the data + self._prev = ' ' + self._before_prev = ' ' + # These probers are owned by the group prober. + + def set_model_probers(self, logicalProber, visualProber): + self._logical_prober = logicalProber + self._visual_prober = visualProber + + def is_final(self, c): + return c in [self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, + self.FINAL_PE, self.FINAL_TSADI] + + def is_non_final(self, c): + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters + # causing the Non-Final tsadi to appear at an end of a word even + # though this is not the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being + # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' + # for example legally end with a Non-Final Pe or Kaf. However, the + # benefit of these letters as Non-Final letters outweighs the damage + # since these words are quite rare. + return c in [self.NORMAL_KAF, self.NORMAL_MEM, + self.NORMAL_NUN, self.NORMAL_PE] + + def feed(self, byte_str): + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew + # or visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is + # an indication that the text is laid out "naturally" since the + # final letter really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In + # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, + # should not end with the Non-Final form of that letter. Exceptions + # to this rule are mentioned above in isNonFinal(). This is an + # indication that the text is laid out backwards. +1 for visual + # score + # 3) A word longer than 1 letter, starting with a final letter. Final + # letters should not appear at the beginning of a word. This is an + # indication that the text is laid out backwards. +1 for visual + # score. + # + # The visual score and logical score are accumulated throughout the + # text and are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since + # that case is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with + # spaces) so the word boundary detection works properly. [MAP] + + if self.state == ProbingState.NOT_ME: + # Both model probers say it's not them. No reason to continue. + return ProbingState.NOT_ME + + byte_str = self.filter_high_byte_only(byte_str) + + for cur in byte_str: + if cur == ' ': + # We stand on a space - a word just ended + if self._before_prev != ' ': + # next-to-last char was not a space so self._prev is not a + # 1 letter word + if self.is_final(self._prev): + # case (1) [-2:not space][-1:final letter][cur:space] + self._final_char_logical_score += 1 + elif self.is_non_final(self._prev): + # case (2) [-2:not space][-1:Non-Final letter][ + # cur:space] + self._final_char_visual_score += 1 + else: + # Not standing on a space + if ((self._before_prev == ' ') and + (self.is_final(self._prev)) and (cur != ' ')): + # case (3) [-2:space][-1:final letter][cur:not space] + self._final_char_visual_score += 1 + self._before_prev = self._prev + self._prev = cur + + # Forever detecting, till the end or until both model probers return + # ProbingState.NOT_ME (handled above) + return ProbingState.DETECTING + + @property + def charset_name(self): + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = self._final_char_logical_score - self._final_char_visual_score + if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = (self._logical_prober.get_confidence() + - self._visual_prober.get_confidence()) + if modelsub > self.MIN_MODEL_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if modelsub < -self.MIN_MODEL_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # Still no good, back to final letter distance, maybe it'll save the + # day. + if finalsub < 0.0: + return self.VISUAL_HEBREW_NAME + + # (finalsub > 0 - Logical) or (don't know what to do) default to + # Logical. + return self.LOGICAL_HEBREW_NAME + + @property + def language(self): + return 'Hebrew' + + @property + def state(self): + # Remain active as long as any of the model probers are active. + if (self._logical_prober.state == ProbingState.NOT_ME) and \ + (self._visual_prober.state == ProbingState.NOT_ME): + return ProbingState.NOT_ME + return ProbingState.DETECTING diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py new file mode 100644 index 0000000..83fc082 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py @@ -0,0 +1,325 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +JIS_CHAR_TO_FREQ_ORDER = ( + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +) + + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py new file mode 100644 index 0000000..20044e4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +# This is hiragana 2-char sequence table, the number in each cell represents its frequency category +jp2CharContext = ( +(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1), +(2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4), +(0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2), +(0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4), +(1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4), +(0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3), +(0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3), +(0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3), +(0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4), +(0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3), +(2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4), +(0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3), +(0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5), +(0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3), +(2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5), +(0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4), +(1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4), +(0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3), +(0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3), +(0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3), +(0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5), +(0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4), +(0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5), +(0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3), +(0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4), +(0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4), +(0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4), +(0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1), +(0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), +(1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3), +(0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0), +(0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3), +(0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3), +(0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5), +(0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4), +(2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5), +(0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3), +(0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3), +(0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3), +(0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3), +(0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4), +(0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4), +(0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2), +(0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3), +(0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3), +(0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3), +(0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3), +(0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4), +(0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3), +(0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4), +(0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3), +(0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3), +(0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4), +(0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4), +(0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3), +(2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4), +(0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4), +(0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3), +(0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4), +(0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4), +(1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4), +(0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3), +(0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2), +(0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2), +(0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3), +(0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3), +(0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5), +(0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3), +(0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4), +(1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4), +(0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1), +(0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2), +(0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3), +(0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), +) + +class JapaneseContextAnalysis(object): + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + + def __init__(self): + self._total_rel = None + self._rel_sample = None + self._need_to_skip_char_num = None + self._last_char_order = None + self._done = None + self.reset() + + def reset(self): + self._total_rel = 0 # total sequence received + # category counters, each integer counts sequence in its category + self._rel_sample = [0] * self.NUM_OF_CATEGORY + # if last byte in current buffer is not the last byte of a character, + # we need to know how many bytes to skip in next buffer + self._need_to_skip_char_num = 0 + self._last_char_order = -1 # The order of previous char + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + + def feed(self, byte_str, num_bytes): + if self._done: + return + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not + # complete, we record how many byte needed to complete that character + # and skip these bytes here. We can choose to record those bytes as + # well and analyse the character once it is complete, but since a + # character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = self._need_to_skip_char_num + while i < num_bytes: + order, char_len = self.get_order(byte_str[i:i + 2]) + i += char_len + if i > num_bytes: + self._need_to_skip_char_num = i - num_bytes + self._last_char_order = -1 + else: + if (order != -1) and (self._last_char_order != -1): + self._total_rel += 1 + if self._total_rel > self.MAX_REL_THRESHOLD: + self._done = True + break + self._rel_sample[jp2CharContext[self._last_char_order][order]] += 1 + self._last_char_order = order + + def got_enough_data(self): + return self._total_rel > self.ENOUGH_REL_THRESHOLD + + def get_confidence(self): + # This is just one way to calculate confidence. It works well for me. + if self._total_rel > self.MINIMUM_DATA_THRESHOLD: + return (self._total_rel - self._rel_sample[0]) / self._total_rel + else: + return self.DONT_KNOW + + def get_order(self, byte_str): + return -1, 1 + +class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self): + super(SJISContextAnalysis, self).__init__() + self._charset_name = "SHIFT_JIS" + + @property + def charset_name(self): + return self._charset_name + + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): + char_len = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self._charset_name = "CP932" + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 202) and (0x9F <= second_char <= 0xF1): + return second_char - 0x9F, char_len + + return -1, char_len + +class EUCJPContextAnalysis(JapaneseContextAnalysis): + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): + char_len = 2 + elif first_char == 0x8F: + char_len = 3 + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): + return second_char - 0xA1, char_len + + return -1, char_len + + diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..2aa4fb2 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -0,0 +1,228 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +# this table is modified base on win1251BulgarianCharToOrderMap, so +# only number <64 is sure valid + +Latin5_BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 +210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 + 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 + 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 +) + +win1251BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 +221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 + 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 + 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 96.9392% +# first 1024 sequences:3.0618% +# rest sequences: 0.2992% +# negative sequences: 0.0020% +BulgarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, +3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, +0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, +0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, +0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, +0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, +0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, +2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, +3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, +1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, +3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, +1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, +2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, +2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, +3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, +1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, +2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, +2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, +1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, +2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, +2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, +2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, +1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, +2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, +1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, +3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, +1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, +3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, +1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, +2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, +1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, +2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, +1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, +2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, +1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, +2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, +1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, +0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, +1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, +1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, +1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, +0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, +1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, +1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +) + +Latin5BulgarianModel = { + 'char_to_order_map': Latin5_BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Bulgairan', +} + +Win1251BulgarianModel = { + 'char_to_order_map': win1251BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Bulgarian', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langcyrillicmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langcyrillicmodel.py new file mode 100644 index 0000000..e5f9a1f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langcyrillicmodel.py @@ -0,0 +1,333 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# KOI8-R language model +# Character Mapping Table: +KOI8R_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 +223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 +238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 + 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 + 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 + 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 + 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 +) + +win1251_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +) + +latin5_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +macCyrillic_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, +) + +IBM855_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, +206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, + 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, +220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, +230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, + 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, + 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, +250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, +) + +IBM866_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 97.6601% +# first 1024 sequences: 2.3389% +# rest sequences: 0.1237% +# negative sequences: 0.0009% +RussianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, +1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, +1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, +2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, +1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, +3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, +1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, +2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, +1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, +1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, +1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, +1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, +3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, +1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, +2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, +1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, +2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, +1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, +1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, +1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, +3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, +3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, +1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, +1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, +0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, +1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, +1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, +0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, +1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, +1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, +1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, +2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, +1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, +1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, +0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, +0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, +2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, +0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +) + +Koi8rModel = { + 'char_to_order_map': KOI8R_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "KOI8-R", + 'language': 'Russian', +} + +Win1251CyrillicModel = { + 'char_to_order_map': win1251_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Russian', +} + +Latin5CyrillicModel = { + 'char_to_order_map': latin5_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Russian', +} + +MacCyrillicModel = { + 'char_to_order_map': macCyrillic_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "MacCyrillic", + 'language': 'Russian', +} + +Ibm866Model = { + 'char_to_order_map': IBM866_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM866", + 'language': 'Russian', +} + +Ibm855Model = { + 'char_to_order_map': IBM855_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM855", + 'language': 'Russian', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py new file mode 100644 index 0000000..5332221 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin7_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +win1253_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.2851% +# first 1024 sequences:1.7001% +# rest sequences: 0.0359% +# negative sequences: 0.0148% +GreekLangModel = ( +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, +2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, +2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, +2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, +0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, +3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, +2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, +0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, +0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, +0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, +0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, +0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, +0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, +0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, +0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, +0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, +0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, +0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, +0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, +0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, +0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, +0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, +0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, +0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, +0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin7GreekModel = { + 'char_to_order_map': Latin7_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-7", + 'language': 'Greek', +} + +Win1253GreekModel = { + 'char_to_order_map': win1253_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "windows-1253", + 'language': 'Greek', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py new file mode 100644 index 0000000..58f4c87 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -0,0 +1,200 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Simon Montagu +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Shoshannah Forbes - original C code (?) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Windows-1255 language model +# Character Mapping Table: +WIN1255_CHAR_TO_ORDER_MAP = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 + 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 +253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 + 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 +124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, +215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, + 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, +106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, + 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, +238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, + 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, + 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.4004% +# first 1024 sequences: 1.5981% +# rest sequences: 0.087% +# negative sequences: 0.0015% +HEBREW_LANG_MODEL = ( +0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, +3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, +1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, +1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, +1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, +0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, +0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, +0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, +0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, +0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, +0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, +0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, +0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, +0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, +0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, +0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, +0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, +0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, +1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, +1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, +2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, +0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, +) + +Win1255HebrewModel = { + 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, + 'precedence_matrix': HEBREW_LANG_MODEL, + 'typical_positive_ratio': 0.984004, + 'keep_english_letter': False, + 'charset_name': "windows-1255", + 'language': 'Hebrew', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py new file mode 100644 index 0000000..bb7c095 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin2_HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, +175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, + 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, + 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, +245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +win1250HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, + 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, + 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, +245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 94.7368% +# first 1024 sequences:5.2623% +# rest sequences: 0.8894% +# negative sequences: 0.0009% +HungarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, +3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, +0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, +1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, +1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, +3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, +2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, +2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, +2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, +2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, +1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, +1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, +3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, +1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, +1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, +2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, +2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, +2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, +3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, +1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, +1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, +1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, +2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, +1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, +2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, +2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, +1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, +1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, +0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, +2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, +2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, +1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, +1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, +2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, +2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, +2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, +1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, +0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +) + +Latin2HungarianModel = { + 'char_to_order_map': Latin2_HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-2", + 'language': 'Hungarian', +} + +Win1250HungarianModel = { + 'char_to_order_map': win1250HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "windows-1250", + 'language': 'Hungarian', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py new file mode 100644 index 0000000..15f94c2 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -0,0 +1,199 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# The following result for thai was collected from a limited sample (1M). + +# Character Mapping Table: +TIS620CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 +188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 +253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 + 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 +209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, +223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, +236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, + 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, + 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, + 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, + 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, + 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 92.6386% +# first 1024 sequences:7.3177% +# rest sequences: 1.0230% +# negative sequences: 0.0436% +ThaiLangModel = ( +0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, +0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, +3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, +0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, +3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, +3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, +3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, +3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, +2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, +3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, +1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, +3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, +1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, +0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, +0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, +2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, +0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, +3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, +2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, +2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, +3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, +3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, +3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, +3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, +1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, +0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, +0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, +3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, +3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, +1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, +3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, +3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, +0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, +0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, +1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, +1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, +3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, +0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, +3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, +0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, +0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, +0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, +0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, +0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, +0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, +0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, +0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, +3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, +2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, +0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, +3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, +1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, +1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +TIS620ThaiModel = { + 'char_to_order_map': TIS620CharToOrderMap, + 'precedence_matrix': ThaiLangModel, + 'typical_positive_ratio': 0.926386, + 'keep_english_letter': False, + 'charset_name': "TIS-620", + 'language': 'Thai', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py new file mode 100644 index 0000000..a427a45 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Özgür Baskın - Turkish Language Model +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin5_TurkishCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, + 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, +255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, + 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, +180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, +164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, +150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, + 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, +124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, + 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, + 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, + 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, +) + +TurkishLangModel = ( +3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, +3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, +3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, +3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, +3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, +3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, +2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, +3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, +1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, +3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, +3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, +2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, +2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, +3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, +0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, +3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, +3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, +0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, +1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, +3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, +1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, +3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, +0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, +3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, +1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, +1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, +2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, +2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, +3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, +1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, +0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, +3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, +0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, +3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, +1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, +2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, +0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, +3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, +0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, +0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, +3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, +0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, +0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, +3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, +0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, +3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, +0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, +0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, +3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, +0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, +3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, +0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, +0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, +0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, +0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, +0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, +0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, +1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, +0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, +0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, +3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, +0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, +2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, +2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, +0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, +0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin5TurkishModel = { + 'char_to_order_map': Latin5_TurkishCharToOrderMap, + 'precedence_matrix': TurkishLangModel, + 'typical_positive_ratio': 0.970290, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-9", + 'language': 'Turkish', +} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py new file mode 100644 index 0000000..7d1e8c2 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +CLASS_NUM = 8 # total classes + +Latin1_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +Latin1ClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO +) + + +class Latin1Prober(CharSetProber): + def __init__(self): + super(Latin1Prober, self).__init__() + self._last_char_class = None + self._freq_counter = None + self.reset() + + def reset(self): + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + CharSetProber.reset(self) + + @property + def charset_name(self): + return "ISO-8859-1" + + @property + def language(self): + return "" + + def feed(self, byte_str): + byte_str = self.filter_with_english_letters(byte_str) + for c in byte_str: + char_class = Latin1_CharToClass[c] + freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self): + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + if total < 0.01: + confidence = 0.0 + else: + confidence = ((self._freq_counter[3] - self._freq_counter[1] * 20.0) + / total) + if confidence < 0.0: + confidence = 0.0 + # lower the confidence of latin1 so that other more accurate + # detector can take priority. + confidence = confidence * 0.73 + return confidence diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py new file mode 100644 index 0000000..6256ecf --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -0,0 +1,91 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState + + +class MultiByteCharSetProber(CharSetProber): + """ + MultiByteCharSetProber + """ + + def __init__(self, lang_filter=None): + super(MultiByteCharSetProber, self).__init__(lang_filter=lang_filter) + self.distribution_analyzer = None + self.coding_sm = None + self._last_char = [0, 0] + + def reset(self): + super(MultiByteCharSetProber, self).reset() + if self.coding_sm: + self.coding_sm.reset() + if self.distribution_analyzer: + self.distribution_analyzer.reset() + self._last_char = [0, 0] + + @property + def charset_name(self): + raise NotImplementedError + + @property + def language(self): + raise NotImplementedError + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.distribution_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + return self.distribution_analyzer.get_confidence() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..530abe7 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -0,0 +1,54 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .utf8prober import UTF8Prober +from .sjisprober import SJISProber +from .eucjpprober import EUCJPProber +from .gb2312prober import GB2312Prober +from .euckrprober import EUCKRProber +from .cp949prober import CP949Prober +from .big5prober import Big5Prober +from .euctwprober import EUCTWProber + + +class MBCSGroupProber(CharSetGroupProber): + def __init__(self, lang_filter=None): + super(MBCSGroupProber, self).__init__(lang_filter=lang_filter) + self.probers = [ + UTF8Prober(), + SJISProber(), + EUCJPProber(), + GB2312Prober(), + EUCKRProber(), + CP949Prober(), + Big5Prober(), + EUCTWProber() + ] + self.reset() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py new file mode 100644 index 0000000..8360d0f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py @@ -0,0 +1,572 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import MachineState + +# BIG5 + +BIG5_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 4,4,4,4,4,4,4,4, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 4,3,3,3,3,3,3,3, # a0 - a7 + 3,3,3,3,3,3,3,3, # a8 - af + 3,3,3,3,3,3,3,3, # b0 - b7 + 3,3,3,3,3,3,3,3, # b8 - bf + 3,3,3,3,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +BIG5_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 +) + +BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) + +BIG5_SM_MODEL = {'class_table': BIG5_CLS, + 'class_factor': 5, + 'state_table': BIG5_ST, + 'char_len_table': BIG5_CHAR_LEN_TABLE, + 'name': 'Big5'} + +# CP949 + +CP949_CLS = ( + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f + 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f + 1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f + 4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f + 1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f + 5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f + 0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f + 6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f + 6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af + 7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf + 7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff +) + +CP949_ST = ( +#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 +) + +CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) + +CP949_SM_MODEL = {'class_table': CP949_CLS, + 'class_factor': 10, + 'state_table': CP949_ST, + 'char_len_table': CP949_CHAR_LEN_TABLE, + 'name': 'CP949'} + +# EUC-JP + +EUCJP_CLS = ( + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,5,5, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,5,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,4,4,4,4,4,4,4, # 30 - 37 + 4,4,4,4,4,4,4,4, # 38 - 3f + 4,4,4,4,4,4,4,4, # 40 - 47 + 4,4,4,4,4,4,4,4, # 48 - 4f + 4,4,4,4,4,4,4,4, # 50 - 57 + 4,4,4,4,4,4,4,4, # 58 - 5f + 4,4,4,4,4,4,4,4, # 60 - 67 + 4,4,4,4,4,4,4,4, # 68 - 6f + 4,4,4,4,4,4,4,4, # 70 - 77 + 4,4,4,4,4,4,4,4, # 78 - 7f + 5,5,5,5,5,5,5,5, # 80 - 87 + 5,5,5,5,5,5,1,3, # 88 - 8f + 5,5,5,5,5,5,5,5, # 90 - 97 + 5,5,5,5,5,5,5,5, # 98 - 9f + 5,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,0,5 # f8 - ff +) + +EUCJP_ST = ( + 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f + 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 +) + +EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) + +EUCJP_SM_MODEL = {'class_table': EUCJP_CLS, + 'class_factor': 6, + 'state_table': EUCJP_ST, + 'char_len_table': EUCJP_CHAR_LEN_TABLE, + 'name': 'EUC-JP'} + +# EUC-KR + +EUCKR_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,3,3,3, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,3,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 2,2,2,2,2,2,2,2, # e0 - e7 + 2,2,2,2,2,2,2,2, # e8 - ef + 2,2,2,2,2,2,2,2, # f0 - f7 + 2,2,2,2,2,2,2,0 # f8 - ff +) + +EUCKR_ST = ( + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f +) + +EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) + +EUCKR_SM_MODEL = {'class_table': EUCKR_CLS, + 'class_factor': 4, + 'state_table': EUCKR_ST, + 'char_len_table': EUCKR_CHAR_LEN_TABLE, + 'name': 'EUC-KR'} + +# EUC-TW + +EUCTW_CLS = ( + 2,2,2,2,2,2,2,2, # 00 - 07 + 2,2,2,2,2,2,0,0, # 08 - 0f + 2,2,2,2,2,2,2,2, # 10 - 17 + 2,2,2,0,2,2,2,2, # 18 - 1f + 2,2,2,2,2,2,2,2, # 20 - 27 + 2,2,2,2,2,2,2,2, # 28 - 2f + 2,2,2,2,2,2,2,2, # 30 - 37 + 2,2,2,2,2,2,2,2, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,2, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,6,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,3,4,4,4,4,4,4, # a0 - a7 + 5,5,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,3,1,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +EUCTW_ST = ( + MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 + MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 + MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) + +EUCTW_SM_MODEL = {'class_table': EUCTW_CLS, + 'class_factor': 7, + 'state_table': EUCTW_ST, + 'char_len_table': EUCTW_CHAR_LEN_TABLE, + 'name': 'x-euc-tw'} + +# GB2312 + +GB2312_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 3,3,3,3,3,3,3,3, # 30 - 37 + 3,3,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,4, # 78 - 7f + 5,6,6,6,6,6,6,6, # 80 - 87 + 6,6,6,6,6,6,6,6, # 88 - 8f + 6,6,6,6,6,6,6,6, # 90 - 97 + 6,6,6,6,6,6,6,6, # 98 - 9f + 6,6,6,6,6,6,6,6, # a0 - a7 + 6,6,6,6,6,6,6,6, # a8 - af + 6,6,6,6,6,6,6,6, # b0 - b7 + 6,6,6,6,6,6,6,6, # b8 - bf + 6,6,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 6,6,6,6,6,6,6,6, # e0 - e7 + 6,6,6,6,6,6,6,6, # e8 - ef + 6,6,6,6,6,6,6,6, # f0 - f7 + 6,6,6,6,6,6,6,0 # f8 - ff +) + +GB2312_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 + 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +# To be accurate, the length of class 6 can be either 2 or 4. +# But it is not necessary to discriminate between the two since +# it is used for frequency analysis only, and we are validating +# each code range there as well. So it is safe to set it to be +# 2 here. +GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) + +GB2312_SM_MODEL = {'class_table': GB2312_CLS, + 'class_factor': 7, + 'state_table': GB2312_ST, + 'char_len_table': GB2312_CHAR_LEN_TABLE, + 'name': 'GB2312'} + +# Shift_JIS + +SJIS_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 3,3,3,3,3,2,2,3, # 80 - 87 + 3,3,3,3,3,3,3,3, # 88 - 8f + 3,3,3,3,3,3,3,3, # 90 - 97 + 3,3,3,3,3,3,3,3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,4,4,4, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,0,0,0) # f8 - ff + + +SJIS_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 +) + +SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) + +SJIS_SM_MODEL = {'class_table': SJIS_CLS, + 'class_factor': 6, + 'state_table': SJIS_ST, + 'char_len_table': SJIS_CHAR_LEN_TABLE, + 'name': 'Shift_JIS'} + +# UCS2-BE + +UCS2BE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2BE_ST = ( + 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 + 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 + 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f + 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) + +UCS2BE_SM_MODEL = {'class_table': UCS2BE_CLS, + 'class_factor': 6, + 'state_table': UCS2BE_ST, + 'char_len_table': UCS2BE_CHAR_LEN_TABLE, + 'name': 'UTF-16BE'} + +# UCS2-LE + +UCS2LE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2LE_ST = ( + 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 + 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) + +UCS2LE_SM_MODEL = {'class_table': UCS2LE_CLS, + 'class_factor': 6, + 'state_table': UCS2LE_ST, + 'char_len_table': UCS2LE_CHAR_LEN_TABLE, + 'name': 'UTF-16LE'} + +# UTF-8 + +UTF8_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 2,2,2,2,3,3,3,3, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 5,5,5,5,5,5,5,5, # a0 - a7 + 5,5,5,5,5,5,5,5, # a8 - af + 5,5,5,5,5,5,5,5, # b0 - b7 + 5,5,5,5,5,5,5,5, # b8 - bf + 0,0,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 7,8,8,8,8,8,8,8, # e0 - e7 + 8,8,8,8,8,9,8,8, # e8 - ef + 10,11,11,11,11,11,11,11, # f0 - f7 + 12,13,13,13,14,15,0,0 # f8 - ff +) + +UTF8_ST = ( + MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f + MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f + MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f + MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f + MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af + MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf +) + +UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) + +UTF8_SM_MODEL = {'class_table': UTF8_CLS, + 'class_factor': 16, + 'state_table': UTF8_ST, + 'char_len_table': UTF8_CHAR_LEN_TABLE, + 'name': 'UTF-8'} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py new file mode 100644 index 0000000..0adb51d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -0,0 +1,132 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import CharacterCategory, ProbingState, SequenceLikelihood + + +class SingleByteCharSetProber(CharSetProber): + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + + def __init__(self, model, reversed=False, name_prober=None): + super(SingleByteCharSetProber, self).__init__() + self._model = model + # TRUE if we need to reverse every pair in the model lookup + self._reversed = reversed + # Optional auxiliary prober for name decision + self._name_prober = name_prober + self._last_order = None + self._seq_counters = None + self._total_seqs = None + self._total_char = None + self._freq_char = None + self.reset() + + def reset(self): + super(SingleByteCharSetProber, self).reset() + # char order of last character + self._last_order = 255 + self._seq_counters = [0] * SequenceLikelihood.get_num_categories() + self._total_seqs = 0 + self._total_char = 0 + # characters that fall in our sampling range + self._freq_char = 0 + + @property + def charset_name(self): + if self._name_prober: + return self._name_prober.charset_name + else: + return self._model['charset_name'] + + @property + def language(self): + if self._name_prober: + return self._name_prober.language + else: + return self._model.get('language') + + def feed(self, byte_str): + if not self._model['keep_english_letter']: + byte_str = self.filter_international_words(byte_str) + if not byte_str: + return self.state + char_to_order_map = self._model['char_to_order_map'] + for i, c in enumerate(byte_str): + # XXX: Order is in range 1-64, so one would think we want 0-63 here, + # but that leads to 27 more test failures than before. + order = char_to_order_map[c] + # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but + # CharacterCategory.SYMBOL is actually 253, so we use CONTROL + # to make it closer to the original intent. The only difference + # is whether or not we count digits and control characters for + # _total_char purposes. + if order < CharacterCategory.CONTROL: + self._total_char += 1 + if order < self.SAMPLE_SIZE: + self._freq_char += 1 + if self._last_order < self.SAMPLE_SIZE: + self._total_seqs += 1 + if not self._reversed: + i = (self._last_order * self.SAMPLE_SIZE) + order + model = self._model['precedence_matrix'][i] + else: # reverse the order of the letters in the lookup + i = (order * self.SAMPLE_SIZE) + self._last_order + model = self._model['precedence_matrix'][i] + self._seq_counters[model] += 1 + self._last_order = order + + charset_name = self._model['charset_name'] + if self.state == ProbingState.DETECTING: + if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: + confidence = self.get_confidence() + if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, we have a winner', + charset_name, confidence) + self._state = ProbingState.FOUND_IT + elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, below negative ' + 'shortcut threshhold %s', charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD) + self._state = ProbingState.NOT_ME + + return self.state + + def get_confidence(self): + r = 0.01 + if self._total_seqs > 0: + r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / + self._total_seqs / self._model['typical_positive_ratio']) + r = r * self._freq_char / self._total_char + if r >= 1.0: + r = 0.99 + return r diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..98e95dc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -0,0 +1,73 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .sbcharsetprober import SingleByteCharSetProber +from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, + Latin5CyrillicModel, MacCyrillicModel, + Ibm866Model, Ibm855Model) +from .langgreekmodel import Latin7GreekModel, Win1253GreekModel +from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel +# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel +from .langthaimodel import TIS620ThaiModel +from .langhebrewmodel import Win1255HebrewModel +from .hebrewprober import HebrewProber +from .langturkishmodel import Latin5TurkishModel + + +class SBCSGroupProber(CharSetGroupProber): + def __init__(self): + super(SBCSGroupProber, self).__init__() + self.probers = [ + SingleByteCharSetProber(Win1251CyrillicModel), + SingleByteCharSetProber(Koi8rModel), + SingleByteCharSetProber(Latin5CyrillicModel), + SingleByteCharSetProber(MacCyrillicModel), + SingleByteCharSetProber(Ibm866Model), + SingleByteCharSetProber(Ibm855Model), + SingleByteCharSetProber(Latin7GreekModel), + SingleByteCharSetProber(Win1253GreekModel), + SingleByteCharSetProber(Latin5BulgarianModel), + SingleByteCharSetProber(Win1251BulgarianModel), + # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) + # after we retrain model. + # SingleByteCharSetProber(Latin2HungarianModel), + # SingleByteCharSetProber(Win1250HungarianModel), + SingleByteCharSetProber(TIS620ThaiModel), + SingleByteCharSetProber(Latin5TurkishModel), + ] + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, + False, hebrew_prober) + visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, + hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) + self.probers.extend([hebrew_prober, logical_hebrew_prober, + visual_hebrew_prober]) + + self.reset() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py new file mode 100644 index 0000000..9e29623 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py @@ -0,0 +1,92 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import SJISDistributionAnalysis +from .jpcntx import SJISContextAnalysis +from .mbcssm import SJIS_SM_MODEL +from .enums import ProbingState, MachineState + + +class SJISProber(MultiByteCharSetProber): + def __init__(self): + super(SJISProber, self).__init__() + self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) + self.distribution_analyzer = SJISDistributionAnalysis() + self.context_analyzer = SJISContextAnalysis() + self.reset() + + def reset(self): + super(SJISProber, self).reset() + self.context_analyzer.reset() + + @property + def charset_name(self): + return self.context_analyzer.charset_name + + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char[2 - char_len:], + char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i + 1 - char_len:i + 3 + - char_len], char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py new file mode 100644 index 0000000..7b4e92d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py @@ -0,0 +1,286 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### +""" +Module containing the UniversalDetector detector class, which is the primary +class a user of ``chardet`` should use. + +:author: Mark Pilgrim (initial port to Python) +:author: Shy Shalom (original C code) +:author: Dan Blanchard (major refactoring for 3.0) +:author: Ian Cordasco +""" + + +import codecs +import logging +import re + +from .charsetgroupprober import CharSetGroupProber +from .enums import InputState, LanguageFilter, ProbingState +from .escprober import EscCharSetProber +from .latin1prober import Latin1Prober +from .mbcsgroupprober import MBCSGroupProber +from .sbcsgroupprober import SBCSGroupProber + + +class UniversalDetector(object): + """ + The ``UniversalDetector`` class underlies the ``chardet.detect`` function + and coordinates all of the different charset probers. + + To get a ``dict`` containing an encoding and its confidence, you can simply + run: + + .. code:: + + u = UniversalDetector() + u.feed(some_bytes) + u.close() + detected = u.result + + """ + + MINIMUM_THRESHOLD = 0.20 + HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') + ESC_DETECTOR = re.compile(b'(\033|~{)') + WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') + ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', + 'iso-8859-2': 'Windows-1250', + 'iso-8859-5': 'Windows-1251', + 'iso-8859-6': 'Windows-1256', + 'iso-8859-7': 'Windows-1253', + 'iso-8859-8': 'Windows-1255', + 'iso-8859-9': 'Windows-1254', + 'iso-8859-13': 'Windows-1257'} + + def __init__(self, lang_filter=LanguageFilter.ALL): + self._esc_charset_prober = None + self._charset_probers = [] + self.result = None + self.done = None + self._got_data = None + self._input_state = None + self._last_char = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + self._has_win_bytes = None + self.reset() + + def reset(self): + """ + Reset the UniversalDetector and all of its probers back to their + initial states. This is called by ``__init__``, so you only need to + call this directly in between analyses of different documents. + """ + self.result = {'encoding': None, 'confidence': 0.0, 'language': None} + self.done = False + self._got_data = False + self._has_win_bytes = False + self._input_state = InputState.PURE_ASCII + self._last_char = b'' + if self._esc_charset_prober: + self._esc_charset_prober.reset() + for prober in self._charset_probers: + prober.reset() + + def feed(self, byte_str): + """ + Takes a chunk of a document and feeds it through all of the relevant + charset probers. + + After calling ``feed``, you can check the value of the ``done`` + attribute to see if you need to continue feeding the + ``UniversalDetector`` more data, or if it has made a prediction + (in the ``result`` attribute). + + .. note:: + You should always call ``close`` when you're done feeding in your + document if ``done`` is not already ``True``. + """ + if self.done: + return + + if not len(byte_str): + return + + if not isinstance(byte_str, bytearray): + byte_str = bytearray(byte_str) + + # First check for known BOMs, since these are guaranteed to be correct + if not self._got_data: + # If the data starts with BOM, we know it is UTF + if byte_str.startswith(codecs.BOM_UTF8): + # EF BB BF UTF-8 with BOM + self.result = {'encoding': "UTF-8-SIG", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_UTF32_LE, + codecs.BOM_UTF32_BE)): + # FF FE 00 00 UTF-32, little-endian BOM + # 00 00 FE FF UTF-32, big-endian BOM + self.result = {'encoding': "UTF-32", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\xFE\xFF\x00\x00'): + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + self.result = {'encoding': "X-ISO-10646-UCS-4-3412", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\x00\x00\xFF\xFE'): + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + self.result = {'encoding': "X-ISO-10646-UCS-4-2143", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): + # FF FE UTF-16, little endian BOM + # FE FF UTF-16, big endian BOM + self.result = {'encoding': "UTF-16", + 'confidence': 1.0, + 'language': ''} + + self._got_data = True + if self.result['encoding'] is not None: + self.done = True + return + + # If none of those matched and we've only see ASCII so far, check + # for high bytes and escape sequences + if self._input_state == InputState.PURE_ASCII: + if self.HIGH_BYTE_DETECTOR.search(byte_str): + self._input_state = InputState.HIGH_BYTE + elif self._input_state == InputState.PURE_ASCII and \ + self.ESC_DETECTOR.search(self._last_char + byte_str): + self._input_state = InputState.ESC_ASCII + + self._last_char = byte_str[-1:] + + # If we've seen escape sequences, use the EscCharSetProber, which + # uses a simple state machine to check for known escape sequences in + # HZ and ISO-2022 encodings, since those are the only encodings that + # use such sequences. + if self._input_state == InputState.ESC_ASCII: + if not self._esc_charset_prober: + self._esc_charset_prober = EscCharSetProber(self.lang_filter) + if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': + self._esc_charset_prober.charset_name, + 'confidence': + self._esc_charset_prober.get_confidence(), + 'language': + self._esc_charset_prober.language} + self.done = True + # If we've seen high bytes (i.e., those with values greater than 127), + # we need to do more complicated checks using all our multi-byte and + # single-byte probers that are left. The single-byte probers + # use character bigram distributions to determine the encoding, whereas + # the multi-byte probers use a combination of character unigram and + # bigram distributions. + elif self._input_state == InputState.HIGH_BYTE: + if not self._charset_probers: + self._charset_probers = [MBCSGroupProber(self.lang_filter)] + # If we're checking non-CJK encodings, use single-byte prober + if self.lang_filter & LanguageFilter.NON_CJK: + self._charset_probers.append(SBCSGroupProber()) + self._charset_probers.append(Latin1Prober()) + for prober in self._charset_probers: + if prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': prober.charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language} + self.done = True + break + if self.WIN_BYTE_DETECTOR.search(byte_str): + self._has_win_bytes = True + + def close(self): + """ + Stop analyzing the current document and come up with a final + prediction. + + :returns: The ``result`` attribute, a ``dict`` with the keys + `encoding`, `confidence`, and `language`. + """ + # Don't bother with checks if we're already done + if self.done: + return self.result + self.done = True + + if not self._got_data: + self.logger.debug('no data received!') + + # Default to ASCII if it is all we've seen so far + elif self._input_state == InputState.PURE_ASCII: + self.result = {'encoding': 'ascii', + 'confidence': 1.0, + 'language': ''} + + # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD + elif self._input_state == InputState.HIGH_BYTE: + prober_confidence = None + max_prober_confidence = 0.0 + max_prober = None + for prober in self._charset_probers: + if not prober: + continue + prober_confidence = prober.get_confidence() + if prober_confidence > max_prober_confidence: + max_prober_confidence = prober_confidence + max_prober = prober + if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): + charset_name = max_prober.charset_name + lower_charset_name = max_prober.charset_name.lower() + confidence = max_prober.get_confidence() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if self._has_win_bytes: + charset_name = self.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + self.result = {'encoding': charset_name, + 'confidence': confidence, + 'language': max_prober.language} + + # Log all prober confidences if none met MINIMUM_THRESHOLD + if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.result['encoding'] is None: + self.logger.debug('no probers hit minimum threshold') + for group_prober in self._charset_probers: + if not group_prober: + continue + if isinstance(group_prober, CharSetGroupProber): + for prober in group_prober.probers: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + else: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + return self.result diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py new file mode 100644 index 0000000..6c3196c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py @@ -0,0 +1,82 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState +from .codingstatemachine import CodingStateMachine +from .mbcssm import UTF8_SM_MODEL + + + +class UTF8Prober(CharSetProber): + ONE_CHAR_PROB = 0.5 + + def __init__(self): + super(UTF8Prober, self).__init__() + self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) + self._num_mb_chars = None + self.reset() + + def reset(self): + super(UTF8Prober, self).reset() + self.coding_sm.reset() + self._num_mb_chars = 0 + + @property + def charset_name(self): + return "utf-8" + + @property + def language(self): + return "" + + def feed(self, byte_str): + for c in byte_str: + coding_state = self.coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + if self.coding_sm.get_current_charlen() >= 2: + self._num_mb_chars += 1 + + if self.state == ProbingState.DETECTING: + if self.get_confidence() > self.SHORTCUT_THRESHOLD: + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + unlike = 0.99 + if self._num_mb_chars < 6: + unlike *= self.ONE_CHAR_PROB ** self._num_mb_chars + return 1.0 - unlike + else: + return unlike diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/version.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/version.py new file mode 100644 index 0000000..bb2a34a --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/chardet/version.py @@ -0,0 +1,9 @@ +""" +This module exists only to simplify retrieving the version number of chardet +from within setup.py and from chardet subpackages. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +__version__ = "3.0.4" +VERSION = __version__.split('.') diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..34c263c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py @@ -0,0 +1,6 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.3' diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..026a426ade98bb844325af421207920bc4681764 GIT binary patch literal 479 zcmYjNJ5B>J5Z!-1n-y)Yutj1-Xpj)%6H(FxgyhCDyH;Ydwinw82{+&f9DysjrJ|yT z1`pdXlHd5vjGyM27R4~|@wWb}<5(m(385Y} zsmO39dblV0xGx5H@K=wUp~!LW?+G5&=^`kz&(KSiiA%WT)@3#8vhH$GsYYokD6wp3 zVeDFI=8{WVtzEjX`-ZvfYNL(PW$5w?Y51+W=W=%Rk{(Wvr?Z=~a6^#fwBd$naG+y= z1R(X`66&An40sQq4={jE4jrqWK~TfjxSWtJ(}pWa$a|mCvp{vtwl5e3N$GK%d+aYQDnoi;f86-GTM#JSSFPv zoy3L%6j1sjdP|S~ON-um;$KKFompCr{6Y&~N@73m>?~(EPp(@im(LBsD3kogJ z;%kK#nK9JtEYKxd2D&V16X*$A0lLDnz|R4FiB1ANDd{}Wm+2JHQ<5&Q+)%NLEDu;> z1;8>h0Vn8mSE*gy`_)|C^up?PKzI7Ay1{(L-H3(NMyI=2?QOc;?L~}NdjW;TD5ws& z=SQBu0W1B0%O&7%d%oKb>t>g+Th+}d8r-rh=GR|%Ti$?C&#ec1!$QhJxeg0^?tnQc z)ZA0AJ%sV-Ey?r<_-u6HT!Ybs5tacWB_jLEkW7P1rxo~kwIj&(fhx3ScTs3z#A`(8 zDDyz!8F=CCZ`NAKcxZtczPl0l)V1b=-p)4jBUlWW^~{}t#|(QpVBAZV2fJW>7kmpfsPGv-&uRO0*s z+U`kh9AkgdG&51v_k4DFOwlANf^qCZTW4W}$YlytqXN|kH3=1{Q4JDF7ug2HstGoQ z$G#J-O~Yv7>)!xG5Q-t0R~}t|MfS-XLdhQ_G$M6h*(XEwl^SU{i}ZbUNQN3wo*8tM zsZK2=H0p6-hLOvo@P!v`ip&O!+$iEA1F^!1Oo^H%^pN$R2;FzLnMgf%`#Yz+Z8(nS zdy(T@#gD^)D|()oaPL))tf`MSb-+k?1LZl6FdZi;c$nuM=h=?iPdNMo5Hsd%EF+Gx zIlB1@44Fm-k@P87qrYZx(;a{#R~INWKWJ?lEy@N+paGU-3nb72%haG**#al1Npsi& zX&!=75vDX_hlc@=u^aR-lA{afs7{9Fs6CJq?V*t*tje#!RzdtR(`1RLYs;Sj=dd>R z3hy-5*Oo7!Z!Sq$lzCE>KEJfy`Fu=SX-paNE}*V?zPil!ODpS*mHFD3#w0JKL+gj^ z3n-mGU#ULVXx{sBOyjtX$m?OqMi)|QvLGazmh)#8|=fz_!I{t2+peyEa{Y%utTu#Z;}K0?6oar7uhBRMMJ zHxOiZakV8fB5QJ3#pQ%4pqHlqe`R_SyDBSXNK`6*!MMz5S?TY{p773 zU*Wj_{|WCLoJQj7pgzVFjlV#^JdM9Xz+{c<2o?gG6jKC-@C0E4VH3eac#5zE(BUn> zGrhA8j0G6~)jPQVeD7crI6wIzk<-bax*P4naR(EY7Xjia1Q<%EmN}V->A4lmC85nP z-WTc4(#ritTV!RLIgNJvOsmj*%ys!&9mSmEDmD*PIeJt?#&KxSa~zH*e~z`za|l@k zJZy11{%|~eh;;B6PnGiLDf@uT7LHDl(uIyC3Izg@6e($?NH=A}KI=P$@m=y8zoUYL bunM53WIAdF+@@ygp literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df26352838db4fe4694df60db77107bacc7c10b8 GIT binary patch literal 7695 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 ZeAC2^l<+3y_2BB}*sNt^>{_!c{|C#a2WbER literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d92eac1ea533ae44c14c85539c6880ab440b30bf GIT binary patch literal 1746 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..571aa7a51018d958bbcd65a02e258fd966dc8953 GIT binary patch literal 4700 zcmcgwNpl;=6`r0AgCR(Q6m80~EHjoZ8%`)BRuV69Y_6geQ=p4W9HNq%0?|VX5Qw3B z23lCdRH~vwF1F9Pk+C5QZgzUIWg;9T;(?iql9C?5k>zv@-0?i=|1 z{o#Kbw_Y=h{}6NWXMWmp{1~<9&%-~jN{?)2m;BD@JcNA}fcX57h-R4%^;db5SPQAe0 zy2lIkBKPV(FXCihdSAD?%TXs1=h$lqI_lgcVi6%d&K)8ykpf7qGA6ws=r#_+Adr3# z97ep?p?)a{zUehO*^INazPS9Qw3pU*R;9DHxw=}D_VVsxOy(rz? z)%Ep_uceJ8yUz{rD&C>U4}Yuf#G#0*%?FKc;~?ttMs+1>_71~tg25=Pe$$x8V?KOx z6pEG}K6;vjaS|Mfs2w(ws%GcI?xSj_wMXoOsC(!3$JMx%g!4y@=EKH87*~&4N7dj_ z*yWMH!7xVSuu+ArlTaMqK6)z17G!|94B|0+=ugeQ+$UGD<#M0E{*JQ$R`RqH3T0~# ztIz)eQmeRP3{O!aG7ci$+Q#Pn#dYZ}ZQfh^Vp~q>XRri2uWfGZ)K&(@)4f^RO9D)x z#iW52UX8sVGHJO$3%>&yh|JAr2B2hd8&80hyLdW$j(fcLi~(3b;bXjnUV*>G$N2<$ z9>2*ac^SPTf16M7Y4m)4h0kE;7=MSq!e7NmiQnS0{3?3m{9S&HzmDDne~(}1Z=g5H zZ}2y9)O3y;NgCw*GXhC4wImPnOHi(ek4LUp3iC43HAWL3!{p4gP6b| zC;E#u?*^UL!Gq*ElXe_-_AfY^d`tn^>b8;~AZNz}8H1HsiFx$r9G5fO-OiW2+=$n) z6^5b4IGZtMsb`{DOXw>Tol_K4Z1FZ8;uewjh`dkar$qW8ER=A?gWMFfBPPol?071d zsXyCPGc=1I_4}T^4JL&+3lJutp7}X)s^VD660>cg*-M5Rag-;mnxGUdC}km(2C|Zc z1ZgSWAVQ*7OhK7bI*%HiUN|DYxQVHE(Nz2x%x4o9gztN~_Hyaz-N2zTX@)~GDD?Rj zsO0DeRwgs$cm)~_LtDLEyhXx|(W{yojLO!z)WxU^(C|=*5y@pygi5|nht$jDFAd4x zUval-V@>{}e&71*U|@5jXNU>NZJfa7keQ{Xct164z$3BR_6A%QnBD2Y$fR*!jbV?I zTA8+h&~vj^Vd4%>g=5~qLwiTzXW*R=Xx`S2u~h&$Dg@NV5lzLfvG^Bg@dStg(7Vhv zZ8mLAv*S_@G&SQ(4$)ka+5wVHX;Vm@FB$pv;${noSmBb4x(!fs!+4xEdW~Ha2VQnLp7cKk|ik)gHf#u;*Z5xc2oR0jDsI?5mvklG-bt6>m3 zwc9pKLDZcwV4$2Y5D%`wbiB6XOZM1y zxq%rAGv5u(IGC|11z@y@tHn;ev~XrK;(dV6I2}7P^0DlMe_^hKwN9QsDy}Z})HjbH z&;P*YaH&%8DUwxzDz7Ne>R5F7dIG&ho~_=8C*_x7F!nHRDDvIqD_L z4;rG;#HYk1bN#fW)0hH4Q3JrncU3q5fV7+OUQ13k!%ingu@}UuAeC42z+OnVTcABE z{e2M~qF9IomlJwH5(S}X;CpH~&B>A)%}$G;NK6%xVP|+T%jQ*;`Fzk4VoFiV#`p~L z%t7+Fc+3!rD6IM@`X`tlL6JtsCxjk_A%{YWFa?yj-7hkf{1KikplGKV4@k*yhZVOn zU;p`%6*vnkhEe0D&I_o?##BM(j3Pw*4hmFCqj5|*TA@IENj(+JR5j_?rdveRh|pb? z1-RItfkA%zir6h8+eAjZPhv!85&*XL*kA_+$8Ueb4iJ-}n4E|207?@u@C#2_oHSkN)qV{w1J3 bvekmi-XXnD^*>HNBR8npykwWh%Fh1))8)kV literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py new file mode 100644 index 0000000..7877658 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py @@ -0,0 +1,102 @@ +# 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 = '\007' + + +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/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py new file mode 100644 index 0000000..359c92b --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -0,0 +1,257 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import 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 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 + except AttributeError: + 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\\]((?:.|;)*?)(\x07)\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() + + # should we strip ANSI sequences from our output? + if strip is None: + strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = conversion_supported and not self.stream.closed and self.stream.isatty() + 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 in '\x07': # \x07 = BEL + 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 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py new file mode 100644 index 0000000..430d066 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py @@ -0,0 +1,80 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = None +orig_stderr = None + +wrapped_stdout = None +wrapped_stderr = None + +atexit_done = False + + +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 + + +@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 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..c2d8360 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py @@ -0,0 +1,152 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +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 + + 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) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..0fdb4ec --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py @@ -0,0 +1,169 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +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) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/contextlib2.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/contextlib2.py new file mode 100644 index 0000000..3aae8f4 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/contextlib2.py @@ -0,0 +1,518 @@ +"""contextlib2 - backports and enhancements to the contextlib module""" + +import abc +import sys +import warnings +from collections import deque +from functools import wraps + +__all__ = ["contextmanager", "closing", "nullcontext", + "AbstractContextManager", + "ContextDecorator", "ExitStack", + "redirect_stdout", "redirect_stderr", "suppress"] + +# Backwards compatibility +__all__ += ["ContextStack"] + + +# Backport abc.ABC +if sys.version_info[:2] >= (3, 4): + _abc_ABC = abc.ABC +else: + _abc_ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) + + +# Backport classic class MRO +def _classic_mro(C, result): + if C in result: + return + result.append(C) + for B in C.__bases__: + _classic_mro(B, result) + return result + + +# Backport _collections_abc._check_methods +def _check_methods(C, *methods): + try: + mro = C.__mro__ + except AttributeError: + mro = tuple(_classic_mro(C, [])) + + for method in methods: + for B in mro: + if method in B.__dict__: + if B.__dict__[method] is None: + return NotImplemented + break + else: + return NotImplemented + return True + + +class AbstractContextManager(_abc_ABC): + """An abstract base class for context managers.""" + + def __enter__(self): + """Return `self` upon entering the runtime context.""" + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + """Raise any exception triggered within the runtime context.""" + return None + + @classmethod + def __subclasshook__(cls, C): + """Check whether subclass is considered a subclass of this ABC.""" + if cls is AbstractContextManager: + return _check_methods(C, "__enter__", "__exit__") + return NotImplemented + + +class ContextDecorator(object): + """A base class or mixin that enables context managers to work as decorators.""" + + def refresh_cm(self): + """Returns the context manager used to actually wrap the call to the + decorated function. + + The default implementation just returns *self*. + + Overriding this method allows otherwise one-shot context managers + like _GeneratorContextManager to support use as decorators via + implicit recreation. + + DEPRECATED: refresh_cm was never added to the standard library's + ContextDecorator API + """ + warnings.warn("refresh_cm was never added to the standard library", + DeprecationWarning) + return self._recreate_cm() + + def _recreate_cm(self): + """Return a recreated instance of self. + + Allows an otherwise one-shot context manager like + _GeneratorContextManager to support use as + a decorator via implicit recreation. + + This is a private interface just for _GeneratorContextManager. + See issue #11647 for details. + """ + return self + + def __call__(self, func): + @wraps(func) + def inner(*args, **kwds): + with self._recreate_cm(): + return func(*args, **kwds) + return inner + + +class _GeneratorContextManager(ContextDecorator): + """Helper for @contextmanager decorator.""" + + def __init__(self, func, args, kwds): + self.gen = func(*args, **kwds) + self.func, self.args, self.kwds = func, args, kwds + # Issue 19330: ensure context manager instances have good docstrings + doc = getattr(func, "__doc__", None) + if doc is None: + doc = type(self).__doc__ + self.__doc__ = doc + # Unfortunately, this still doesn't provide good help output when + # inspecting the created context manager instances, since pydoc + # currently bypasses the instance docstring and shows the docstring + # for the class instead. + # See http://bugs.python.org/issue19404 for more details. + + def _recreate_cm(self): + # _GCM instances are one-shot context managers, so the + # CM must be recreated each time a decorated function is + # called + return self.__class__(self.func, self.args, self.kwds) + + def __enter__(self): + try: + return next(self.gen) + except StopIteration: + raise RuntimeError("generator didn't yield") + + def __exit__(self, type, value, traceback): + if type is None: + try: + next(self.gen) + except StopIteration: + return + else: + raise RuntimeError("generator didn't stop") + else: + if value is None: + # Need to force instantiation so we can reliably + # tell if we get the same exception back + value = type() + try: + self.gen.throw(type, value, traceback) + raise RuntimeError("generator didn't stop after throw()") + except StopIteration as exc: + # Suppress StopIteration *unless* it's the same exception that + # was passed to throw(). This prevents a StopIteration + # raised inside the "with" statement from being suppressed. + return exc is not value + except RuntimeError as exc: + # Don't re-raise the passed in exception + if exc is value: + return False + # Likewise, avoid suppressing if a StopIteration exception + # was passed to throw() and later wrapped into a RuntimeError + # (see PEP 479). + if _HAVE_EXCEPTION_CHAINING and exc.__cause__ is value: + return False + raise + except: + # only re-raise if it's *not* the exception that was + # passed to throw(), because __exit__() must not raise + # an exception unless __exit__() itself failed. But throw() + # has to raise the exception to signal propagation, so this + # fixes the impedance mismatch between the throw() protocol + # and the __exit__() protocol. + # + if sys.exc_info()[1] is not value: + raise + + +def contextmanager(func): + """@contextmanager decorator. + + Typical usage: + + @contextmanager + def some_generator(): + + try: + yield + finally: + + + This makes this: + + with some_generator() as : + + + equivalent to this: + + + try: + = + + finally: + + + """ + @wraps(func) + def helper(*args, **kwds): + return _GeneratorContextManager(func, args, kwds) + return helper + + +class closing(object): + """Context to automatically close something at the end of a block. + + Code like this: + + with closing(.open()) as f: + + + is equivalent to this: + + f = .open() + try: + + finally: + f.close() + + """ + def __init__(self, thing): + self.thing = thing + + def __enter__(self): + return self.thing + + def __exit__(self, *exc_info): + self.thing.close() + + +class _RedirectStream(object): + + _stream = None + + def __init__(self, new_target): + self._new_target = new_target + # We use a list of old targets to make this CM re-entrant + self._old_targets = [] + + def __enter__(self): + self._old_targets.append(getattr(sys, self._stream)) + setattr(sys, self._stream, self._new_target) + return self._new_target + + def __exit__(self, exctype, excinst, exctb): + setattr(sys, self._stream, self._old_targets.pop()) + + +class redirect_stdout(_RedirectStream): + """Context manager for temporarily redirecting stdout to another file. + + # How to send help() to stderr + with redirect_stdout(sys.stderr): + help(dir) + + # How to write help() to a file + with open('help.txt', 'w') as f: + with redirect_stdout(f): + help(pow) + """ + + _stream = "stdout" + + +class redirect_stderr(_RedirectStream): + """Context manager for temporarily redirecting stderr to another file.""" + + _stream = "stderr" + + +class suppress(object): + """Context manager to suppress specified exceptions + + After the exception is suppressed, execution proceeds with the next + statement following the with statement. + + with suppress(FileNotFoundError): + os.remove(somefile) + # Execution still resumes here if the file was already removed + """ + + def __init__(self, *exceptions): + self._exceptions = exceptions + + def __enter__(self): + pass + + def __exit__(self, exctype, excinst, exctb): + # Unlike isinstance and issubclass, CPython exception handling + # currently only looks at the concrete type hierarchy (ignoring + # the instance and subclass checking hooks). While Guido considers + # that a bug rather than a feature, it's a fairly hard one to fix + # due to various internal implementation details. suppress provides + # the simpler issubclass based semantics, rather than trying to + # exactly reproduce the limitations of the CPython interpreter. + # + # See http://bugs.python.org/issue12029 for more details + return exctype is not None and issubclass(exctype, self._exceptions) + + +# Context manipulation is Python 3 only +_HAVE_EXCEPTION_CHAINING = sys.version_info[0] >= 3 +if _HAVE_EXCEPTION_CHAINING: + def _make_context_fixer(frame_exc): + def _fix_exception_context(new_exc, old_exc): + # Context may not be correct, so find the end of the chain + while 1: + exc_context = new_exc.__context__ + if exc_context is old_exc: + # Context is already set correctly (see issue 20317) + return + if exc_context is None or exc_context is frame_exc: + break + new_exc = exc_context + # Change the end of the chain to point to the exception + # we expect it to reference + new_exc.__context__ = old_exc + return _fix_exception_context + + def _reraise_with_existing_context(exc_details): + try: + # bare "raise exc_details[1]" replaces our carefully + # set-up context + fixed_ctx = exc_details[1].__context__ + raise exc_details[1] + except BaseException: + exc_details[1].__context__ = fixed_ctx + raise +else: + # No exception context in Python 2 + def _make_context_fixer(frame_exc): + return lambda new_exc, old_exc: None + + # Use 3 argument raise in Python 2, + # but use exec to avoid SyntaxError in Python 3 + def _reraise_with_existing_context(exc_details): + exc_type, exc_value, exc_tb = exc_details + exec("raise exc_type, exc_value, exc_tb") + +# Handle old-style classes if they exist +try: + from types import InstanceType +except ImportError: + # Python 3 doesn't have old-style classes + _get_type = type +else: + # Need to handle old-style context managers on Python 2 + def _get_type(obj): + obj_type = type(obj) + if obj_type is InstanceType: + return obj.__class__ # Old-style class + return obj_type # New-style class + + +# Inspired by discussions on http://bugs.python.org/issue13585 +class ExitStack(object): + """Context manager for dynamic management of a stack of exit callbacks + + For example: + + with ExitStack() as stack: + files = [stack.enter_context(open(fname)) for fname in filenames] + # All opened files will automatically be closed at the end of + # the with statement, even if attempts to open files later + # in the list raise an exception + + """ + def __init__(self): + self._exit_callbacks = deque() + + def pop_all(self): + """Preserve the context stack by transferring it to a new instance""" + new_stack = type(self)() + new_stack._exit_callbacks = self._exit_callbacks + self._exit_callbacks = deque() + return new_stack + + def _push_cm_exit(self, cm, cm_exit): + """Helper to correctly register callbacks to __exit__ methods""" + def _exit_wrapper(*exc_details): + return cm_exit(cm, *exc_details) + _exit_wrapper.__self__ = cm + self.push(_exit_wrapper) + + def push(self, exit): + """Registers a callback with the standard __exit__ method signature + + Can suppress exceptions the same way __exit__ methods can. + + Also accepts any object with an __exit__ method (registering a call + to the method instead of the object itself) + """ + # We use an unbound method rather than a bound method to follow + # the standard lookup behaviour for special methods + _cb_type = _get_type(exit) + try: + exit_method = _cb_type.__exit__ + except AttributeError: + # Not a context manager, so assume its a callable + self._exit_callbacks.append(exit) + else: + self._push_cm_exit(exit, exit_method) + return exit # Allow use as a decorator + + def callback(self, callback, *args, **kwds): + """Registers an arbitrary callback and arguments. + + Cannot suppress exceptions. + """ + def _exit_wrapper(exc_type, exc, tb): + callback(*args, **kwds) + # We changed the signature, so using @wraps is not appropriate, but + # setting __wrapped__ may still help with introspection + _exit_wrapper.__wrapped__ = callback + self.push(_exit_wrapper) + return callback # Allow use as a decorator + + def enter_context(self, cm): + """Enters the supplied context manager + + If successful, also pushes its __exit__ method as a callback and + returns the result of the __enter__ method. + """ + # We look up the special methods on the type to match the with statement + _cm_type = _get_type(cm) + _exit = _cm_type.__exit__ + result = _cm_type.__enter__(cm) + self._push_cm_exit(cm, _exit) + return result + + def close(self): + """Immediately unwind the context stack""" + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, *exc_details): + received_exc = exc_details[0] is not None + + # We manipulate the exception state so it behaves as though + # we were actually nesting multiple with statements + frame_exc = sys.exc_info()[1] + _fix_exception_context = _make_context_fixer(frame_exc) + + # Callbacks are invoked in LIFO order to match the behaviour of + # nested context managers + suppressed_exc = False + pending_raise = False + while self._exit_callbacks: + cb = self._exit_callbacks.pop() + try: + if cb(*exc_details): + suppressed_exc = True + pending_raise = False + exc_details = (None, None, None) + except: + new_exc_details = sys.exc_info() + # simulate the stack of exceptions by setting the context + _fix_exception_context(new_exc_details[1], exc_details[1]) + pending_raise = True + exc_details = new_exc_details + if pending_raise: + _reraise_with_existing_context(exc_details) + return received_exc and suppressed_exc + + +# Preserve backwards compatibility +class ContextStack(ExitStack): + """Backwards compatibility alias for ExitStack""" + + def __init__(self): + warnings.warn("ContextStack has been renamed to ExitStack", + DeprecationWarning) + super(ContextStack, self).__init__() + + def register_exit(self, callback): + return self.push(callback) + + def register(self, callback, *args, **kwds): + return self.callback(callback, *args, **kwds) + + def preserve(self): + return self.pop_all() + + +class nullcontext(AbstractContextManager): + """Context manager that does no additional processing. + Used as a stand-in for a normal context manager, when a particular + block of code is only sometimes used with a normal context manager: + cm = optional_cm if condition else nullcontext() + with cm: + # Perform operation, using optional_cm if condition is True + """ + + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, *excinfo): + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..63d916e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2019 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.3.1' + +class DistlibException(Exception): + pass + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + class NullHandler(logging.Handler): + def handle(self, record): pass + def emit(self, record): pass + def createLock(self): self.lock = None + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0839a0e6c19a32a63820539f84e7af027a938fbe GIT binary patch literal 1092 zcmah|&2G~`5T5mak`_WihzIB;hr~raRYIr|tpKTtkSc`mWo5iW>ejK{wNo__xAwp* zaBGi!rM+_E%ndHg*!_tL5@YRTX6M_P*>5ND>S`BAz8(FJcP)VL>?}(_&Ni(oBc(uz z3?figBwmBEm2(ctLF>$p>_wlVZU$2)as+&UeBA+b=kEmYM`%y^g!T~*J;FO5A-6UF z;ljt@QbZl>Ty)VngG(W(%03K&A5;;>3wL9*IeMP3vn+y~ZCcd|DTGKUh^*$IW%@g5 zRcGm^-O~iiIxX_?z&3%Dc|1iaO;^fkp=KG|D^ecM;;fBaf%Yg{qker2-&d%sFgb|x zcv9pl4tI)VHpRTA%>u*Yct}4(JS|bD?RGh@v8rXMi$hH6uyu!+pM=y6y9Y(SIob-V zw8mi>Cr9xFtFTPVP@Z6}3LUDp^H9n(PirYhVODvW!z< z_StwElR~RI>a{C_pwp`&eT9@P6q9Gs^0V-7sv68+^8 z97?$T8)2KT)Le(NDfj}ixl~hzICZijIT{GfblFvp_0 z2fz*okz-I2=*TjySg~yRXF*AJjs>Qsy1TlLufF>3s;RD6F>K)P2d{p;`o(*O@!Pye{*B<`5I)|+ z1;cO*$7~o)vuv8oTMesh$#Xsyc{Zhq;wY4D$8m2ZNZqT-tDU@4 zXsl_jEw7bl#m2hky7Ic_`tthbhVq8y#`4DIrt&6vHq>~qxw*W#`B3@c<|E}tns(W4 zZYghRZY^(ZK3aYhWrm%RH;wW`QunsNi`cQkjFcQz-=6U|b&)SN6& zHXkcL)|@I&HK)td=xq-D=Nl{V#!Pu8?CGxZE~#yIdAEE&UfzT6vBne4C(2KRWu7cQ z={{9{3inqwo^F1qyjSjzBmd#@KIEJCm z62s_$5Jsy3BhC;Ygzq(tLn-*|FYgCD9ujzjunJ*eIBOe+W!!`rVGzOVgT`-#<3Aja ze_i7lffykND2L-c(mYz;73L2&kCnHD`Qy01JIwD1;q+_*PA8ft%TEH*&k3y713vo* zA9rtgZYW$?iDndIb1(x_n0J*(UX5md4|HY-bDV+2Vc_^_)q(wY9OfdA593 z-kJ>GLSOjLwXU4Q=lnC6i+Rg=)Y*oX&+2#4-uA|e&6mnAqueX*%g&D5*4r7?a9=LJ z)SAN6op}0cxojF3!-P{p+a*RpcLc_MraPBA8&rbLBatwQ>z_9><#w-t6f&-RxcWp7C~e@vQSidET9Op1f+k zomGzWRC%G5b)GISI3IHMqP{uj!_Gc@*P?pS=65tMH|yoPjFR={QSYzCJ=)*6(!5%} z+H8~?Ax42RxtY67=fFL)+;R>&A3=R>=a6$4-}OYj&omaB*UHzVUJaAnQa-NtnTFC& z^&3LJEa%9_Ezq!|pkX<+!+qkmam6cpEkgGgYD3;MWM+;x{AQ=zk-A@(KIfKxB(D2e zJiT7N9+o}PxY4{>UXrp8hh;w%mUW%yoacc*OS%`pf>X{5ZyL@E?sZ^6Co!7YMz{G! z`3r-z`S$XIfGW-(Dfy1{U~~Rt9(mpeN$@9FMT>u>)FO% zYJRNzo$_46Fu(L%JkGO?zpS5zHEsZ$&NaTPdAoeO`DXde=Eut)mp9jhHQ&^4>e3kN zdFMrp^+op+80)RD4zB-~8lP<5Dc@K)6=E`^9v&w(PdBu4Z zGyfLqD?1lZ-vz1fQ_g~O(Yb_Ce-EC#=2Y;c;(i(}eJ`$GcdEFqx^Lt9`*1zy)NoxB zI&eq##Iep9rOWS_x#eL z`>v@U=Do`uzuqY2f?`KC7OTp0gJI-4EuAkQf2Cb-$zyM^QTM~gk}nm45fo4^T5zws zLGD_o?Ylvt)6$tyJlwNb^)I)oO*a^ga(i$)SaYP^^4*qyzTG}|8T~z`R9gi@Qm>C5 z*4V@7k9iK~2tM9sB)-uzeDk)Y*7S_KmSf&CZyA23XWTXc6KnRpT*sfE-doBB!*#FT z^89M6CjF|{+KwCK-Io5YNIwA>(|gBIn^5t4pV;-H=PGYk?Q*qMU1+zQ>aL^hTBnJw z;%3|3b*(y$uW9$@qO0oq_TrN7dVXb5wXe7}f0w?Tc3anXHR^M`yxeX*G4tVFUfp-6 z7pt|a)dknvwOC)=Rk`lAoVMEK)IFbNYwaeWG_$y*#?YCEIov!F!yLvxxmOwr3TNGG z9W)3)t44d##q7Lz_EZSK6X(yL(b+Z3K2!DTwZk3%@`-B8X}Bs_$-8H&o_C|IoEH{U zFv1(r)788<>pFFXfkZb)=Q{O|*TW~yzi{ddafeR}_4#_k*9F7u ze6``FCzO+!#YZM|KQcbVybh+o!DKefTV~J1v}W#^RXn+iiNyq4Jxe{=v+ib{tdqM3 z;AZ(?Jg=b~3%NZ;?ZEE*-ku_Iz9c!!GSC+0)Yp?R0^iQ})henVDHd zZ0nA^HZe0cm~;vBqdA;Cf!s;~jf^+I4NAQh(!t18GftA6crkXumg&SEJOWLc|v z3@@o^Cd4kCJcwL~>8$EWTzaEOj7-rin#0zxIc}=mxCS&=oOf^fCr`hus*Aw4(vT|R zz8YdeAzF$FUZ|D4r0A?zms*XaG^`$CegzXQ8#T`40VXz+EljpDd6daEB&Cdcgn8ng zTE!b?FrsHvrm>S z`-Qv3UhW=97)otO-nlzOHQ^q5XBxfyQRC8@TgCV}YUTZFKa2N=ol$4S-H{&F%X*0X z>kYqfW%QnPYp6GLcSR57utrvP7hfQPf)&`+S`CYzIlpb6J$}S~`tdzaP1#tZe!bnY zpPqSg=8559JN&!s`-}T_?eg30hBs4p{rQ=;TG(~jZ#H(R`5Iq+G<4?HizYc`lun6AXh5DBrtL_b_U^yVP5z?1eG^;l&@gT}s zkZmu>fou9+x!7>${U(-2Fr?gOwcc{jjiI=3keyf6g&>1h5*RI1VXWe`JVAC@V{_<` zH;Kf^Wlj78X;_C(F=v^>?`N}Fb39kfjGAk5t1=rvJXTqoP4zrV*SHpo2;lVg(bW z5)c^+%JopKYfna)F*#$)n%cAX@x3;fZoI(a`V>f;XIGVrb{oV|XUd-I`1Z*&cGYo| z>v`ehRvUe))!ap2_c7T>e|;Y5FCL-3b{)g0);+v~#XMiFH#*9lu}`;buY<}wZ%R5m z-)YHm?{`av?%8OSRAMH6(DmKsqQ7Lf6`m#6>Xe2+%n9t4>taDyZ9vTS1Y~@PS2ap| zH+?L7|FZ2Oag}}K*xB=y(`U|~JUx5(6bs6Yv&TOA;>okejs|01XO8mJ;f6m8dLTfmk|M${bpo9>`JUw0cAK4=bSw$pMTs|~+YZFDqO3^G6;S)S@3x*B8} zZcE6OdPXjr)kQ^72vT*H7^aT#b}=LfUXbystsryNT?%s7St)LG<^~9edIIl!KR(_b zBt|i77Bg9M6#pPtn0b6>GvnqcXa%T+PJ0Klce2|tX=7R#ds(Us@sKfRGMagyEED3m)KY+0W5w@f{n1Yv^QHrV<;`L* zdlaPlwyFN+9b>_~mAm#Oc{bvVM$c~LT6z7QdRacf9QjYAw8!YGD&nclFB_ba_&3#^5obH=r=T>B-Ct+b;0JOK4< z9eS2smE{YP47vrfKPA+;YM(iL{)7!t9W-w$L@^w_(c8g4FVk`T#wIn^ORa;z4?dz^mb;ki< zgeU_#9nWt!AqG}eqMvLr(a)4wSEy-h1_Sbi4snAt4vkD{=; zgrXo0R@)0GI9-M)b`u(~2U$%xRFJEC&_S^>t@&1vC1?_>M+As6 zhGA|nN8j&mmU)}m-)PsMdmfxg6`l>U1P7K_z4gQ%A@o6kcaFVsJ}{QilpJa$S@mor zJ>$}bTjm{T4flk$@cW+m5|Hj1+!E2yEOh{7gCfLEkvBnhteM?G1|0AmGsx7MjvkJT zWN1IB;mt*L@N0PKtN0k*4Fmm;i!AFM!M_?GZw-p>IvKI&skOb$k#g1K_H&PJ_BwSWS|wGjOz!*7M|CtFhyl34?TlgN+aq9zEpz= z+g42^L4kDxs6mda3CQHRu!dw_x7sRB(3~ zcaPol`G9O7^~VKt2p^BCcb|=ttb>(ioUE`?Xp{KPIRy~3qIw%em#sqbgd?noDtpBE zt^hSP>ys6ys3w;zRmE43odbXqxFOJB|JH+EMqF-nzw0rZOJN|&0`ae+;_qO7TudQFmB+Q zAbTI^#c zDIhOpLtGcJZVt7o?_<)RHi5?v;$DfJWZPsQ-O;FMw&UnO2$t9h0!QZm2O@t$gbW1Z{|EnQYIKa z2}wei4+=+g2IB6=(6gWbLBhiTG-LQ4+)^K5(uZ;%Dxb##I9?5J3>gE2H7n#6>W8WL z4A2<@n}D`BUthpQ@kq7NsLp|M;-&bH`4~Dh??vu_2qP@D7I)R(K!Qp#?YS$7-f+%? zBIE+8_$YhBrD&MyN0BP>QHUIKHAo|r3Tu9_((AxGu>f5`Ta1=) zP_AIYP}@MVGJe(<)3u++gcj(pxKh++3sF$4+3tgMEYy71AK+6ECDT_^)XAZ>!u-o| z1vz;b3{j)5&{?9^^Y%z(-V@ITCKxOT7xbxk!4N$@Vj2jtQkq)II{+@(VxK9-vxEn_ z9`}`as$`mm!408NzOQ}?75u*ff_ev)ha**g8+rA&kOX;ct5JW4cm6JuA7k?4OgJ9( z6HNLzr)PQI)2~%OU?4w@mw+KF9oGlYLH=M50`wqw7N0r&LgmHt$M**LM!S8rv)Emy zpIz8@;L=5Ja^k}D#T&3rmQrvaBE`sX0vA0|(r8cC5+iv!)D}=Qs#)Ho_&H!-Rx@G~ zGo8Y9!#gB*q4cPu&JbB=b6QaS_Eoo4@49NHHZ%L~ zC|VB8%6sOtpmR4b?y%`nrdxpcRc}o{{vI4-?diRHpZf6hk6piTP7U->$0GVOJHkaU&Ou0N%|IM;FjrRZdsqSprXRM z|7P)4=99)JVN+yC53aJ{Cb0IyLXQOssJEQrTeRU1IVNZBR?ZoQx}5X6J!?TcPQ7d| zcU!zv>TTE`a|@^o^Tb+U{q(-jL+S8dwwDh}{v1p8@(T#cpi_ zp+BMp7{VJi>2F*qx&w21z1eBn&|Uo-^;)Q%YjbRRfE-EOi3zk?4H;Z1ROqR!upYV@ z*k#Y2ph2D;6vA1u>%M(ijJ;I{iznSX4O2aq7^w3KiZMsG2*}_r)MwOh%d)cP@C+J1 z)as~w+`Sq|M;&1FY!6f&J~GT3;I`;4PVe6H^b>^mJl=<)p(@qTUR;HlLcayWfo{+5 zL!E|Vc#EqG$|a2Kb4!pPZVGkRfTf*GtiQT)b~@@-R5389+KT&FZ^6=5cWijP;llMX z)qFi&HR_s$Yabv0a>1&R96D+fYiGZYJ2koS>I*x?7#-TwlW?A=JtXcIs)*qu)@hAe zp)rK8)>ZE84>7LO4*zD43$3exFr@zI`Va+qD>B{U9pg44w$6b{`^M5{O)g#qi8yEM z7G(m_1EK?iE37!@w---BjT8AyNEz-Hqm`g(oA8-Y-mA1#1qPJG&yu9ERl~GIRqSpb zSj~OAMDo29u@k8=>iKUW(}gqvS0#*@w@B{xp#SF5p^)6cns*02)egL$PUbEwdv{=U zfi*8fr^4L7jQX|k%`Z6xyfy3;`IcB2!+ycRH1r{Z zu2(X+S@*M@F805N*$Gy?*pk?iqvBE0 z!d9Mkj;bGwB-Us!q!)LEp#$^q41x>_t5g4g)r_1HSwr4e6s1e~AWIq+j4%a$(M%a+ zn%+WC2;EOKGilKT8Zc;k4P1B#G^rx1QvD=qNuVlx`7NiSJMnp9su(^@D=W?b!Pha7 z%jq_-@EL_EWEe&d>;0@X2F5&$>pY#1_}AU`fta0W=DfdI ze~~$^QT6wcAdbxNo=# zKgCE^&5uY&VnL_1zev#$t0mKI#sr)Mka&l6fi(lubhCPu^qY0}#fFfTg3&j`vm*G+ zLSa`@)sZ`=Zvb-fUnJ!USv`f@K8WgD#wRV2sxso1Ku3KO1R!(U>X{Ww@>$7eB%hOf zR`U6}aXW)jAWs-tVKxN|!hrEEiN>fmA^HoV7@={8YVjs|JMO>>qTM~+b$4Ke1~s!@ z=3P`yOIT~PpWh7;EK(P|6y3Kc04)kD>b5EWu;PWL30%L<-r!2S8wuu0RydKy$zICzm12$>HokA{v#4t z@BY2M^;^6p_l4c9zE5xdfP)gP9@JrclrG0n46i%&=NXb-zm_zseus^ULieAUla^Nw z)W$eQZMMmeu2ug9zxfKIqq`*fW(5ASGlVt z3Y=xvgmK_&oktuS<<>b{oUQm?FVWgYd4sdv*@5egP7w=YLj7xWz535ER{O9WQ5IVQ z&Ah(9lwM7Ux*!3NCtWa4O>evsNCIXYx8fBI8c~Dj0fDY+lfgD1N@`~)36*`l?%qfU zYv&3c;HQozT5ShC_1PhD+NWl_gXMYa!NX>cj-3Ov!^qmX@Mf4Ye<_KHpLw*=_mHG`3pZZl4ALx{L z{I(}RIU0i!9Y|p~$XDNLy9yURL%5AAVANMKp9WcsfRk0{*pTq5T&G3TvS86?mN(A` z3)nC#m5D?b#^Wx85C4MQ2(o~+l76WF!L~oJ7Yq>*ci&@)ZlthUo~AI0lR(Ll6CzPG zzR{QR01{J2QBc-P3q*X8FFnB**CBz=cTD?yuNwoNXqhc6MIXfivk8t)DMrtsNX0jC zxeGOFzwbl>awL6_VIep41&!DZjYW2w6$wQCTU_NIGbfB1al45M5>Q!t-zJvL5C|HLgHx zy$5|-W}^Gx8@oZJpoMybOcb+xYvPT^ZFR*{lFgeeJL0#VzcSL-wJjp&& z?>C@jt$II26?KUe{$x;)_#+HMqk*`~Ot1@!?lQfkN@n*XMd{vxVoi_98g+ zJlUOhVfQ8B-^!hbp-)(#cj57Um&A;qqm?P)$MY<+hp0SMaX-9M@1wT|h3k*e*2uyl?>6nl zY8?g((w}gV&e&(g79GiU6w;&hi>yeddW%8#(b+b|_)v(yFQjI$%|x%{!eV=oEh+)m zeK?82K1}1K7($kXXdpl+fr+|;)Z`Nj2_2!xhX|cb(c~kHAwicz`?Ix#J;j0sfRnaS zB+!7-i`nkWA+#njw#gK1k^rnj@e`xYa)8a-`c-<6;dzJvt0`R;qnt&Q1W^b35fJ4Z z<4g2!@b0A~gf4(d;l1S7(8nozadjw16DzCcqaxd2R^Ci;R1I7E{p;B84E zn$}PmCOIZVIYp>SpA$4GUcX`o$a63=s2!H0(P9DdMa5yn80mkoS!Q8N$`-STVAHuw zF^^IOc^9_F2WnMBOjfwcD%$tA42NTC$`u}Y9lWvU3~Y9ymjU$B5z8BP;lr(p{nJ@) zBI#M{NxE{g-3&G`o)64gV75VW6>0%CE#NB{bKJSkLM4nn#ZFAGGe2LynHa1{rK5mC zi{sP=C2b|k!mtFtFrQ%@Bc~NC60UO;f(aV=4QWKQK<_1(FT%ikogX@hwJzBjilzR@ z&ko_^kzwA?I^^cA2DQ~2KEY{AU!Qx5>P-4a0>s`}#Z!yYELIfo!0EIx*6;u-8VfPQLy6OyEU61=sD zjMmcO3CgO?WOVvkN4s=~K^cMnV2DqGA!e3v;sF0GK%|ASLpCD8Ha(5cu{p z{L83hwOb!=!&n>zgn@c!YWXWk<4D*^7?~(C=>fr#bgsV8s)FrEP&U1z;=qQ77T%iJ zSVMz`au+&qHuc}G)BZ#MD`+uy&&J+yxKmLbDJ;yl*dc*$a#gau}y3it~>F_`#}5Vu676 zwA;S=6+U<%{Q!hDSd+dLZi~>tb0XOX*{e4kgx5+fLXP@0iA(CUT&tw%Mt&5W5)=r$ zU>NZnlqwSa=-YRQEzuu!9$pJ)IF5{%Buw=*xuh}Vi)s_~DSN48?>}(hpxt&Z)Gr-aN=p2Q8YA^(@FqZ6 zMMt!@Oc4>so&brWs?k!9mhRdj6vX2NhQ%3tt&b{I=O$p9VwRr<5#9^(Rez zNmsF(0~jk_ z*MZ&|4>=rdSV=Gh-{VzmZ+EYUS%j-E8#sB`k8|K80!Egz4~x{mu;Xr}NC$_EH^p`@ ztW74K%!U&WcXrQ&YkLwKS)gRl!hxJTxi%IK`au~qL^_0ABk)9Km{${Rr=-VQLjAL_ ze{hQ535{BM7U6JFgR@8;|FiKdO2{|2Of5rZZPr1R@!$XXSRR$J0N9|ouG1e+bVtU- zCdCOks~E`d19p0 z0pr>zp2*fB&Jc(BOde zZl?k9j&uj8yp-QTw5ctiD2X3Y83#UKPW09>y__ik3guJpq3@wsi_HMsQ$hq7fSL$j zUna1Go}OR*5BO+l0cIlo=-_o1XWDUdbmh#w*`23kb$Z8YGh_C29X+6&H^>d z08q$rHMeYhV?(->;5X?X?w|xh$5;YyB6b4-XEdSa4}gnuhfOb!sEk4{-z&(`2)XV( zKqpjpG$<#oPTAM76)1EF(~kiTFDjWm0oI16++J7`bu8^LI0FSuGdumV_WYbWdHl2% z2pAefqDYSBs}4LN%08ZYDrX1`@VM^kK7;0)qSo~Zv-Y*;v}zJlXqin-xQd!^aYsQ! zm_e`9gS%;?y zJYv^|J?9Zj1NBoq2T_O`?@%oRnKISy;*I<1FyOu@_TSl0e5k~XCwsz&%ep}_#E)Qc z->!TIOIFTE$#h9(^g2m*N#E0*N>$6YiP)=IEiG5c-9{Ui?KXlD%10s1LO;V?UuNjy z@~fy%Tb*|#N28lhn4AeJ*`B1|@7E+bF*fJ*OMjSv(5mHirUt{Q`qnTa5fVC+2GO!r z63$_&1%iwGT)?#f83EV0L@a=(bHnY!15v2ti5oVBtPpOScsq3RC+Q~T(u*vqU}zD_4R8D%YBndPeU_}dMOrJJs$W>WcK@2 zOQjGK26}lt-9?09>T@2rm6K7uVD0#ya9!wOCu=(%k=uSZulM&z6G9>lboYi+gV+|t zx&aKKTD-?zY~z$ID*K4*2!q!V6tms2Wv_OrJGJ~x*bY+~wLP&rEUkqQSuR9(wGDVN zi;%=v&DQ&j-ovw?fa#E^Di%^Zkw7He%Briy*mJ_3%d)U5?D%A~!r#~Ntdq_bY@18~! zcr6F)8qF%$hY~q5+!?56;#UC|^_xs&rX>;9OJnRLr$fw;ISPXk5Q^{a$|TSMndMOy zcu|8~2)qV)BFch5-w%Q&;5`cP!XyyJwl-T#B-1HsWpaor!{cqp41p?I5SSp9-2rG{hSOGohWT1a}PSF9tRRuk_q7*jNL zAl6p$TL)@R4{AymW*i^(Wbr~gcsK~O$V@yU0! zhOhnLkj6yA=ap!~0eYk@W!deJu)BGr+f|7ta{M|T;Y2Y2F^ugLGelQpVM0p%4? zmHRQAW!WBB0bUNG1P%QM2|`XPL8xuhMcBNu*d?C-Gs|3K<-cKBgQkMegAa^~|F|wA zi;u^+j6pe_;KhCglO7U8+GB&$ZCnXIglY|i8J5v|unk?Cz|nZ8?jTa32Y%bL=CP8a zE71b8-R)=fW>ATm1KkGEMff-^xg@`f_1aa~covokDc@9pU05c-MlwJAe@P)PmSblD zIW6-~CYKNG6i%mx^WN?!%wiUU>~-u-5qFB%sI{AfGZ`&p&Sfw}t}3UyDCvQ$3Q;65~TA-DWE)pJXAuE|CN4k5o#!$shEcoZ! z{qO_P^aKZ`V*?b2fdntHZ=~0Qik`$0u;u8FvqL00f{!311SuK}h$5q6Qh?9|aS4Z{ zNZ;<9J~~5#38n0ba~HtDH7TdRzOCIUeQP(5Xv4lFmeo?uxfFXRnGXeJY#0N#MHt0( zdqNJ5NS$Ir%S~xO7?M?m#reO`arI|RXk!gbM-$`$pnM(A$^?p#tvn5?gQmD>R4JlG z7{=hCGakf>apRVngnWsWgcLmPs@0yr6v8+KxezH2@RPkVxR$6Nv-^2WDmKSogE1~L zr?#B~2bN%wI>Hk>6}Col4<;l(9h`dlDEX4WbQ=4`D3V-vQ}6?z5NS)rHt{w%s`fd+ zD$l(F{YvOE2&)-@C8UhC7@J-fBA9S3hrRL2i^AdI%Xr+=xaShM6mX2(Fm2T- zfkIcY46w04p;tf;V2qD;(M0n@%UffIu#2VAVuOO>%Npb3Wy>vYj2NIe&F7}e~>y&a(H zQGsu}zx%`f>V*@M2z2LL^i&r|Usr!nUx(QhUF=4pw<%q;U*y~F?=xY4MgET|+P4c} zc?-Q>^Fh5HVwa@U8;MS*bkDd%$vjVAz5c)TgFW`|aV=!nqBzyR%1fJ<&PaZp~kHP&YDlb`TBfiuhG3f(G;BpXf`8csbh&>2B)gHyWHIz>M zvBQV(@wOtlpYPr&I6OMX#NH0>Ff0aRfDo)w43j?!oxN)xW`uuuC&l{oPMw()*&{EB z`N?y*&_E)WVEn@sN@R!>1p|il0uHO=+9SCi+=R!K_&Fa3TFz%6ARmYDpQ#if;$|vC zWX?K@eOMc{e;-u`qv#mUzz8Q3rwmIs^?3(}0FC5WPBH3!Wq)kvJ2+S*$m?i1VX5zg z&Rj~FI^^v)nHzvT8~z+&PwTD$xO49xnXUCck3Gtp-A(;Q2tY8!4+t}-Xu{7Cd_28H zj0i=c{vQ07A+&H{t+wm{S|{N7z;l5X_Ms-AbprRZ0nvJOhXE1w43i({ zB>|+4FgeQP7!wZtorI55N3&DzxUcTVYQNBj1)M*Fu;3AZ31p(4M)I`m%piwN5VSgm zs1I|=3DUE_pJiiY!XO4#aa6{TQsYw~J&J-6`h6fs4*~JgpgiIFU^8~CRnc429)Y=t?O1Ka1|NE-_ zU5r4_@V5)R=*<_FUpR`z9%dK_I8nvNqwE&p@{r_k$__&FBxvu4 ztui)mx;P~op?LbIm{x|td*jZSvl1b34>;q_1NdI$ta4W48{0gcwfJ7+taH}md#$qp zv}mK^>C)Yg#;V~J#z&l06>jGg@0r`X21DToQ1?=)h4xo;WmZhvj! zBWLy_<_6)B2PwAUs3Dxf&YL=B@*sbipgZ~+&my{bI4IQRTz z{iig2~Po{R!cvU zgKg?O#|^)7Ait--L-`vN7 zB&_h%N=q=M|0mv)sKSwxa!+<(%CT_jukjhBec4CD=tms6yrNP;lw-}E!;hd~L;8?L z2vG*)9EdD8mf}zco}R5v@@3NvEPpOAuMc1hPwkf;{xPv;D~i&1!M%_?nu14HQ2JmL z8c)4m13wAOdmEPFR9Hz0rHJFGRHIaE-~&ow!JFO5qxy*6NOORbn1q3#C(O6hC}|s94Clh?_$RcuMdj@8c9DA@hu;3lC-k_GG(%nxd4U zj1w8S#juG%V|S;8r1U=7s}hj|DmX%QSdnhPK(J#2`9 zdf_WhJIw)LClDcHR^e9%@jEfvnk*XhdzfLZ4~i}=YK|z>&k+?zHA|8MC_R`TPS3u? zA9P5r4V$wHE7pFsE;TfuorqGf~Iuz!QtPn{oUVIi~x z^%(gz!0#fcy*69$Bte}MPde@r?4B~IYSGsI{D>kz#n-aI*OP)FVIlr7LL?qkTxF}4sv7~ewICq$App|BvB*()%L z{n{%%?fiWFNN5bgWXI;>a8D>WrkeITi}-Oi%JI>)qP@0X5 z1f}6bb1tsiroyu1+2I4hY2aftz^TWPykjQhH(_C2RP!RHKs)xHTE@TtRxxC`6@9?` z{d{~-=0s=vQPd?oQ(MAzqCx!Fhd!|!A{H)XGGDKqpWzc={msfd3li9iR1Fw4W&IG8J2T#wJz;^yL*XkWli zju>Boe2J6bGwjlQhx>fcB~Fk&o>+$WFid+XhjtnbzzvrRe7jF zF_=QTqA)l3({3ZYH_B%6_?ijW)uC3ZdBOiXW!&^0a1RB{>?Y} z2{_vH^$)w*%xz(^mC2(_wlUd`1W`=*wJ*FI4Bc!t)MAa<+($buer^W8?uak7gT==9 z^Itqo+Fyb`iz9|r^#;JV*uhU;!J(t%$HLT37R}?FEZk8OY-10TNhXX#!m(Rz%mYrM zdYo7Mxk$7(Bj5?f!YiIf${#z7o}w!JP~-^vi46jEbOK7I{)S4R%dXI!kDjA&v!Sb1 z!3w#MzGFid56{CWn9E83l;d^XutkGUrfTV=5 z3NjK|^TLk;^Q8I+!7Rdx@DCxABCH6l6k+8)bS5b7$8U<@XSfb>;dz`ZZQd_JLe`Qp z_j@zgi_$b@=FR4|3|wyJRF?WA@9AHG5XqX7HjX^&mq$5kcTQ`btdDOnW$7&1SOZ=& zo*mvZ_851TkD2R=@@;KCJPK(Y&OU2=WPDUV8yjYy#vrd#dZ!~|3@$*PRybDR?@#0{ z_zQ-{hIHw1oLz{W3nOC}^z|yVHH`Z7RrEdn(7HK&N2lx12Ks?|*&DXB0pIz|x{nz* TbZ^Hp#qse6hlj`4kB|Ou%sTt` literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20c0bf4dfc5933656364d51a974b58ee87eb55c1 GIT binary patch literal 42516 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 aAASeQ52l_$`7&yx@ppUiL}71X&;JEH0L<3_ literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecda7c71706b0acc52911ad578d8267579efe92f GIT binary patch literal 17537 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

      VKtx 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41bc527359de48608d00a403cbc79552b6779a30 GIT binary patch literal 11058 zcmbVSTZ|l6TCQ7H*YxyUeH&lmbgrBB+B05u$?kfMNn*#@tik|^lWZE6PEDVhnQr%V zw@=mBo~B2zJBB1^#j+7Afk0x65XcB2KpIo@Nd0z1n1k3mRr>k$%UPbAd zI(6>nKmYmfr^30pf`#9o-u|C({(@zF!ouip28EaK#1CxCQkJrt){b4VZRVY(Q*q?& zR$O^|6;Ix|N>1K>#g})!l9zW-3FJLfnUQy)Qc$k)nzK8_N)dCqD%YIbnXk<6EL0YD z7AuQ8OO++tioC<+=-5NMa$Gs#iPtRUtNa5?<)ftow{jBYK+T{$6P-eN1?7U8MR_(_ zLHRVwMKy==oRrU?Jg*i|UXcE0QC?I_C@)24(f?fZgmMn-=xkJIoR7}coywEl8{MMf zeoK|7-nCliJS%$okL@=WEa_pFw4*}}Ldqo*3rUd?jPUA2CLqgFU7dVbTv z$XjNl8KIWdu?JS=OLeRAf;z5FAYYDV4lhJsRwoZ^?E0cQrB+aTNjYy><q(@;X1x9>&PZBr)Z?UC-+1+YE$SrocB|~A3$?Je9jR(Zw>yze_R{&S zD5*+ibt8V8z+DlU(BWtOMsT-vgF-4w&Cr@6PM~0+(cWLLILGbo$uab< zW_zj;rP+^plFD^FF;7$3L|yljLaG0IJuW5NQArS3ij%Mzl{VX2>bB|tsg%L-%Z0(1 zE1gh>JA;-hrCXTciXiUF`x#X4mo}n$Ys<{6RwoDZkjy~lO6^TqGFz-vZw|slqmr%O z0@+f-ne#0&w}d2}>*#2+exLG=k}i*gOQyMc%yO_3-j0xoQ$M;7){FHDM|qH)tu%K} zgHO}k2D!Q%2qo!LZ1>HqvRLYawsxC|CWk`Mkcm=zbCSkfh+)dn_#6@|zi0=x@2uEG z{1x!{PSN)8T(G-K84q2jR9?lnvb|o;Q*IITYBeoXt2=GA+ho32t=`!Uo2Er1g=|5; zKxpRr0<9J`o7L(FNXux|qq4pPYex2#9lqodEDCH1(Ovf*5*(QBw4fnEToP<$Pa z2sX?1?e2-u)dv!Bz3k~S$HVNg^p}}%=*V>3kU_>JFQBtN5OfwW>8WMFsyUD)vDlE_jpA*d{d zx}h8mTj0e^WeoW9VkzFOZI{Bh6qZz&gr&`TGg^f}hCHmtGFz*ilv-`Q6E>TBn1T$M z)WalFrH#GPE;L+;w#CKL#%@y5U`SM;A(XL`Lj9)j-TS3`+jXqEL%kRo1p+`t*Beg1 zS)>JUq<=pHl$CmgIAfz3(@H26?VWt3Q;?PR(?vJL+Wu?#3C zoe8NrINv+m9!cEFcbBsxT^%$(9wn4G39K6EiTT7nutJm$!52i2rpPF}X|6_DAdD%T zt$zXaBP1+pY=);_rBtn=)ffvd#Ed-x&!EfW;h8)l14ExTXNVCIh|@sO&tTzAE-r|0 zy7L1>$e1s??%(sgbjz5#>k=r%X5`2>*c}(|EKwCRkdprp3v&Gb}I?%Em((N z#T=Suss2SK+>8DOli`^U4qnr0UB?qslm@n!^PFJDu?s#P*RiF&hNiKfi_%MYLr>>n&Kj-sFQQ)Pz_`N-ui;*n#|Of_w?}06;)9MV$@J6HK zjS~&{XO&axtU8Cf74-zp=X^RKZ6g)CUlW0nVGNR(Hi}r&=p8y)4#kc3}?x_qr24w>kePnm!6WJYQk?{#>QxD|f5ExqaVUsfN zQ6HFT27a*`s>UuoGj#lYLM8aLy8{Z(AR6g61{K8uJt%?d6@jW}Lt0}|W$!znLfHE~ z_^!0-VfVX-u!*4PyoU~~_guqo_7{d?o`T0*xm(X3)dYgnO2U={YpP(f>qT`bQu)~qH zxxSoD0U0HJ*e0SVMt9j_sAmL13w~l*eHL|pf+t(_iHSuiL2FdO2pw#;$5tm*40e74 z1)MO}2Nghv!^{Ca#AlSX@3x*tS?EJwPCy$yP!DPg324IPVGX<0To1Oif!o(_VLp8e z2~2tDDtIc0E41~V2u;y3vNf2v!ic}d?!o}mPTM2q{tR7WcD8WBJZX3zcwi(Po7nSI zJP&qiI1^qbWX=IF{m5BQ9lZgO@YDc^x0#!e3tvIq-{8qGJxO1p>cyZM!!p+$c-mCT z3C~#8{?Q6B?HoD};p;j$`^?*Y%YwIiVbt5D^wZzPU^o{8C{qs-L}l)V5XYC$WfJZe zQTMla`tAm#*~$dI2)VIxM7W?Ye`Ab6{>sEz<1qq_f|quSo`$`zV}ifOlVNXCq6+pf zcno(0f%1zuj4vShz(xd*o?Gw{z>#E2#ynARDO?WPytyZDZgl8CO3lQIm@6{2TTaItnI~{~5qx(tJiV?Pq%eEGr z53O42kTegcrF}0_|HRdd9trHi&$^eUU|#n;+|pG*1y|xwnU2}=N^Jq zP1`$+SUC+xuOLO-45@d+`rftuT+fT&=;aU>n^(>uO1<0`^)TYbki|Z7h>2m`UCie9 z@{Eh!xz@{bzB||MtcX?Qu8R`ZZvdcjUcZZ1>UP>4W7bLpLd;qUzSIvv!7Y^qg+%UB zbfjL=R_!z=K_&@Xqybz`qm6U5?RrxU2}@!QX&_O32=#x%__&0Gl9_HBb+9jg1>}8a zUdl!1h#i&9uwFhrX_J2w(}A}3;xy-$p`Hy!WXiQ({Je2gk%xy3WO))w(N&mmtzc0zoPg(16jsce#7$$#- z0-do2%#h#18SKL)L&?62D*$m5-s#!;3fwfbF{FRnWr?{{=$m-`%K=_{xn1Z~Tokyc zc~u|+5SNQ5JuAuYK~Twsq`U8!tHquZ|1b#-XB5;IMim;f4_$=&m!&Q@TG!*%qQ8FY z)1TpYvwKX;?b2ol7gb#;J*V-XRo-iB#K!xT<)TioIXQR3>c5U+>TlE$=uPw<+ghwC zyw0P3RqM96j$2y@)aO*RvAd;7Tl!ffnYlW!R8zY?A|^ya+(N%k@R)cxd=c8J%Sh*K z$38AmbI(4Gn(nD7r**`3=Q zs^e|+o;+eImjA_*!EtUn945RvRb&bvR5w5ck%H)lupKGpiP-%ATo)b{+$1rV3bx$Q%L7(HD6oIN*Mi$o*fp_8qu^Ai;e% z$sHg}koe$w3qb=1vLM&X(MfFN5A55P{&T6%K`sR1TyEbP)b!jw0{otbt3&^=z}XQj zLkQ?J0?N=OIptur+?{WmtG{*K1)M_TCI&G(f}#SO$mg2Wu4_zwo5_&f-B2mLjyjl3 zqzJhmlPPc$4~=2M4!?tz)Q%@HO#{{=rq3W_LEe+UY5C0KZxMXHY_FiE`{Z=O7^v7W z+87ha+n9G!AnOZ^li>)!J`lhtF$bhT3LHwz2I3ac91*}c6d)QR6gH&W7r_4BMqdC! zO!oya@4uj2iOicog;3ZKed9>eJ@m?ChY%$3Gc7#@R>mdZG|Y^}3Ci=+ z48jqE?-p04gfeVOmK-tj#E84dIuU1*h|U%fO{?KQ@W_oIj5xPA3xjQ0j5s&&f(5ZF z#tw&)Y#@e7FF?MrcI>MVzL_j@(nV2Bh)?Qh3+|BKgJr$fiQoiL4yQ#5c(gJYrAL4x zT8>UB)}bm(nOXomf9^`Me;L~ z0pR)-AijJ4?N+#fP&qF0=)xyhQ9zKs&uM;Blu?B!O5Mb$G;gMj8Phh#C~mrMqDAvq z^#@E|VDcf7vY+Pr;2VAuTU2;Z^gE-V6XYZ}&@YmNsAw!75eUS~kirHD@GVXeTp5_W zZSJq}8IjuE+1bP8JBAH@)SLrKdlLm%gg9$+6Wag815zIJ;Uu|3Pz?0}#ZJ%F>q#Co z6f|ZI3y8Qtp7*@!EQ}PdTI{)`D$)%_Q1|aXAH5{o7yH+^@pgN+sX$o$+h&}~;7Y82 z={lgO(T{fe#P)^gC)qZm*YyqHp*)8}xxKZU)SGFMiq#w=7+Vhbn2=XiXKW;MqH?x8 zRuG24xS@p+U$lr;eVqwO3}JFP>*rV@2Tsj1?fxQNFtk?%oeF)wNeZ9PZJuj(p6f^UMWEQeMQqfhmEdGAs9)e2vKroBj3%KS7f(7)+e_TWq5rZM;1} z+iW*;;4lrbnVzX`>UKA3_0IsGtx1>;@r-801^1=1uZzR?su=i7M|!7=Z^?%MH{npc zAva8Efq5fL()pL+Jl658K_nB1IM$T9hI0zPBA>O~0xI+zYyX%DkvqbaJcNGeVS$MPx`9ZPhwn`9%{yX%du)`6pSE$tt(8)n2il13tD zxc3evix{QF+GtXsiv(%WA}LT%KpS9-JfsgrfucZ*^r1!30zvvV4|!?(V4yEW`m_b& ze&=42615HzR5mZ~+`0evobP<+T>7PwtKsi^TmS2y|B|NtJ3So!v+(dLKH^76n8x&` z*3xUbPI;qg)C~D;)=c?r)hzj)tL5akU9*|VtY*ID)SOnKR%p337i~?JYmT*wwW7@1 z%~GpeE4RjLMC(}XSZlI2scV7JJ048!>$MXs-#fXl)lLOg?=*8za)uRZXPL{! z9&5F8!MWP`-_Tf*l^$!X6kK>@)-Ez*S*w(vVAfhi&+@PBCV{y7`qC4f3e2n^c10tO z*248{oCQhHNa{h%iNy)^BK#E+QYjHBS@^ll)gUwPTR;R_M8R-3yX+DGE%bcaU^qOlRQd@8-Wlp zzr`CHA#GBJV>WNU^m)9D{1);v0@-A@jqFYsz3|!T>dP*Azce@Jsh-z%y<{Ws{C2w; zHv9w&_5`+t(~vxPzZ3GHg&Bx>Z*Gp}mGF*8?nZ0zuert-rm5ASOUhHPSaHDa^Kcu7 zwjPL);yVxNSTK=$!FJH}nv~w0_Ql<3JKhTTwATr5HE7*8r`PTvcrtm@#jUKST2bYHuCY5r<^H`$1zomakkeeR3%?_Hh!s#kb#{%Ylw=ez%U*_*pM z=P40f@H)xb+)GsAhAomZ?A0I#$#vIw-125-TQTcEPKDR6G&8F*bvm4iaKkmPF7x%e zck`xqrCx9Oki`0x1=qtLdYbq7`u10^zvtb=2=k(|I?J!j&WQ#O+sV80?cJU9-TB~Q zfEix7=3T+(P@BrIli*>P%+AcNb)rT^%vLIHgVb#BXCgy50({ZL+enz6YTFuLNwpqO zWMAj6A*VB)nfu1O#-6@spoSr97^q?G>tcoFQk1P3C$&8-nv^Y7xt(fMmTJ)V{L;s| z*DX~$f>&3=s0xkqI1T@?KHbgF2#@5oYf)M;G8Zbw6R{IQVRHiVnE)7EtahNaCfa9X z(DOmbM;f1^-o9CVLqMUbjSWBY*JJ2u^>*CoNI|YP;-LDz4=oJlfDi!>RWXhpQdx&i zZ9TwPuRGZ-{Jwh;EnEzr`K+)#feq+mr;8xpVyGqExVqRsTNT{wl9f8xyCWGkx zPMidX=J*2b@&po1cXF;ip-<{%!_^&~pF~-MUL5|T-FX!sks%4Rn$9$Ux?VE_3m|R) zz^||zvmfiVJj*i&aO(txO}9733UqLN2req{t(91rjiXMP&9Y-`60OGBaW;jLiQrgm zlAT~Dkw4B(vD5gTVik6Vokgt^>>N9fl9RzHc7a`dtUc0er-Rd*XV`PYr?Z>qn73t! zzX>Q;*fRK!kvmUi{KxDPn@0N!>?*s=o=1-t*{9hI`xJ7|NmFnw8(WrA{S_Zd-mOd7 zz2!BU&{8jnp?K|GFXmo@2YwQG{XO=iqKUY}8wX`n%WpOb*p&vfgGRX4M>jDq%a&Fa z8#Z=y@JD$AAE^T$AOmUE>RM_b)l(BG$-GuKWojk4O`928Mtec#%q=6PT%O8M%Sj5% zdPMczG}nHRjHT+SC~N1?jt|>WF3(Vg+NGqtIgamfM%rR8EoQlPEW(GGwdrsBPzmj0 z^X>U}G9yYV6VMO2?m7#3=0a7-rcu7+Ggfccd9k(;P9*TODpwh0$ihb)?4`B)_c4mp7 zxyCP`0Vgo!&&j5Y@78%IA~Q{0Naa+?33~6;(L7A*^(8!s^GGzq*8BgADcyN$K{H4@ zCXwq-9TJfFLDL2)$lx!B%n-?5#Y0N?qyxhYek0X3b$%l;HqD-u>ias7%-pk5tCu4V zv~TQL@|}&-rYAksaR#}i%v|q;i=$@RZ(=qzZB;XMQ{Sa`}(OgJ22*{Ywi+Wk_ z&WvoyFKfxqplO4|d+T)N?0#L9C$y=w$%d}Iqp{qwb{)NtNOzl3{m8n} z-)&Gk-U_3@gDE5)B9yKM%sW7JFBG2N*Z7gvv*2_I3?{w4e93E~A7%4Y=brb3*4#BN zUs~!OTl67=jG!cL2az`;7D;R=pIN{ZmgQexzQZ}lWB%^zDq}a}^>t`We#hq#m}kX2 zbdbi{C*-+68X`7=W^*0miXsv%XXvi}S$SUF6CM>g%5O-C>wj~<3Q_yHag=snZ7Og_P& z@zG0(L4uCdc%+|#Sf0|L`R1m@EbQfrEVq}7EN1WJSU%Oa^>{J0(%hE5XQn3q8go*# zNe#3spq(9g%-yq7n~e?2i>I_N;0?-3>KkHT_Rm8ShqC)xCu}kgBE1o;Vqc};&U<$w zr2(Lb%>Wim1l}yz_*?LB;vI2~jNfRJVShUtVPk`|B#ILVcFNhM(hP_6Fl?97MJ zT&LNZXF>HF@YrBI8(UQ}hH*z!`-`YvZU)hMvN7yv(4i+g66k4TeZCcULlBVpn6Fp+ z`MC!9p?}!Ig&K z@}>T9xPrj^%9Sg09#=duM)jwkhub>+{HxQhAiH=4?!gt9qG{y$Wh9x2rSPkib6~9! z5(g;#fvFk;_efx&0nzQOtb~T(jbs+jDpObckxjPzhl7`cGMfjY(@c&6K)Jq33NWZm zE0i9XHs9%=j@akE|??;LnMI>WeT;5K`9x18F&Nopw#vIm2F{5$$C z;y3g!nPTIqeG;Vo1N|K>GOoj-Fn7!Rj?V9?mSkaurkq-SdDwCywNsr|bf~orOgjb& zo#lm8VbOIIEt(OTdFSmrnf=<`rQ3HOblpKQUgY1r^I#bv9%&Rl&PfkCGaJpYTbO%$ zVUdH8if)Oron})#N-6ga-dR*%@9LfF29rm=obw4uR~u`^v9bn}AHMV;JS} zsPoDRN!d8bRW=TDtrnxCmy?chGVw~IUZmXTsis3T>>$ddqF8m{2w$K&q;UMVC>f{Z z%aoAkm)SDJ6!5p?gIrONO}-I@SXywK78od6;=buOJ1XDTSF*k;GQl5Ee_x<3@_pa4 z&pZ3uc2pUQl5h53{wdnY2!N(reID+RB7$lbpW3F4JUrE>)-No}(u+n>cS}%I(qXd9?o)(t$n@NNfB^lUtJ&F<+DC8nL~4?V(y2(*c zWVYz6;ahU~iXjm(%gHd}(cPO+yC;x%9AN-~qIK(@dd-%i2f!QpGwDy3APxRgkRC8AKEN91J4xP`$CJ&e6#iY5NCF z{uAQ~iJ-#YA_q=lZlukDSc4w1HWjK)j#+uC>Z_a5vOF~_l95%VpjMZjn&wE>CWlBMOe zBwfVv-gr9R8z-RW|B;sWN}z=$1hh(B1i3aR0KuifJfhzP>`nqaANRrYcyEf#0)prT z*=9oad?KCL*D(%=XzwKD0nkS86#AK<^^EqK97jN1!g{xSP#-?mRDY~*sIe5Rt2AoT z31EaDrX~1cV`=V`Mk9(J!jzQKoD9;H`g6f5QS&EgaRxPhg7KzcXi7Ab_y1yaE_gbN zy8k0f&!wexX*A$KpQpW|`Z8=$UgGaTC^7@_FFncho$lBmu8ELX*QCUr5z^^cIPl7L z5OO|DD7`rkaeL4^;)ldr!ONO(7h3`7?Rwbgj-ii1QTO^S0)JwX%JV+NOci@P+MI7< zj@=ykm=WDG%UfZ)O=BTaDu`2hMAP6TRxZd;#vwO9Pj03N+I$rq@Vm6ql0<<)#Dx=j zae}YRk&6-u+#@K_Ho-O@8VVUGLGXnFn?Md_4rZy@G1;t+2&s<%3e6-d+jNA$6?}v0 z7yJ0QN-@!_Kr@hUGZPcdia3p7e2MQ-181;!U}pkvx|JE-wan>;?K<-jnz~Gt@^L4T z^V6eD$86aeJsm}*QeCZHP2mX|_AOEoz(jFUi~K`aAJ9aUTtx|$PvB_+-$Wb@6;nm6 zGD;{=;u^{lm5tMQK8X)K(|eq~gku?YoF5r`giw;XCsHY~?!S?|%^a}KCaeR-Xq$Ni*xSG_ zCsi2Y^vk|WWk3{qE-?3uCg~=j%vit1-^v=rgBr4C2{mCTNmD^ge=6sVXfQ%!KV=hp zE}Y(DKvtrw1yDXaq>^o;~yhIL+Q3~D)@a$hyh71lvy-QCDyi*DJ6|#>Oo2Uhg6-e4pbtelSSCZ zt)Q-|UBDUyjX}%^+y@_$tqCc2Hgfduv@CI+ItHYXVi%)q=TLVe5eo`>41JY9!axu$ zvv2!h^%Dfr9wC&rrw_|=G6!D1Pvzv-tWU&pzxC`BOU>Ruf5i;HE?XPdZa?T<3|GHDs9ksu6Ge#j306XccRTsq)` zN_%7^-lg|49N(dwM~O5rxhO!)PFa#~Qt1s!UZG?TN#;;Ml}nQvbwDC1P>n>^7P~pRdlsI zqid3{LRt!O5Y6ZwUp{2q`nNG7J_psKj~uaxjMOvSd6C4+QTbn@bm#>s4)csrTL+`^ z^Jp7MK(cqVJcaE-(b!eiqYb(<`H7t zpxZK1B5E*#xT}$L)=q#`6rlhjqE(^;3FBo86s# zBa)#5ut)ki2`FU$LXp%s^7?nZ(7=i#=}iZ&qJta8h6uX>U1E_9snHiz1Gz^LonezO8P~}k6b)k*$Y-@1Iqke9(favY;-bWQ7mOO}P z_k+T|2f+*JQiy(RF&xKx=AZP`M+fm{akylEhqF?FBL7!(HMmEvsObBE2tT8{)!kp! zRo`gH8*A*1y0dP^YRMIKElum`Z&LrBaKXFfQE&oxhHwVFcn#NA@&Zu>+T$jmZha8G zS`Pr(GN6B`JCHT{9TNI!!ndIw5Q6voRS`Ek@VF>aB}tG+ygNb6Jc$olFY)g|DiBX7 z;EVr=k{%VPE6uFbzpqn^P^?SxFUiD4VIs5W0KQ40OF*NI5p)7PL6*7`hjEIkeL_k} zsic%rIEbW_y!iJ{LQ28ErG#1(3MKRZK1c^-Qt$qFcqA+8=m#4xGHWu_ObLgw1wzRN z!h)@9!ycX`y0G*In>yO(!459`KlcA%Sls!;%fqQK{`Xk;VBgE5qaE7#zeBAfkdc*W zN9(xj!L>s(_&%KgA){u~2}~UUi;=n&b7gS};7|Nyse-_h`tR|-MnUGnub01Y0{9i{ z_4hk|vk!&&dY#1$6pt@|>A~ImEA^E-U%7wp*2*0op`i+^Rw?(akSHCqii^HQxuFk` z9jo8Ivv})`dn@&?+*-Q3cxQQqZ&Qt3N+fgWQI4b(R~r2bO2uewc);*k6ivEJwMdR6 zn97ai|3Hri0xNUm2Jllz?%*R{L;?$Mk^P62e(D}`@* zwqU|bH7{DuqhBF&93mPe7&XOFdKtf?) z5=7s+;0JO|`TGo|n3CxYBhpy`fPYWkUCV=#L=Qiq!o6IZ{}T<9r(xjL^FODN|B(_3 zIA+K3YssTOSjy~pm3|T>*YT_^%A6ZY3|=NBl~*~~m%%^$j-#lbM~v7x4)5G`CM@Is E05-=oJ^%m! literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a68f1672e8cb5cc98c574aa3cb29828fc898ef52 GIT binary patch literal 48238 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a1bb7a55e6ba27cccd9a1e31e8cae7a1466cb5c GIT binary patch literal 20343 zcmch9dvF{_df&WucCmO8e1f9L5k-nvl30R|J4rl>BE^Tqi-coHiYJ30my7KIxZq+J z>{&p<-a>U}%65FIoaH3G*e)dj6i=1P0@K^w(~qyazyA7r^%nd4vj#rDcK zCz!MhLl~2WFojhyCr$NjOd&M=|mdF(&dD-EMJAjN|(`_2#&5&FxvUCZ884@OKi=DPiI7OP{1C_lhrzmr-&b z=HnIdD(2%=HBTPWr^Q#0{)(EP7gS4UqL%iH*Th-WdQrSC&f)t2N}m^Rp!6H6_CcgC zh>J*HRJC71`jWVe^kubHhx|kSR)2^8+(R3&ef|sLP4U)8hDsgqU-EZ9w8fQA?8#Ao zRD5;BK=@bA7~*a54*JRo>$;JfXndHxGQUvq=lyEESTEPAUTwxJdNUROgYtC6_m+Ga zAh%qd^@7qp6!R8jZK*6&#^S<4P1e1cn)I$-x;l31)cDbC;MW%y>a|)W7%SE07tkh~ zDOQCS`18eTyYaKJ9s?;CVrn}#3gpVTMhL1l5Q?Z zS(9OQ)~^@zT$BiW`-j6ghbI_9;2YSk2DYj>X{*V$CAW2B-;I0az{7vB>Mgc9iKit$ zk#oXKp-?T(`-MW7EfnT!VzI(_U!m||u~>=TqzeU6D-{Zl3^|N;Rq*5&j=dXTQjV4G z6|2SBT2&N}U96QBxxC1%`NtjQJX{#-;PKL7vCvO$R=G)z$Vk0a7|`JzZk x*6}@SPP~ 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 ANdN!< literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a60ba33d4fc0ef36760464509cd5cd293e6fdc6d GIT binary patch literal 26427 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py new file mode 100644 index 0000000..f7dbf4c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py @@ -0,0 +1,6 @@ +"""Modules copied from Python 3 standard libraries, for internal use only. + +Individual classes and functions are found in d2._backport.misc. Intended +usage is to always import things missing from 3.1 from that module: the +built-in/stdlib objects will be used if found. +""" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78480226adf1682a3263350ac62f49700b95af6b GIT binary patch literal 519 zcmYjOv2GMG5cQ=YT9k$_m^Ko-7F-n+MHFdLfRGRkjpbd>-3{^Dj%W75{stYNz%Qlc z6X;08Y*s?CH1^E%%=pc-KRauTuAjG${?*2q-%Id(DQ4cQ@u_aXd^KV7*_@yFmwNFb zhbbb%kPBgeUCkqWn&praY=On6;A?4MI6-R8fm3#VA2|+qvEkwyD~It~Ll`H#=ejaKoL;Vz LUM-*dN6q{L9^k41 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8420ef473eb04b8f735068331c9dcc6291e7a95e GIT binary patch literal 1139 zcmZWo&2Q5%6t|tUNk6qod`OH#galG0wjgmrnh-FM;L;02h+Lx7cDrh46k`6@NfQat=(>kKVy5QAWc`@=5 z4f>1tNk$JVsf>=sQboC7`BBbhSrO49)0*oj6D%6f1U0!7dglj}hK2*rZqke%@N}$X znQA#xl-rPIMUjmP;KrH@Dj6SOMId0GI(o4HJLKd&*!l{PB~~HBRhyA_WQmsKI)dE1 zp~ci<`4!kCsOIT7hnpLWk7g6=@nfR`2(8J_5AiF_m5%8_CbEeXEQ_B?IxD#_pp-m* zn{C6f&5tXt@|C)p8?H@SDLLiT#4B-|i=(*6M@~GD;{MKqSm%asSAbGB;X1DJDo&5M zU{b{_*T(s#qcw(cnQOXJ&Fh*sU3ESXgu5#g2H1vsYltXzmi2PH1EmJU&6q(;Wblb= z5grbRZRa`{+GK)q)d4%)IWQv_AZ^s$NJtai)`OPn z!V|Vp8?6T>+gyzW2y<<(PPlPR1>SII3JgpxCbqRdulO^iq_VyN)`n>+vXZB%1JDIH z>EGI2S+`*8J`jZ4*oWOGJ`R5?XB*)CLC*$h$|OxwOQcq=Lrw!N0gacJRoyt-c~QDQ zqE4a4(6)x~;wq&Ml|=z1yT76CMSZ*NKQ>0?qnTN`UCOSWQr8|E?v|3x3jVMOl8%7* aB*Y;JJ%Tr2NH!V-hj4aD-zSiL7ykheauuoo literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd509013dde3faea66837860f3a788ca005cebba GIT binary patch literal 21713 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

      %9E#z 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 hl^h>I=_!dFEMy9~!tug5{f@k literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f415e17bd2f698de163bc7dc40c8c47241955666 GIT binary patch literal 16003 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 ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + The optional copy_function argument is a callable that will be used + to copy each file. It will be called with the source path and the + destination path as arguments. By default, copy2() is used, but any + function that supports the same signature (like copy()) can be used. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + os.makedirs(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if os.path.islink(srcname): + linkto = os.readlink(srcname) + if symlinks: + os.symlink(linkto, dstname) + else: + # ignore dangling symlink if the flag is on + if not os.path.exists(linkto) and ignore_dangling_symlinks: + continue + # otherwise let the copy occurs. copy2 will raise an error + copy_function(srcname, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore, copy_function) + else: + # Will raise a SpecialFileError for unsupported file types + copy_function(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as err: + errors.extend(err.args[0]) + except EnvironmentError as why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError as why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.extend((src, dst, str(why))) + if errors: + raise Error(errors) + +def rmtree(path, ignore_errors=False, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if onerror + is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If ignore_errors + is false and onerror is None, an exception is raised. + + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + try: + if os.path.islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError: + onerror(os.path.islink, path, sys.exc_info()) + # can't continue even if onerror hook returns + return + names = [] + try: + names = os.listdir(path) + except os.error: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + +def _basename(path): + # A basename() variant which first strips the trailing slash, if present. + # Thus we always get the last component of the path, even for directories. + return os.path.basename(path.rstrip(os.path.sep)) + +def move(src, dst): + """Recursively move a file or directory to another location. This is + similar to the Unix "mv" command. + + If the destination is a directory or a symlink to a directory, the source + is moved inside the directory. The destination path must not already + exist. + + If the destination already exists but is not a directory, it may be + overwritten depending on os.rename() semantics. + + If the destination is on our current filesystem, then rename() is used. + Otherwise, src is copied to the destination and then removed. + A lot more could be done here... A look at a mv.c shows a lot of + the issues this implementation glosses over. + + """ + real_dst = dst + if os.path.isdir(dst): + if _samefile(src, dst): + # We might be on a case insensitive filesystem, + # perform the rename anyway. + os.rename(src, dst) + return + + real_dst = os.path.join(dst, _basename(src)) + if os.path.exists(real_dst): + raise Error("Destination path '%s' already exists" % real_dst) + try: + os.rename(src, real_dst) + except OSError: + if os.path.isdir(src): + if _destinsrc(src, dst): + raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + copytree(src, real_dst, symlinks=True) + rmtree(src) + else: + copy2(src, real_dst) + os.unlink(src) + +def _destinsrc(src, dst): + src = abspath(src) + dst = abspath(dst) + if not src.endswith(os.path.sep): + src += os.path.sep + if not dst.endswith(os.path.sep): + dst += os.path.sep + return dst.startswith(src) + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None, logger=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", or None. + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_name' + ".tar", possibly plus + the appropriate compression extension (".gz", or ".bz2"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', None: ''} + compress_ext = {'gzip': '.gz'} + + if _BZ2_SUPPORTED: + tar_compression['bzip2'] = 'bz2' + compress_ext['bzip2'] = '.bz2' + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext: + raise ValueError("bad value for 'compress', or compression format not " + "supported : {0}".format(compress)) + + archive_name = base_name + '.tar' + compress_ext.get(compress, '') + archive_dir = os.path.dirname(archive_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # creating the tarball + if logger is not None: + logger.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + return archive_name + +def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): + # XXX see if we want to keep an external call here + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + from distutils.errors import DistutilsExecError + from distutils.spawn import spawn + try: + spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise ExecError("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename + +def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises ExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + archive_dir = os.path.dirname(base_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # If zipfile module is not available, try spawning an external 'zip' + # command. + try: + import zipfile + except ImportError: + zipfile = None + + if zipfile is None: + _call_external_zip(base_dir, zip_filename, verbose, dry_run) + else: + if logger is not None: + logger.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) + zip.close() + + return zip_filename + +_ARCHIVE_FORMATS = { + 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (_make_zipfile, [], "ZIP file"), + } + +if _BZ2_SUPPORTED: + _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file") + +def get_archive_formats(): + """Returns a list of supported formats for archiving and unarchiving. + + Each element of the returned sequence is a tuple (name, description) + """ + formats = [(name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items()] + formats.sort() + return formats + +def register_archive_format(name, function, extra_args=None, description=''): + """Registers an archive format. + + name is the name of the format. function is the callable that will be + used to create archives. If provided, extra_args is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_archive_formats() function. + """ + if extra_args is None: + extra_args = [] + if not isinstance(function, Callable): + raise TypeError('The %s object is not callable' % function) + if not isinstance(extra_args, (tuple, list)): + raise TypeError('extra_args needs to be a sequence') + for element in extra_args: + if not isinstance(element, (tuple, list)) or len(element) !=2: + raise TypeError('extra_args elements are : (arg_name, value)') + + _ARCHIVE_FORMATS[name] = (function, extra_args, description) + +def unregister_archive_format(name): + del _ARCHIVE_FORMATS[name] + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "bztar" + or "gztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + if logger is not None: + logger.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run, 'logger': logger} + + try: + format_info = _ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + if logger is not None: + logger.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename + + +def get_unpack_formats(): + """Returns a list of supported formats for unpacking. + + Each element of the returned sequence is a tuple + (name, extensions, description) + """ + formats = [(name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items()] + formats.sort() + return formats + +def _check_unpack_options(extensions, function, extra_args): + """Checks what gets registered as an unpacker.""" + # first make sure no other unpacker is registered for this extension + existing_extensions = {} + for name, info in _UNPACK_FORMATS.items(): + for ext in info[0]: + existing_extensions[ext] = name + + for extension in extensions: + if extension in existing_extensions: + msg = '%s is already registered for "%s"' + raise RegistryError(msg % (extension, + existing_extensions[extension])) + + if not isinstance(function, Callable): + raise TypeError('The registered function must be a callable') + + +def register_unpack_format(name, extensions, function, extra_args=None, + description=''): + """Registers an unpack format. + + `name` is the name of the format. `extensions` is a list of extensions + corresponding to the format. + + `function` is the callable that will be + used to unpack archives. The callable will receive archives to unpack. + If it's unable to handle an archive, it needs to raise a ReadError + exception. + + If provided, `extra_args` is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_unpack_formats() function. + """ + if extra_args is None: + extra_args = [] + _check_unpack_options(extensions, function, extra_args) + _UNPACK_FORMATS[name] = extensions, function, extra_args, description + +def unregister_unpack_format(name): + """Removes the pack format from the registry.""" + del _UNPACK_FORMATS[name] + +def _ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + +def _unpack_zipfile(filename, extract_dir): + """Unpack zip `filename` to `extract_dir` + """ + try: + import zipfile + except ImportError: + raise ReadError('zlib not supported, cannot unpack this archive.') + + if not zipfile.is_zipfile(filename): + raise ReadError("%s is not a zip file" % filename) + + zip = zipfile.ZipFile(filename) + try: + for info in zip.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + if not target: + continue + + _ensure_directory(target) + if not name.endswith('/'): + # file + data = zip.read(info.filename) + f = open(target, 'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + zip.close() + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +_UNPACK_FORMATS = { + 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), + 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), + 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") + } + +if _BZ2_SUPPORTED: + _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file") + +def _find_unpack_format(filename): + for name, info in _UNPACK_FORMATS.items(): + for extension in info[0]: + if filename.endswith(extension): + return name + return None + +def unpack_archive(filename, extract_dir=None, format=None): + """Unpack an archive. + + `filename` is the name of the archive. + + `extract_dir` is the name of the target directory, where the archive + is unpacked. If not provided, the current working directory is used. + + `format` is the archive format: one of "zip", "tar", or "gztar". Or any + other registered format. If not provided, unpack_archive will use the + filename extension and see if an unpacker was registered for that + extension. + + In case none is found, a ValueError is raised. + """ + if extract_dir is None: + extract_dir = os.getcwd() + + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] + except KeyError: + raise ValueError("Unknown unpack format '{0}'".format(format)) + + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) + if format is None: + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] + kwargs = dict(_UNPACK_FORMATS[format][2]) + func(filename, extract_dir, **kwargs) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg @@ -0,0 +1,84 @@ +[posix_prefix] +# Configuration directories. Some of these come straight out of the +# configure script. They are for implementing the other variables, not to +# be used directly in [resource_locations]. +confdir = /etc +datadir = /usr/share +libdir = /usr/lib +statedir = /var +# User resource directory +local = ~/.local/{distribution.name} + +stdlib = {base}/lib/python{py_version_short} +platstdlib = {platbase}/lib/python{py_version_short} +purelib = {base}/lib/python{py_version_short}/site-packages +platlib = {platbase}/lib/python{py_version_short}/site-packages +include = {base}/include/python{py_version_short}{abiflags} +platinclude = {platbase}/include/python{py_version_short}{abiflags} +data = {base} + +[posix_home] +stdlib = {base}/lib/python +platstdlib = {base}/lib/python +purelib = {base}/lib/python +platlib = {base}/lib/python +include = {base}/include/python +platinclude = {base}/include/python +scripts = {base}/bin +data = {base} + +[nt] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2_home] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[nt_user] +stdlib = {userbase}/Python{py_version_nodot} +platstdlib = {userbase}/Python{py_version_nodot} +purelib = {userbase}/Python{py_version_nodot}/site-packages +platlib = {userbase}/Python{py_version_nodot}/site-packages +include = {userbase}/Python{py_version_nodot}/Include +scripts = {userbase}/Scripts +data = {userbase} + +[posix_user] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[osx_framework_user] +stdlib = {userbase}/lib/python +platstdlib = {userbase}/lib/python +purelib = {userbase}/lib/python/site-packages +platlib = {userbase}/lib/python/site-packages +include = {userbase}/include +scripts = {userbase}/bin +data = {userbase} diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..b470a37 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,786 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Access to Python's configuration information.""" + +import codecs +import os +import re +import sys +from os.path import pardir, realpath +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + + +def is_python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + return True + return False + +_PYTHON_BUILD = is_python_build() + +_cfg_read = False + +def _ensure_cfg_read(): + global _cfg_read + if not _cfg_read: + from ..resources import finder + backport_package = __name__.rsplit('.', 1)[0] + _finder = finder(backport_package) + _cfgfile = _finder.find('sysconfig.cfg') + assert _cfgfile, 'sysconfig.cfg exists' + with _cfgfile.as_stream() as s: + _SCHEMES.readfp(s) + if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _SCHEMES.set(scheme, 'include', '{srcdir}/Include') + _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.') + + _cfg_read = True + + +_SCHEMES = configparser.RawConfigParser() +_VAR_REPL = re.compile(r'\{([^{]*?)\}') + +def _expand_globals(config): + _ensure_cfg_read() + if config.has_section('globals'): + globals = config.items('globals') + else: + globals = tuple() + + sections = config.sections() + for section in sections: + if section == 'globals': + continue + for option, value in globals: + if config.has_option(section, option): + continue + config.set(section, option, value) + config.remove_section('globals') + + # now expanding local variables defined in the cfg file + # + for section in config.sections(): + variables = dict(config.items(section)) + + def _replacer(matchobj): + name = matchobj.group(1) + if name in variables: + return variables[name] + return matchobj.group(0) + + for option, value in config.items(section): + config.set(section, option, _VAR_REPL.sub(_replacer, value)) + +#_expand_globals(_SCHEMES) + +_PY_VERSION = '%s.%s.%s' % sys.version_info[:3] +_PY_VERSION_SHORT = '%s.%s' % sys.version_info[:2] +_PY_VERSION_SHORT_NO_DOT = '%s%s' % sys.version_info[:2] +_PREFIX = os.path.normpath(sys.prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _subst_vars(path, local_vars): + """In the string `path`, replace tokens like {some.thing} with the + corresponding value from the map `local_vars`. + + If there is no corresponding value, leave the token unchanged. + """ + def _replacer(matchobj): + name = matchobj.group(1) + if name in local_vars: + return local_vars[name] + elif name in os.environ: + return os.environ[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, path) + + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _SCHEMES.items(scheme): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def format_value(value, vars): + def _replacer(matchobj): + name = matchobj.group(1) + if name in vars: + return vars[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, value) + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # what about 'os2emx', 'riscos' ? + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + return env_base + else: + return joinuser(base, "Python") + + if sys.platform == "darwin": + framework = get_config_var("PYTHONFRAMEWORK") + if framework: + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) + + if env_base: + return env_base + else: + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if (name.startswith('PY_') and + name[3:] in renamed_variables): + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['SO'] = '.pyd' + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_SCHEMES.sections())) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + # xxx see if we want a static list + return _SCHEMES.options('posix_prefix') + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + _ensure_cfg_read() + if expand: + return _expand_vars(scheme, vars) + else: + return dict(_SCHEMES.items(scheme)) + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # distutils2 module. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name in ('nt', 'os2'): + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + if sys.version >= '2.6': + _CONFIG_VARS['userbase'] = _getuserbase() + + if 'srcdir' not in _CONFIG_VARS: + _CONFIG_VARS['srcdir'] = _PROJECT_BASE + else: + _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if _PYTHON_BUILD and os.name == "posix": + base = _PROJECT_BASE + try: + cwd = os.getcwd() + except OSError: + cwd = None + if (not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) + _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _CONFIG_VARS[key] = flags + else: + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _CONFIG_VARS[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + CFLAGS = _CONFIG_VARS.get('CFLAGS', '') + m = re.search(r'-isysroot\s+(\S+)', CFLAGS) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-isysroot\s+\S+(\s|$)', ' ', flags) + _CONFIG_VARS[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + cfgvars = get_config_vars() + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if True: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + try: + m = re.search(r'ProductUserVisibleVersion\s*' + r'(.*?)', f.read()) + finally: + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + release = macver + osname = "macosx" + + if ((macrelease + '.') >= '10.4.' and + '-arch' in get_config_vars().get('CFLAGS', '').strip()): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall(r'-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r" % (archs,)) + + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + else: + machine = 'ppc' + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..d66d856 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel +# All rights reserved. +# +# 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. +# +from __future__ import print_function + +"""Read from and write to tar format archives. +""" + +__version__ = "$Revision$" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" +__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +import sys +import os +import stat +import errno +import time +import struct +import copy +import re + +try: + import grp, pwd +except ImportError: + grp = pwd = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +symlink_exception = (AttributeError, NotImplementedError) +try: + # WindowsError (1314) will be raised if the caller does not hold the + # SeCreateSymbolicLinkPrivilege privilege + symlink_exception += (WindowsError,) +except NameError: + pass + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins + +_open = builtins.open # Since 'open' is TarFile.open + +#--------------------------------------------------------- +# tar constants +#--------------------------------------------------------- +NUL = b"\0" # the null character +BLOCKSIZE = 512 # length of processing blocks +RECORDSIZE = BLOCKSIZE * 20 # length of records +GNU_MAGIC = b"ustar \0" # magic gnu tar string +POSIX_MAGIC = b"ustar\x0000" # magic posix tar string + +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field + +REGTYPE = b"0" # regular file +AREGTYPE = b"\0" # regular file +LNKTYPE = b"1" # link (inside tarfile) +SYMTYPE = b"2" # symbolic link +CHRTYPE = b"3" # character special device +BLKTYPE = b"4" # block special device +DIRTYPE = b"5" # directory +FIFOTYPE = b"6" # fifo special device +CONTTYPE = b"7" # contiguous file + +GNUTYPE_LONGNAME = b"L" # GNU tar longname +GNUTYPE_LONGLINK = b"K" # GNU tar longlink +GNUTYPE_SPARSE = b"S" # GNU tar sparse file + +XHDTYPE = b"x" # POSIX.1-2001 extended header +XGLTYPE = b"g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b"X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT + +#--------------------------------------------------------- +# tarfile constants +#--------------------------------------------------------- +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") + +# Fields from a pax header that are affected by hdrcharset. +PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) + +# Fields in a pax header that are numbers, all other fields +# are treated as strings. +PAX_NUMBER_FIELDS = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int +} + +#--------------------------------------------------------- +# Bits used in the mode field, values in octal. +#--------------------------------------------------------- +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o060000 # block device +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFIFO = 0o010000 # fifo + +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved + +TUREAD = 0o400 # read by owner +TUWRITE = 0o200 # write by owner +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group +TGWRITE = 0o020 # write by group +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other +TOWRITE = 0o002 # write by other +TOEXEC = 0o001 # execute/search by other + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name in ("nt", "ce"): + ENCODING = "utf-8" +else: + ENCODING = sys.getfilesystemencoding() + +#--------------------------------------------------------- +# Some useful functions +#--------------------------------------------------------- + +def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +def nts(s, encoding, errors): + """Convert a null-terminated bytes object to a string. + """ + p = s.find(b"\0") + if p != -1: + s = s[:p] + return s.decode(encoding, errors) + +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0o200): + try: + n = int(nts(s, "ascii", "strict") or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + else: + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, format=DEFAULT_FORMAT): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0o200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + else: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = bytearray() + for i in range(digits - 1): + s.insert(0, n & 0o377) + n >>= 8 + s.insert(0, 0o200) + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. + """ + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + if length == 0: + return + if length is None: + while True: + buf = src.read(16*1024) + if not buf: + break + dst.write(buf) + return + + BUFSIZE = 16 * 1024 + blocks, remainder = divmod(length, BUFSIZE) + for b in range(blocks): + buf = src.read(BUFSIZE) + if len(buf) < BUFSIZE: + raise IOError("end of file reached") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise IOError("end of file reached") + dst.write(buf) + return + +filemode_table = ( + ((S_IFLNK, "l"), + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((TUREAD, "r"),), + ((TUWRITE, "w"),), + ((TUEXEC|TSUID, "s"), + (TSUID, "S"), + (TUEXEC, "x")), + + ((TGREAD, "r"),), + ((TGWRITE, "w"),), + ((TGEXEC|TSGID, "s"), + (TSGID, "S"), + (TGEXEC, "x")), + + ((TOREAD, "r"),), + ((TOWRITE, "w"),), + ((TOEXEC|TSVTX, "t"), + (TSVTX, "T"), + (TOEXEC, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form + -rwxrwxrwx. + Used by TarFile.list() + """ + perm = [] + for table in filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + +class TarError(Exception): + """Base exception.""" + pass +class ExtractError(TarError): + """General exception for extract errors.""" + pass +class ReadError(TarError): + """Exception for unreadable tar archives.""" + pass +class CompressionError(TarError): + """Exception for unavailable compression methods.""" + pass +class StreamError(TarError): + """Exception for unsupported operations on stream-like TarFiles.""" + pass +class HeaderError(TarError): + """Base exception for header errors.""" + pass +class EmptyHeaderError(HeaderError): + """Exception for empty headers.""" + pass +class TruncatedHeaderError(HeaderError): + """Exception for truncated headers.""" + pass +class EOFHeaderError(HeaderError): + """Exception for end of file headers.""" + pass +class InvalidHeaderError(HeaderError): + """Exception for invalid headers.""" + pass +class SubsequentHeaderError(HeaderError): + """Exception for missing and invalid extended headers.""" + pass + +#--------------------------- +# internal stream interface +#--------------------------- +class _LowLevelFile(object): + """Low-level file object. Supports reading and writing. + It is used instead of a regular file object for streaming + access. + """ + + def __init__(self, name, mode): + mode = { + "r": os.O_RDONLY, + "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + }[mode] + if hasattr(os, "O_BINARY"): + mode |= os.O_BINARY + self.fd = os.open(name, mode, 0o666) + + def close(self): + os.close(self.fd) + + def read(self, size): + return os.read(self.fd, size) + + def write(self, s): + os.write(self.fd, s) + +class _Stream(object): + """Class that serves as an adapter between TarFile and + a stream-like object. The stream-like object only + needs to have a read() or write() method and is accessed + blockwise. Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin, + sys.stdout, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize): + """Construct a _Stream object. + """ + self._extfileobj = True + if fileobj is None: + fileobj = _LowLevelFile(name, mode) + self._extfileobj = False + + if comptype == '*': + # Enable transparent compression detection for the + # stream interface + fileobj = _StreamProxy(fileobj) + comptype = fileobj.getcomptype() + + self.name = name or "" + self.mode = mode + self.comptype = comptype + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b"" + self.pos = 0 + self.closed = False + + try: + if comptype == "gz": + try: + import zlib + except ImportError: + raise CompressionError("zlib module is not available") + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self._init_read_gz() + else: + self._init_write_gz() + + if comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + else: + self.cmp = bz2.BZ2Compressor() + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + def __del__(self): + if hasattr(self, "closed") and not self.closed: + self.close() + + def _init_write_gz(self): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, + -self.zlib.MAX_WBITS, + self.zlib.DEF_MEM_LEVEL, + 0) + timestamp = struct.pack(" self.bufsize: + self.fileobj.write(self.buf[:self.bufsize]) + self.buf = self.buf[self.bufsize:] + + def close(self): + """Close the _Stream object. No operation should be + done on it afterwards. + """ + if self.closed: + return + + if self.mode == "w" and self.comptype != "tar": + self.buf += self.cmp.flush() + + if self.mode == "w" and self.buf: + self.fileobj.write(self.buf) + self.buf = b"" + if self.comptype == "gz": + # The native zlib crc is an unsigned 32-bit integer, but + # the Python wrapper implicitly casts that to a signed C + # long. So, on a 32-bit box self.crc may "look negative", + # while the same crc on a 64-bit box may "look positive". + # To avoid irksome warnings from the `struct` module, force + # it to look positive on all boxes. + self.fileobj.write(struct.pack("= 0: + blocks, remainder = divmod(pos - self.pos, self.bufsize) + for i in range(blocks): + self.read(self.bufsize) + self.read(remainder) + else: + raise StreamError("seeking backwards is not allowed") + return self.pos + + def read(self, size=None): + """Return the next size number of bytes from the stream. + If size is not defined, return all bytes of the stream + up to EOF. + """ + if size is None: + t = [] + while True: + buf = self._read(self.bufsize) + if not buf: + break + t.append(buf) + buf = "".join(t) + else: + buf = self._read(size) + self.pos += len(buf) + return buf + + def _read(self, size): + """Return size bytes from the stream. + """ + if self.comptype == "tar": + return self.__read(size) + + c = len(self.dbuf) + while c < size: + buf = self.__read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") + self.dbuf += buf + c += len(buf) + buf = self.dbuf[:size] + self.dbuf = self.dbuf[size:] + return buf + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + self.buf += buf + c += len(buf) + buf = self.buf[:size] + self.buf = self.buf[size:] + return buf +# class _Stream + +class _StreamProxy(object): + """Small proxy class that enables transparent compression + detection for the Stream interface (mode 'r|*'). + """ + + def __init__(self, fileobj): + self.fileobj = fileobj + self.buf = self.fileobj.read(BLOCKSIZE) + + def read(self, size): + self.read = self.fileobj.read + return self.buf + + def getcomptype(self): + if self.buf.startswith(b"\037\213\010"): + return "gz" + if self.buf.startswith(b"BZh91"): + return "bz2" + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.name = getattr(self.fileobj, "name", None) + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = b"" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + x = len(self.buf) + while x < size: + raw = self.fileobj.read(self.blocksize) + if not raw: + break + data = self.bz2obj.decompress(raw) + self.buf += data + x += len(data) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) +# class _BZ2Proxy + +#------------------------ +# Extraction file object +#------------------------ +class _FileInFile(object): + """A thin wrapper around an existing file object that + provides a part of its data as an individual file + object. + """ + + def __init__(self, fileobj, offset, size, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + + if blockinfo is None: + blockinfo = [(0, size)] + + # Construct a map with data and zero blocks. + self.map_index = 0 + self.map = [] + lastpos = 0 + realpos = self.offset + for offset, size in blockinfo: + if offset > lastpos: + self.map.append((False, lastpos, offset, None)) + self.map.append((True, offset, offset + size, realpos)) + realpos += size + lastpos = offset + size + if lastpos < self.size: + self.map.append((False, lastpos, self.size, None)) + + def seekable(self): + if not hasattr(self.fileobj, "seekable"): + # XXX gzip.GzipFile and bz2.BZ2File + return True + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position): + """Seek to a position in the file. + """ + self.position = position + + def read(self, size=None): + """Read data from the file. + """ + if size is None: + size = self.size - self.position + else: + size = min(size, self.size - self.position) + + buf = b"" + while size > 0: + while True: + data, start, stop, offset = self.map[self.map_index] + if start <= self.position < stop: + break + else: + self.map_index += 1 + if self.map_index == len(self.map): + self.map_index = 0 + length = min(size, stop - self.position) + if data: + self.fileobj.seek(offset + (self.position - start)) + buf += self.fileobj.read(length) + else: + buf += NUL * length + size -= length + self.position += length + return buf +#class _FileInFile + + +class ExFileObject(object): + """File-like object for reading an archive member. + Is returned by TarFile.extractfile(). + """ + blocksize = 1024 + + def __init__(self, tarfile, tarinfo): + self.fileobj = _FileInFile(tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse) + self.name = tarinfo.name + self.mode = "r" + self.closed = False + self.size = tarinfo.size + + self.position = 0 + self.buffer = b"" + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def read(self, size=None): + """Read at most size bytes from the file. If size is not + present or None, read all data until EOF is reached. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + buf = b"" + if self.buffer: + if size is None: + buf = self.buffer + self.buffer = b"" + else: + buf = self.buffer[:size] + self.buffer = self.buffer[size:] + + if size is None: + buf += self.fileobj.read() + else: + buf += self.fileobj.read(size - len(buf)) + + self.position += len(buf) + return buf + + # XXX TextIOWrapper uses the read1() method. + read1 = read + + def readline(self, size=-1): + """Read one entire line from the file. If size is present + and non-negative, return a string with at most that + size, which may be an incomplete line. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + while True: + buf = self.fileobj.read(self.blocksize) + self.buffer += buf + if not buf or b"\n" in buf: + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + pos = len(self.buffer) + break + + if size != -1: + pos = min(size, pos) + + buf = self.buffer[:pos] + self.buffer = self.buffer[pos:] + self.position += len(buf) + return buf + + def readlines(self): + """Return a list with all remaining lines. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def tell(self): + """Return the current file position. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + return self.position + + def seek(self, pos, whence=os.SEEK_SET): + """Seek to a position in the file. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + if whence == os.SEEK_SET: + self.position = min(max(pos, 0), self.size) + elif whence == os.SEEK_CUR: + if pos < 0: + self.position = max(self.position + pos, 0) + else: + self.position = min(self.position + pos, self.size) + elif whence == os.SEEK_END: + self.position = max(min(self.size + pos, self.size), 0) + else: + raise ValueError("Invalid argument") + + self.buffer = b"" + self.fileobj.seek(self.position) + + def close(self): + """Close the file object. + """ + self.closed = True + + def __iter__(self): + """Get an iterator over the file's lines. + """ + while True: + line = self.readline() + if not line: + break + yield line +#class ExFileObject + +#------------------ +# Exported Classes +#------------------ +class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. + TarInfo objects are returned by TarFile.getmember(), + TarFile.getmembers() and TarFile.gettarinfo() and are + usually created internally. + """ + + __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", + "chksum", "type", "linkname", "uname", "gname", + "devmajor", "devminor", + "offset", "offset_data", "pax_headers", "sparse", + "tarfile", "_sparse_structs", "_link_target") + + def __init__(self, name=""): + """Construct a TarInfo object. name is the optional name + of the member. + """ + self.name = name # member name + self.mode = 0o644 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "" # user name + self.gname = "" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here + + self.sparse = None # sparse member information + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": self.name, + "mode": self.mode & 0o7777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": self.linkname, + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) + elif format == GNU_FORMAT: + return self.create_gnu_header(info, encoding, errors) + elif format == PAX_FORMAT: + return self.create_pax_header(info, encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self, info, encoding, errors): + """Return the object as a ustar header block. + """ + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT, encoding, errors) + + def create_gnu_header(self, info, encoding, errors): + """Return the object as a GNU header block sequence. + """ + info["magic"] = GNU_MAGIC + + buf = b"" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + + return buf + self._create_header(info, GNU_FORMAT, encoding, errors) + + def create_pax_header(self, info, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + if hname in pax_headers: + # The pax header has priority. + continue + + # Try to encode the string as ASCII. + try: + info[name].encode("ascii", "strict") + except UnicodeEncodeError: + pax_headers[hname] = info[name] + continue + + if len(info[name]) > length: + pax_headers[hname] = info[name] + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + if name in pax_headers: + # The pax header has priority. Avoid overflow. + info[name] = 0 + continue + + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = str(val) + info[name] = 0 + + # Create a pax extended header if necessary. + if pax_headers: + buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) + else: + buf = b"" + + return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + + @classmethod + def create_pax_global_header(cls, pax_headers): + """Return the object as a pax global header block sequence. + """ + return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format, encoding, errors): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), + stn(info.get("gname", ""), 32, encoding, errors), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type, encoding, errors): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name = name.encode(encoding, errors) + NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type, encoding): + """Return a POSIX.1-2008 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be strings. + """ + # Check if one of the fields contains surrogate characters and thereby + # forces hdrcharset=BINARY, see _proc_pax() for more information. + binary = False + for keyword, value in pax_headers.items(): + try: + value.encode("utf8", "strict") + except UnicodeEncodeError: + binary = True + break + + records = b"" + if binary: + # Put the hdrcharset field at the beginning of the header. + records += b"21 hdrcharset=BINARY\n" + + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + if binary: + # Try to restore the original byte representation of `value'. + # Needless to say, that the encoding must match the string. + value = value.encode(encoding, "surrogateescape") + else: + value = value.encode("utf8") + + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ + cls._create_payload(records) + + @classmethod + def frombuf(cls, buf, encoding, errors): + """Construct a TarInfo object from a 512 byte bytes object. + """ + if len(buf) == 0: + raise EmptyHeaderError("empty header") + if len(buf) != BLOCKSIZE: + raise TruncatedHeaderError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise EOFHeaderError("end of file header") + + chksum = nti(buf[148:156]) + if chksum not in calc_chksums(buf): + raise InvalidHeaderError("bad checksum") + + obj = cls() + obj.name = nts(buf[0:100], encoding, errors) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257], encoding, errors) + obj.uname = nts(buf[265:297], encoding, errors) + obj.gname = nts(buf[297:329], encoding, errors) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500], encoding, errors) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE + + # The old GNU sparse format occupies some of the unused + # space in the buffer for up to 4 sparse structures. + # Save the them for later processing in _proc_sparse(). + if obj.type == GNUTYPE_SPARSE: + pos = 386 + structs = [] + for i in range(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[482]) + origsize = nti(buf[483:495]) + obj._sparse_structs = (structs, isextended, origsize) + + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") + + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj + + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) + + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) + else: + return self._proc_builtin(tarfile) + + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset + + # Patch the TarInfo object with saved global + # header information. + self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors) + + return self + + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) + + # Fetch the next header and process it. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = nts(buf, tarfile.encoding, tarfile.errors) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = nts(buf, tarfile.encoding, tarfile.errors) + + return next + + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + # We already collected some sparse structures in frombuf(). + structs, isextended, origsize = self._sparse_structs + del self._sparse_structs + + # Collect sparse structures from extended header blocks. + while isextended: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in range(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset and numbytes: + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[504]) + self.sparse = structs + + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2008. + """ + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) + + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Check if the pax header contains a hdrcharset field. This tells us + # the encoding of the path, linkpath, uname and gname fields. Normally, + # these fields are UTF-8 encoded but since POSIX.1-2008 tar + # implementations are allowed to store them as raw binary strings if + # the translation to UTF-8 fails. + match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + if match is not None: + pax_headers["hdrcharset"] = match.group(1).decode("utf8") + + # For the time being, we don't care about anything other than "BINARY". + # The only other value that is currently allowed by the standard is + # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. + hdrcharset = pax_headers.get("hdrcharset") + if hdrcharset == "BINARY": + encoding = tarfile.encoding + else: + encoding = "utf8" + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. keyword and value are both UTF-8 encoded strings. + regex = re.compile(br"(\d+) ([^=]+)=") + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf8" as the encoding and "strict" + # as the error handler, but we better not take the risk. For + # example, GNU tar <= 1.23 is known to store filenames it cannot + # translate to UTF-8 as raw strings (unfortunately without a + # hdrcharset=BINARY header). + # We first try the strict standard encoding, and if that fails we + # fall back on the user's encoding and error handler. + keyword = self._decode_pax_field(keyword, "utf8", "utf8", + tarfile.errors) + if keyword in PAX_NAME_FIELDS: + value = self._decode_pax_field(value, encoding, tarfile.encoding, + tarfile.errors) + else: + value = self._decode_pax_field(value, "utf8", "utf8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Process GNU sparse information. + if "GNU.sparse.map" in pax_headers: + # GNU extended sparse format version 0.1. + self._proc_gnusparse_01(next, pax_headers) + + elif "GNU.sparse.size" in pax_headers: + # GNU extended sparse format version 0.0. + self._proc_gnusparse_00(next, pax_headers, buf) + + elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + # GNU extended sparse format version 1.0. + self._proc_gnusparse_10(next, pax_headers, tarfile) + + if self.type in (XHDTYPE, SOLARIS_XHDTYPE): + # Patch the TarInfo object with the extended header info. + next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) + next.offset = self.offset + + if "size" in pax_headers: + # If the extended header replaces the size field, + # we need to recalculate the offset where the next + # header starts. + offset = next.offset_data + if next.isreg() or next.type not in SUPPORTED_TYPES: + offset += next._block(next.size) + tarfile.offset = offset + + return next + + def _proc_gnusparse_00(self, next, pax_headers, buf): + """Process a GNU tar extended sparse header, version 0.0. + """ + offsets = [] + for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + offsets.append(int(match.group(1))) + numbytes = [] + for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + numbytes.append(int(match.group(1))) + next.sparse = list(zip(offsets, numbytes)) + + def _proc_gnusparse_01(self, next, pax_headers): + """Process a GNU tar extended sparse header, version 0.1. + """ + sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _proc_gnusparse_10(self, next, pax_headers, tarfile): + """Process a GNU tar extended sparse header, version 1.0. + """ + fields = None + sparse = [] + buf = tarfile.fileobj.read(BLOCKSIZE) + fields, buf = buf.split(b"\n", 1) + fields = int(fields) + while len(sparse) < fields * 2: + if b"\n" not in buf: + buf += tarfile.fileobj.read(BLOCKSIZE) + number, buf = buf.split(b"\n", 1) + sparse.append(int(number)) + next.offset_data = tarfile.fileobj.tell() + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _apply_pax_info(self, pax_headers, encoding, errors): + """Replace fields with supplemental information from a previous + pax extended or global header. + """ + for keyword, value in pax_headers.items(): + if keyword == "GNU.sparse.name": + setattr(self, "path", value) + elif keyword == "GNU.sparse.size": + setattr(self, "size", int(value)) + elif keyword == "GNU.sparse.realsize": + setattr(self, "size", int(value)) + elif keyword in PAX_FIELDS: + if keyword in PAX_NUMBER_FIELDS: + try: + value = PAX_NUMBER_FIELDS[keyword](value) + except ValueError: + value = 0 + if keyword == "path": + value = value.rstrip("/") + setattr(self, keyword, value) + + self.pax_headers = pax_headers.copy() + + def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors): + """Decode a single field from a pax record. + """ + try: + return value.decode(encoding, "strict") + except UnicodeDecodeError: + return value.decode(fallback_encoding, fallback_errors) + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE + + def isreg(self): + return self.type in REGULAR_TYPES + def isfile(self): + return self.isreg() + def isdir(self): + return self.type == DIRTYPE + def issym(self): + return self.type == SYMTYPE + def islnk(self): + return self.type == LNKTYPE + def ischr(self): + return self.type == CHRTYPE + def isblk(self): + return self.type == BLKTYPE + def isfifo(self): + return self.type == FIFOTYPE + def issparse(self): + return self.sparse is not None + def isdev(self): + return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) +# class TarInfo + +class TarFile(object): + """The TarFile Class provides an interface to tar archives. + """ + + debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) + + dereference = False # If true, add content of linked file to the + # tar file, else the link. + + ignore_zeros = False # If true, skips empty or invalid blocks and + # continues processing. + + errorlevel = 1 # If 0, fatal errors only appear in debug + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. + + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Encoding for 8-bit character strings. + + errors = None # Error handler for unicode conversion. + + tarinfo = TarInfo # The default TarInfo class to use. + + fileobject = ExFileObject # The default ExFileObject class to use. + + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to + read from an existing archive, 'a' to append data to an existing + file or 'w' to create a new file overwriting an existing one. `mode' + defaults to 'r'. + If `fileobj' is given, it is used for reading or writing data. If it + can be determined, `mode' is overridden by `fileobj's mode. + `fileobj' is not closed, when TarFile is closed. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + + if not fileobj: + if self.mode == "a" and not os.path.exists(name): + # Create nonexistent files in append mode. + self.mode = "w" + self._mode = "wb" + fileobj = bltn_open(name, self._mode) + self._extfileobj = False + else: + if name is None and hasattr(fileobj, "name"): + name = fileobj.name + if hasattr(fileobj, "mode"): + self._mode = fileobj.mode + self._extfileobj = True + self.name = os.path.abspath(name) if name else None + self.fileobj = fileobj + + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + self.errors = errors + + if pax_headers is not None and self.format == PAX_FORMAT: + self.pax_headers = pax_headers + else: + self.pax_headers = {} + + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # Init datastructures. + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = self.fileobj.tell() + # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added + + try: + if self.mode == "r": + self.firstmember = None + self.firstmember = self.next() + + if self.mode == "a": + # Move to the end of the archive, + # before the first empty block. + while True: + self.fileobj.seek(self.offset) + try: + tarinfo = self.tarinfo.fromtarfile(self) + self.members.append(tarinfo) + except EOFHeaderError: + self.fileobj.seek(self.offset) + break + except HeaderError as e: + raise ReadError(str(e)) + + if self.mode in "aw": + self._loaded = True + + if self.pax_headers: + buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy()) + self.fileobj.write(buf) + self.offset += len(buf) + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + #-------------------------------------------------------------------------- + # Below are the classmethods which act as alternate constructors to the + # TarFile class. The open() method is the only one that is needed for + # public use; it is the "super"-constructor and is able to select an + # adequate "sub"-constructor for a particular compression using the mapping + # from OPEN_METH. + # + # This concept allows one to subclass TarFile without losing the comfort of + # the super-constructor. A sub-constructor is registered and made available + # by adding it to the mapping in OPEN_METH. + + @classmethod + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + """Open a tar archive for reading, writing or appending. Return + an appropriate TarFile class. + + mode: + 'r' or 'r:*' open for reading with transparent compression + 'r:' open for reading exclusively uncompressed + 'r:gz' open for reading with gzip compression + 'r:bz2' open for reading with bzip2 compression + 'a' or 'a:' open for appending, creating the file if necessary + 'w' or 'w:' open for writing without compression + 'w:gz' open for writing with gzip compression + 'w:bz2' open for writing with bzip2 compression + + 'r|*' open a stream of tar blocks with transparent compression + 'r|' open an uncompressed stream of tar blocks for reading + 'r|gz' open a gzip compressed stream of tar blocks + 'r|bz2' open a bzip2 compressed stream of tar blocks + 'w|' open an uncompressed stream for writing + 'w|gz' open a gzip compressed stream for writing + 'w|bz2' open a bzip2 compressed stream for writing + """ + + if not name and not fileobj: + raise ValueError("nothing to open") + + if mode in ("r", "r:*"): + # Find out which *open() is appropriate for opening the file. + for comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + if fileobj is not None: + saved_pos = fileobj.tell() + try: + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: + if fileobj is not None: + fileobj.seek(saved_pos) + continue + raise ReadError("file could not be opened successfully") + + elif ":" in mode: + filemode, comptype = mode.split(":", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + # Select the *open() function according to + # given compression. + if comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + else: + raise CompressionError("unknown compression type %r" % comptype) + return func(name, filemode, fileobj, **kwargs) + + elif "|" in mode: + filemode, comptype = mode.split("|", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + if filemode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + stream = _Stream(name, filemode, comptype, fileobj, bufsize) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in "aw": + return cls.taropen(name, mode, fileobj, **kwargs) + + raise ValueError("undiscernible mode") + + @classmethod + def taropen(cls, name, mode="r", fileobj=None, **kwargs): + """Open uncompressed tar archive name for reading or writing. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + return cls(name, mode, fileobj, **kwargs) + + @classmethod + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open gzip compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + try: + import gzip + gzip.GzipFile + except (ImportError, AttributeError): + raise CompressionError("gzip module is not available") + + extfileobj = fileobj is not None + try: + fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) + except IOError: + if not extfileobj and fileobj is not None: + fileobj.close() + if fileobj is None: + raise + raise ReadError("not a gzip file") + except: + if not extfileobj and fileobj is not None: + fileobj.close() + raise + t._extfileobj = extfileobj + return t + + @classmethod + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open bzip2 compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'.") + + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + + if fileobj is not None: + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (IOError, EOFError): + fileobj.close() + raise ReadError("not a bzip2 file") + t._extfileobj = False + return t + + # All *open() methods are registered here. + OPEN_METH = { + "tar": "taropen", # uncompressed tar + "gz": "gzopen", # gzip compressed tar + "bz2": "bz2open" # bzip2 compressed tar + } + + #-------------------------------------------------------------------------- + # The public methods which TarFile provides: + + def close(self): + """Close the TarFile. In write-mode, two finishing zero blocks are + appended to the archive. + """ + if self.closed: + return + + if self.mode in "aw": + self.fileobj.write(NUL * (BLOCKSIZE * 2)) + self.offset += (BLOCKSIZE * 2) + # fill up the end with zero-blocks + # (like option -b20 for tar does) + blocks, remainder = divmod(self.offset, RECORDSIZE) + if remainder > 0: + self.fileobj.write(NUL * (RECORDSIZE - remainder)) + + if not self._extfileobj: + self.fileobj.close() + self.closed = True + + def getmember(self, name): + """Return a TarInfo object for member `name'. If `name' can not be + found in the archive, KeyError is raised. If a member occurs more + than once in the archive, its last occurrence is assumed to be the + most up-to-date version. + """ + tarinfo = self._getmember(name) + if tarinfo is None: + raise KeyError("filename %r not found" % name) + return tarinfo + + def getmembers(self): + """Return the members of the archive as a list of TarInfo objects. The + list has the same order as the members in the archive. + """ + self._check() + if not self._loaded: # if we want to obtain a list of + self._load() # all members, we first have to + # scan the whole archive. + return self.members + + def getnames(self): + """Return the members of the archive as a list of their names. It has + the same order as the list returned by getmembers(). + """ + return [tarinfo.name for tarinfo in self.getmembers()] + + def gettarinfo(self, name=None, arcname=None, fileobj=None): + """Create a TarInfo object for either the file `name' or the file + object `fileobj' (using os.fstat on its file descriptor). You can + modify some of the TarInfo's attributes before you add it using + addfile(). If given, `arcname' specifies an alternative name for the + file in the archive. + """ + self._check("aw") + + # When fileobj is given, replace name by + # fileobj's real name. + if fileobj is not None: + name = fileobj.name + + # Building the name of the member in the archive. + # Backward slashes are converted to forward slashes, + # Absolute paths are turned to relative paths. + if arcname is None: + arcname = name + drv, arcname = os.path.splitdrive(arcname) + arcname = arcname.replace(os.sep, "/") + arcname = arcname.lstrip("/") + + # Now, fill the TarInfo object with + # information specific for the file. + tarinfo = self.tarinfo() + tarinfo.tarfile = self + + # Use os.stat or os.lstat, depending on platform + # and if symlinks shall be resolved. + if fileobj is None: + if hasattr(os, "lstat") and not self.dereference: + statres = os.lstat(name) + else: + statres = os.stat(name) + else: + statres = os.fstat(fileobj.fileno()) + linkname = "" + + stmd = statres.st_mode + if stat.S_ISREG(stmd): + inode = (statres.st_ino, statres.st_dev) + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: + # Is it a hardlink to an already + # archived file? + type = LNKTYPE + linkname = self.inodes[inode] + else: + # The inode is added only if its valid. + # For win32 it is always 0. + type = REGTYPE + if inode[0]: + self.inodes[inode] = arcname + elif stat.S_ISDIR(stmd): + type = DIRTYPE + elif stat.S_ISFIFO(stmd): + type = FIFOTYPE + elif stat.S_ISLNK(stmd): + type = SYMTYPE + linkname = os.readlink(name) + elif stat.S_ISCHR(stmd): + type = CHRTYPE + elif stat.S_ISBLK(stmd): + type = BLKTYPE + else: + return None + + # Fill the TarInfo object with all + # information we can get. + tarinfo.name = arcname + tarinfo.mode = stmd + tarinfo.uid = statres.st_uid + tarinfo.gid = statres.st_gid + if type == REGTYPE: + tarinfo.size = statres.st_size + else: + tarinfo.size = 0 + tarinfo.mtime = statres.st_mtime + tarinfo.type = type + tarinfo.linkname = linkname + if pwd: + try: + tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0] + except KeyError: + pass + if grp: + try: + tarinfo.gname = grp.getgrgid(tarinfo.gid)[0] + except KeyError: + pass + + if type in (CHRTYPE, BLKTYPE): + if hasattr(os, "major") and hasattr(os, "minor"): + tarinfo.devmajor = os.major(statres.st_rdev) + tarinfo.devminor = os.minor(statres.st_rdev) + return tarinfo + + def list(self, verbose=True): + """Print a table of contents to sys.stdout. If `verbose' is False, only + the names of the members are printed. If it is True, an `ls -l'-like + output is produced. + """ + self._check() + + for tarinfo in self: + if verbose: + print(filemode(tarinfo.mode), end=' ') + print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid), end=' ') + if tarinfo.ischr() or tarinfo.isblk(): + print("%10s" % ("%d,%d" \ + % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + else: + print("%10d" % tarinfo.size, end=' ') + print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6], end=' ') + + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + + if verbose: + if tarinfo.issym(): + print("->", tarinfo.linkname, end=' ') + if tarinfo.islnk(): + print("link to", tarinfo.linkname, end=' ') + print() + + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): + """Add the file `name' to the archive. `name' may be any type of file + (directory, fifo, symbolic link, etc.). If given, `arcname' + specifies an alternative name for the file in the archive. + Directories are added recursively by default. This can be avoided by + setting `recursive' to False. `exclude' is a function that should + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. + """ + self._check("aw") + + if arcname is None: + arcname = name + + # Exclude pathnames. + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Skip if somebody tries to archive the archive... + if self.name is not None and os.path.abspath(name) == self.name: + self._dbg(2, "tarfile: Skipped %r" % name) + return + + self._dbg(1, name) + + # Create a TarInfo object from the file. + tarinfo = self.gettarinfo(name, arcname) + + if tarinfo is None: + self._dbg(1, "tarfile: Unsupported type %r" % name) + return + + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Append the tar header and data to the archive. + if tarinfo.isreg(): + f = bltn_open(name, "rb") + self.addfile(tarinfo, f) + f.close() + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in os.listdir(name): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is + given, tarinfo.size bytes are read from it and added to the archive. + You can create TarInfo objects using gettarinfo(). + On Windows platforms, `fileobj' should always be opened with mode + 'rb' to avoid irritation about the file size. + """ + self._check("aw") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size) + blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) + if remainder > 0: + self.fileobj.write(NUL * (BLOCKSIZE - remainder)) + blocks += 1 + self.offset += blocks * BLOCKSIZE + + self.members.append(tarinfo) + + def extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name) + directories.reverse() + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extract(self, member, path="", set_attrs=True): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) + except EnvironmentError as e: + if self.errorlevel > 0: + raise + else: + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be + a filename or a TarInfo object. If `member' is a regular file, a + file-like object is returned. If `member' is a link, a file-like + object is constructed from the link's target. If `member' is none of + the above, None is returned. + The file-like object is read-only and provides the following + methods: read(), readline(), readlines(), seek() and tell() + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg(): + return self.fileobject(self, tarinfo) + + elif tarinfo.type not in SUPPORTED_TYPES: + # If a member's type is unknown, it is treated as a + # regular file. + return self.fileobject(self, tarinfo) + + elif tarinfo.islnk() or tarinfo.issym(): + if isinstance(self.fileobj, _Stream): + # A small but ugly workaround for the case that someone tries + # to extract a (sym)link as a file-object from a non-seekable + # stream of tar blocks. + raise StreamError("cannot extract (sym)link as file object") + else: + # A (sym)link's file object is its target's file object. + return self.extractfile(self._find_link_target(tarinfo)) + else: + # If there's no data associated with the member (directory, chrdev, + # blkdev, etc.), return None instead of a file object. + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True): + """Extract the TarInfo object tarinfo to a physical + file called targetpath. + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing + # forward slashes to platform specific separators. + targetpath = targetpath.rstrip("/") + targetpath = targetpath.replace("/", os.sep) + + # Create all upper directories. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) + + if tarinfo.islnk() or tarinfo.issym(): + self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + else: + self._dbg(1, tarinfo.name) + + if tarinfo.isreg(): + self.makefile(tarinfo, targetpath) + elif tarinfo.isdir(): + self.makedir(tarinfo, targetpath) + elif tarinfo.isfifo(): + self.makefifo(tarinfo, targetpath) + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): + self.makelink(tarinfo, targetpath) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: + self.makefile(tarinfo, targetpath) + + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) + + #-------------------------------------------------------------------------- + # Below are the different file methods. They are called via + # _extract_member() when extract() is called. They can be replaced in a + # subclass to implement other functionality. + + def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + target = bltn_open(targetpath, "wb") + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size) + else: + copyfileobj(source, target, tarinfo.size) + target.seek(tarinfo.size) + target.truncate() + target.close() + + def makeunknown(self, tarinfo, targetpath): + """Make a file from a TarInfo object with an unknown type + at targetpath. + """ + self.makefile(tarinfo, targetpath) + self._dbg(1, "tarfile: Unknown file type %r, " \ + "extracted as regular file." % tarinfo.type) + + def makefifo(self, tarinfo, targetpath): + """Make a fifo called targetpath. + """ + if hasattr(os, "mkfifo"): + os.mkfifo(targetpath) + else: + raise ExtractError("fifo not supported by system") + + def makedev(self, tarinfo, targetpath): + """Make a character or block device called targetpath. + """ + if not hasattr(os, "mknod") or not hasattr(os, "makedev"): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: + mode |= stat.S_IFCHR + + os.mknod(targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. + """ + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): + os.symlink(tarinfo.linkname, targetpath) + else: + # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except symlink_exception: + if tarinfo.issym(): + linkpath = os.path.join(os.path.dirname(tarinfo.name), + tarinfo.linkname) + else: + linkpath = tarinfo.linkname + else: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") + + def chown(self, tarinfo, targetpath): + """Set owner of targetpath according to tarinfo. + """ + if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + try: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + g = tarinfo.gid + try: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + u = tarinfo.uid + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + if sys.platform != "os2emx": + os.chown(targetpath, u, g) + except EnvironmentError as e: + raise ExtractError("could not change owner") + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if hasattr(os, 'chmod'): + try: + os.chmod(targetpath, tarinfo.mode) + except EnvironmentError as e: + raise ExtractError("could not change mode") + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) + except EnvironmentError as e: + raise ExtractError("could not change modification time") + + #-------------------------------------------------------------------------- + def next(self): + """Return the next member of the archive as a TarInfo object, when + TarFile is opened for reading. Return None if there is no more + available. + """ + self._check("ra") + if self.firstmember is not None: + m = self.firstmember + self.firstmember = None + return m + + # Read the next block. + self.fileobj.seek(self.offset) + tarinfo = None + while True: + try: + tarinfo = self.tarinfo.fromtarfile(self) + except EOFHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + except InvalidHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + elif self.offset == 0: + raise ReadError(str(e)) + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) + except SubsequentHeaderError as e: + raise ReadError(str(e)) + break + + if tarinfo is not None: + self.members.append(tarinfo) + else: + self._loaded = True + + return tarinfo + + #-------------------------------------------------------------------------- + # Little helper methods: + + def _getmember(self, name, tarinfo=None, normalize=False): + """Find an archive member by name from bottom to top. + If tarinfo is given, it is used as the starting point. + """ + # Ensure that all members have been loaded. + members = self.getmembers() + + # Limit the member search list up to tarinfo. + if tarinfo is not None: + members = members[:members.index(tarinfo)] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while True: + tarinfo = self.next() + if tarinfo is None: + break + self._loaded = True + + def _check(self, mode=None): + """Check if TarFile is still open, and if the operation's mode + corresponds to TarFile's mode. + """ + if self.closed: + raise IOError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) + + def _find_link_target(self, tarinfo): + """Find the target member of a symlink or hardlink member in the + archive. + """ + if tarinfo.issym(): + # Always search the entire archive. + linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + limit = None + else: + # Search the archive before the link, because a hard link is + # just a reference to an already archived file. + linkname = tarinfo.linkname + limit = tarinfo + + member = self._getmember(linkname, tarinfo=limit, normalize=True) + if member is None: + raise KeyError("linkname %r not found" % linkname) + return member + + def __iter__(self): + """Provide an iterator object. + """ + if self._loaded: + return iter(self.members) + else: + return TarIter(self) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print(msg, file=sys.stderr) + + def __enter__(self): + self._check() + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.close() + else: + # An exception occurred. We must not call close() because + # it would try to write end-of-archive blocks and padding. + if not self._extfileobj: + self.fileobj.close() + self.closed = True +# class TarFile + +class TarIter(object): + """Iterator Class. + + for tarinfo in TarFile(...): + suite... + """ + + def __init__(self, tarfile): + """Construct a TarIter object. + """ + self.tarfile = tarfile + self.index = 0 + def __iter__(self): + """Return iterator object. + """ + return self + + def __next__(self): + """Return the next item using TarFile's next() method. + When all members have been read, set TarFile as _loaded. + """ + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will cause TarIter to stop prematurely. + if not self.tarfile._loaded: + tarinfo = self.tarfile.next() + if not tarinfo: + self.tarfile._loaded = True + raise StopIteration + else: + try: + tarinfo = self.tarfile.members[self.index] + except IndexError: + raise StopIteration + self.index += 1 + return tarinfo + + next = __next__ # for Python 2.x + +#-------------------- +# exported functions +#-------------------- +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + """ + try: + t = open(name) + t.close() + return True + except TarError: + return False + +bltn_open = open +open = TarFile.open diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..c316fd9 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py @@ -0,0 +1,1120 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import absolute_import + +import os +import re +import sys + +try: + import ssl +except ImportError: # pragma: no cover + ssl = None + +if sys.version_info[0] < 3: # pragma: no cover + from StringIO import StringIO + string_types = basestring, + text_type = unicode + from types import FileType as file_type + import __builtin__ as builtins + import ConfigParser as configparser + from ._backport import shutil + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype) + + def quote(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return _quote(s) + + import urllib2 + from urllib2 import (Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib2 import HTTPSHandler + import httplib + import xmlrpclib + import Queue as queue + from HTMLParser import HTMLParser + import htmlentitydefs + raw_input = raw_input + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + + _userprog = None + def splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + global _userprog + if _userprog is None: + import re + _userprog = re.compile('^(.*)@(.*)$') + + match = _userprog.match(host) + if match: return match.group(1, 2) + return None, host + +else: # pragma: no cover + from io import StringIO + string_types = str, + text_type = str + from io import TextIOWrapper as file_type + import builtins + import configparser + import shutil + from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + unquote, urlsplit, urlunsplit, splittype) + from urllib.request import (urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib.request import HTTPSHandler + from urllib.error import HTTPError, URLError, ContentTooShortError + import http.client as httplib + import urllib.request as urllib2 + import xmlrpc.client as xmlrpclib + import queue + from html.parser import HTMLParser + import html.entities as htmlentitydefs + raw_input = input + from itertools import filterfalse + filter = filter + +try: + from ssl import match_hostname, CertificateError +except ImportError: # pragma: no cover + class CertificateError(ValueError): + pass + + + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + parts = dn.split('.') + leftmost, remainder = parts[0], parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +try: + from types import SimpleNamespace as Container +except ImportError: # pragma: no cover + class Container(object): + """ + A generic container for when multiple values need to be returned + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +try: + from shutil import which +except ImportError: # pragma: no cover + # Implementation from Python 3.3 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# ZipFile is a context manager in 2.7, but not in 2.6 + +from zipfile import ZipFile as BaseZipFile + +if hasattr(BaseZipFile, '__enter__'): # pragma: no cover + ZipFile = BaseZipFile +else: # pragma: no cover + from zipfile import ZipExtFile as BaseZipExtFile + + class ZipExtFile(BaseZipExtFile): + def __init__(self, base): + self.__dict__.update(base.__dict__) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + class ZipFile(BaseZipFile): + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + def open(self, *args, **kwargs): + base = BaseZipFile.open(self, *args, **kwargs) + return ZipExtFile(base) + +try: + from platform import python_implementation +except ImportError: # pragma: no cover + def python_implementation(): + """Return a string identifying the Python implementation.""" + if 'PyPy' in sys.version: + return 'PyPy' + if os.name == 'java': + return 'Jython' + if sys.version.startswith('IronPython'): + return 'IronPython' + return 'CPython' + +try: + import sysconfig +except ImportError: # pragma: no cover + from ._backport import sysconfig + +try: + callable = callable +except NameError: # pragma: no cover + from collections.abc import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode + fsdecode = os.fsdecode +except AttributeError: # pragma: no cover + # Issue #99: on some systems (e.g. containerised), + # sys.getfilesystemencoding() returns None, and we need a real value, + # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and + # sys.getfilesystemencoding(): the return value is "the user’s preference + # according to the result of nl_langinfo(CODESET), or None if the + # nl_langinfo(CODESET) failed." + _fsencoding = sys.getfilesystemencoding() or 'utf-8' + if _fsencoding == 'mbcs': + _fserrors = 'strict' + else: + _fserrors = 'surrogateescape' + + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, text_type): + return filename.encode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + + def fsdecode(filename): + if isinstance(filename, text_type): + return filename + elif isinstance(filename, bytes): + return filename.decode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + +try: + from tokenize import detect_encoding +except ImportError: # pragma: no cover + from codecs import BOM_UTF8, lookup + import re + + cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + + def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argument, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + +# For converting & <-> & etc. +try: + from html import escape +except ImportError: + from cgi import escape +if sys.version_info[:2] < (3, 4): + unescape = HTMLParser().unescape +else: + from html import unescape + +try: + from collections import ChainMap +except ImportError: # pragma: no cover + from collections import MutableMapping + + try: + from reprlib import recursive_repr as _recursive_repr + except ImportError: + def _recursive_repr(fillvalue='...'): + ''' + Decorator to make a repr function return fillvalue for a recursive + call + ''' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + + class ChainMap(MutableMapping): + ''' A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + ''' + + def __init__(self, *maps): + '''Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + ''' + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + @_recursive_repr() + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + 'Create a ChainMap with a single dict created from the iterable.' + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self): # like Django's Context.push() + 'New ChainMap with a new dict followed by all previous maps.' + return self.__class__({}, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + 'New ChainMap from maps[1:].' + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + 'Clear maps[0], leaving maps[1:] intact.' + self.maps[0].clear() + +try: + from importlib.util import cache_from_source # Python >= 3.4 +except ImportError: # pragma: no cover + try: + from imp import cache_from_source + except ImportError: # pragma: no cover + def cache_from_source(path, debug_override=None): + assert path.endswith('.py') + if debug_override is None: + debug_override = __debug__ + if debug_override: + suffix = 'c' + else: + suffix = 'o' + return path + suffix + +try: + from collections import OrderedDict +except ImportError: # pragma: no cover +## {{{ http://code.activestate.com/recipes/576693/ (r9) +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. + try: + from thread import get_ident as _get_ident + except ImportError: + from dummy_thread import get_ident as _get_ident + + try: + from _abcoll import KeysView, ValuesView, ItemsView + except ImportError: + pass + + + class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running=None): + 'od.__repr__() <==> repr(od)' + if not _repr_running: _repr_running = {} + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + +try: + from logging.config import BaseConfigurator, valid_ident +except ImportError: # pragma: no cover + IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + + def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + + + # The ConvertingXXX classes are wrappers around standard Python containers, + # and they serve to convert any suitable values in the container. The + # conversion converts base dicts, lists and tuples to their wrapped + # equivalents, whereas strings which match a conversion format are converted + # appropriately. + # + # Each wrapper should have a configurator attribute holding the actual + # configurator to use for conversion. + + class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = staticmethod(__import__) + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + #print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + #rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, string_types): + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/database.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..0a90c30 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/database.py @@ -0,0 +1,1339 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + if self._include_dist and entry.endswith(DISTINFO_EXT): + possible_filenames = [METADATA_FILENAME, + WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME] + for metadata_filename in possible_filenames: + metadata_path = posixpath.join(entry, metadata_filename) + pydist = finder.find(metadata_path) + if pydist: + break + else: + continue + + with contextlib.closing(pydist.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if version is not None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + # We hit a problem on Travis where enum34 was installed and doesn't + # have a provides attribute ... + if not hasattr(dist, 'provides'): + logger.debug('No "provides": %s', dist) + else: + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + self.download_urls = set() + self.digests = {} + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + md = self.metadata + logger.debug('Getting requirements from metadata %r', md.todict()) + reqts = getattr(md, req_attr) + return set(md.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.modules = [] + self.finder = finder = resources.finder_for_path(path) + if finder is None: + raise ValueError('finder unavailable for %s' % path) + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for Wheel 0.23 support + if r is None: + r = finder.find(WHEEL_METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find(LEGACY_METADATA_FILENAME) + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + r = finder.find('REQUESTED') + self.requested = r is not None + p = os.path.join(path, 'top_level.txt') + if os.path.exists(p): + with open(p, 'rb') as f: + data = f.read().decode('utf-8') + self.modules = data.splitlines() + + def __repr__(self): + return '' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + tl_path = tl_data = None + if path.endswith('.egg'): + if os.path.isdir(path): + p = os.path.join(path, 'EGG-INFO') + meta_path = os.path.join(p, 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(p, 'requires.txt') + tl_path = os.path.join(p, 'top_level.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + path = os.path.join(path, 'PKG-INFO') + tl_path = os.path.join(path, 'top_level.txt') + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + # look for top-level modules in top_level.txt, if present + if tl_data is None: + if tl_path is not None and os.path.exists(tl_path): + with open(tl_path, 'rb') as f: + tl_data = f.read().decode('utf-8') + if not tl_data: + tl_data = [] + else: + tl_data = tl_data.splitlines() + self.modules = tl_data + return metadata + + def __repr__(self): + return '' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = [] # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop()[0] + req.append(d) + for pred in graph.adjacency_list[d]: + if pred not in req: + todo.append(pred) + + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Placeholder for summary' + return Distribution(md) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/index.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..7a87cdc --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/index.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: + from dummy_threading import Thread + +from . import DistlibException +from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types) +from .util import cached_property, zip_dir, ServerProxy + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + with open(os.devnull, 'w') as sink: + # Use gpg by default rather than gpg2, as gpg2 insists on + # prompting for passwords + for s in ('gpg', 'gpg2'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from distutils.core import Distribution + from distutils.config import PyPIRCCommand + d = Distribution() + return PyPIRCCommand(d) + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils, getting + PyPI to do the actual work. This populates ``username``, ``password``, + ``realm`` and ``url`` attributes from the configuration. + """ + # get distutils to do the work + c = self._get_pypirc_command() + c.repository = self.url + cfg = c._read_pypirc() + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + + Again, distutils is used to do the actual work. + """ + self.check_credentials() + # get distutils to do the work + c = self._get_pypirc_command() + c._store_pypirc(self.username, self.password) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password, + keystore=None): + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password, keystore=None): + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password, + keystore) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password, + keystore) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protocol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename, + keystore=None): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename, + keystore=None): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename, + keystore) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) + + def search(self, terms, operator=None): + if isinstance(terms, string_types): + terms = {'name': terms} + rpc_proxy = ServerProxy(self.url, timeout=3.0) + try: + return rpc_proxy.search(terms, operator or 'and') + finally: + rpc_proxy('close')() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..12a1d06 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py @@ -0,0 +1,1302 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# + +import gzip +from io import BytesIO +import json +import logging +import os +import posixpath +import re +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import zlib + +from . import DistlibException +from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, string_types, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, text_type, + Request, HTTPError, URLError) +from .database import Distribution, DistributionPath, make_dist +from .metadata import Metadata, MetadataInvalidError +from .util import (cached_property, parse_credentials, ensure_slash, + split_filename, get_project_data, parse_requirement, + parse_name_and_version, ServerProxy, normalize_name) +from .version import get_scheme, UnsupportedVersionError +from .wheel import Wheel, is_compatible + +logger = logging.getLogger(__name__) + +HASHER_HASH = re.compile(r'^(\w+)=([a-f0-9]+)') +CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) +HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') +DEFAULT_INDEX = 'https://pypi.org/pypi' + +def get_all_distribution_names(url=None): + """ + Return all distribution names known by an index. + :param url: The URL of the index. + :return: A list of all known distribution names. + """ + if url is None: + url = DEFAULT_INDEX + client = ServerProxy(url, timeout=3.0) + try: + return client.list_packages() + finally: + client('close')() + +class RedirectHandler(BaseRedirectHandler): + """ + A class to work around a bug in some Python 3.2.x releases. + """ + # There's a bug in the base version for some 3.2.x + # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header + # returns e.g. /abc, it bails because it says the scheme '' + # is bogus, when actually it should use the request's + # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): + # Some servers (incorrectly) return multiple Location headers + # (so probably same goes for URI). Use first header. + newurl = None + for key in ('location', 'uri'): + if key in headers: + newurl = headers[key] + break + if newurl is None: # pragma: no cover + return + urlparts = urlparse(newurl) + if urlparts.scheme == '': + newurl = urljoin(req.get_full_url(), newurl) + if hasattr(headers, 'replace_header'): + headers.replace_header(key, newurl) + else: + headers[key] = newurl + return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, + headers) + + http_error_301 = http_error_303 = http_error_307 = http_error_302 + +class Locator(object): + """ + A base class for locators - things that locate distributions. + """ + source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') + binary_extensions = ('.egg', '.exe', '.whl') + excluded_extensions = ('.pdf',) + + # A list of tags indicating which wheels you want to match. The default + # value of None matches against the tags compatible with the running + # Python. If you want to match other values, set wheel_tags on a locator + # instance to a list of tuples (pyver, abi, arch) which you want to match. + wheel_tags = None + + downloadable_extensions = source_extensions + ('.whl',) + + def __init__(self, scheme='default'): + """ + Initialise an instance. + :param scheme: Because locators look for most recent versions, they + need to know the version scheme to use. This specifies + the current PEP-recommended scheme - use ``'legacy'`` + if you need to support existing distributions on PyPI. + """ + self._cache = {} + self.scheme = scheme + # Because of bugs in some of the handlers on some of the platforms, + # we use our own opener rather than just using urlopen. + self.opener = build_opener(RedirectHandler()) + # If get_project() is called from locate(), the matcher instance + # is set from the requirement passed to locate(). See issue #18 for + # why this can be useful to know. + self.matcher = None + self.errors = queue.Queue() + + def get_errors(self): + """ + Return any errors which have occurred. + """ + result = [] + while not self.errors.empty(): # pragma: no cover + try: + e = self.errors.get(False) + result.append(e) + except self.errors.Empty: + continue + self.errors.task_done() + return result + + def clear_errors(self): + """ + Clear any errors which may have been logged. + """ + # Just get the errors and throw them away + self.get_errors() + + def clear_cache(self): + self._cache.clear() + + def _get_scheme(self): + return self._scheme + + def _set_scheme(self, value): + self._scheme = value + + scheme = property(_get_scheme, _set_scheme) + + def _get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This should be implemented in subclasses. + + If called from a locate() request, self.matcher will be set to a + matcher for the requirement to satisfy, otherwise it will be None. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This calls _get_project to do all the work, and just implements a caching layer on top. + """ + if self._cache is None: # pragma: no cover + result = self._get_project(name) + elif name in self._cache: + result = self._cache[name] + else: + self.clear_errors() + result = self._get_project(name) + self._cache[name] = result + return result + + def score_url(self, url): + """ + Give an url a score which can be used to choose preferred URLs + for a given project release. + """ + t = urlparse(url) + basename = posixpath.basename(t.path) + compatible = True + is_wheel = basename.endswith('.whl') + is_downloadable = basename.endswith(self.downloadable_extensions) + if is_wheel: + compatible = is_compatible(Wheel(basename), self.wheel_tags) + return (t.scheme == 'https', 'pypi.org' in t.netloc, + is_downloadable, is_wheel, compatible, basename) + + def prefer_url(self, url1, url2): + """ + Choose one of two URLs where both are candidates for distribution + archives for the same version of a distribution (for example, + .tar.gz vs. zip). + + The current implementation favours https:// URLs over http://, archives + from PyPI over those from other locations, wheel compatibility (if a + wheel) and then the archive name. + """ + result = url2 + if url1: + s1 = self.score_url(url1) + s2 = self.score_url(url2) + if s1 > s2: + result = url1 + if result != url2: + logger.debug('Not replacing %r with %r', url1, url2) + else: + logger.debug('Replacing %r with %r', url1, url2) + return result + + def split_filename(self, filename, project_name): + """ + Attempt to split a filename in project name, version and Python version. + """ + return split_filename(filename, project_name) + + def convert_url_to_download_info(self, url, project_name): + """ + See if a URL is a candidate for a download URL for a project (the URL + has typically been scraped from an HTML page). + + If it is, a dictionary is returned with keys "name", "version", + "filename" and "url"; otherwise, None is returned. + """ + def same_project(name1, name2): + return normalize_name(name1) == normalize_name(name2) + + result = None + scheme, netloc, path, params, query, frag = urlparse(url) + if frag.lower().startswith('egg='): # pragma: no cover + logger.debug('%s: version hint in fragment: %r', + project_name, frag) + m = HASHER_HASH.match(frag) + if m: + algo, digest = m.groups() + else: + algo, digest = None, None + origpath = path + if path and path[-1] == '/': # pragma: no cover + path = path[:-1] + if path.endswith('.whl'): + try: + wheel = Wheel(path) + if not is_compatible(wheel, self.wheel_tags): + logger.debug('Wheel not compatible: %s', path) + else: + if project_name is None: + include = True + else: + include = same_project(wheel.name, project_name) + if include: + result = { + 'name': wheel.name, + 'version': wheel.version, + 'filename': wheel.filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + 'python-version': ', '.join( + ['.'.join(list(v[2:])) for v in wheel.pyver]), + } + except Exception as e: # pragma: no cover + logger.warning('invalid path for wheel: %s', path) + elif not path.endswith(self.downloadable_extensions): # pragma: no cover + logger.debug('Not downloadable: %s', path) + else: # downloadable extension + path = filename = posixpath.basename(path) + for ext in self.downloadable_extensions: + if path.endswith(ext): + path = path[:-len(ext)] + t = self.split_filename(path, project_name) + if not t: # pragma: no cover + logger.debug('No match for project/version: %s', path) + else: + name, version, pyver = t + if not project_name or same_project(project_name, name): + result = { + 'name': name, + 'version': version, + 'filename': filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + #'packagetype': 'sdist', + } + if pyver: # pragma: no cover + result['python-version'] = pyver + break + if result and algo: + result['%s_digest' % algo] = digest + return result + + def _get_digest(self, info): + """ + Get a digest from a dictionary by looking at a "digests" dictionary + or keys of the form 'algo_digest'. + + Returns a 2-tuple (algo, digest) if found, else None. Currently + looks only for SHA256, then MD5. + """ + result = None + if 'digests' in info: + digests = info['digests'] + for algo in ('sha256', 'md5'): + if algo in digests: + result = (algo, digests[algo]) + break + if not result: + for algo in ('sha256', 'md5'): + key = '%s_digest' % algo + if key in info: + result = (algo, info[key]) + break + return result + + def _update_version_data(self, result, info): + """ + Update a result dictionary (the final result from _get_project) with a + dictionary for a specific version, which typically holds information + gleaned from a filename or URL for an archive for the distribution. + """ + name = info.pop('name') + version = info.pop('version') + if version in result: + dist = result[version] + md = dist.metadata + else: + dist = make_dist(name, version, scheme=self.scheme) + md = dist.metadata + dist.digest = digest = self._get_digest(info) + url = info['url'] + result['digests'][url] = digest + if md.source_url != info['url']: + md.source_url = self.prefer_url(md.source_url, url) + result['urls'].setdefault(version, set()).add(url) + dist.locator = self + result[version] = dist + + def locate(self, requirement, prereleases=False): + """ + Find the most recent distribution which matches the given + requirement. + + :param requirement: A requirement of the form 'foo (1.0)' or perhaps + 'foo (>= 1.0, < 2.0, != 1.3)' + :param prereleases: If ``True``, allow pre-release versions + to be located. Otherwise, pre-release versions + are not returned. + :return: A :class:`Distribution` instance, or ``None`` if no such + distribution could be located. + """ + result = None + r = parse_requirement(requirement) + if r is None: # pragma: no cover + raise DistlibException('Not a valid requirement: %r' % requirement) + scheme = get_scheme(self.scheme) + self.matcher = matcher = scheme.matcher(r.requirement) + logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) + versions = self.get_project(r.name) + if len(versions) > 2: # urls and digests keys are present + # sometimes, versions are invalid + slist = [] + vcls = matcher.version_class + for k in versions: + if k in ('urls', 'digests'): + continue + try: + if not matcher.match(k): + logger.debug('%s did not match %r', matcher, k) + else: + if prereleases or not vcls(k).is_prerelease: + slist.append(k) + else: + logger.debug('skipping pre-release ' + 'version %s of %s', k, matcher.name) + except Exception: # pragma: no cover + logger.warning('error matching %s with %r', matcher, k) + pass # slist.append(k) + if len(slist) > 1: + slist = sorted(slist, key=scheme.key) + if slist: + logger.debug('sorted list: %s', slist) + version = slist[-1] + result = versions[version] + if result: + if r.extras: + result.extras = r.extras + result.download_urls = versions.get('urls', {}).get(version, set()) + d = {} + sd = versions.get('digests', {}) + for url in result.download_urls: + if url in sd: # pragma: no cover + d[url] = sd[url] + result.digests = d + self.matcher = None + return result + + +class PyPIRPCLocator(Locator): + """ + This locator uses XML-RPC to locate distributions. It therefore + cannot be used with simple mirrors (that only mirror file content). + """ + def __init__(self, url, **kwargs): + """ + Initialise an instance. + + :param url: The URL to use for XML-RPC. + :param kwargs: Passed to the superclass constructor. + """ + super(PyPIRPCLocator, self).__init__(**kwargs) + self.base_url = url + self.client = ServerProxy(url, timeout=3.0) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + return set(self.client.list_packages()) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + versions = self.client.package_releases(name, True) + for v in versions: + urls = self.client.release_urls(name, v) + data = self.client.release_data(name, v) + metadata = Metadata(scheme=self.scheme) + metadata.name = data['name'] + metadata.version = data['version'] + metadata.license = data.get('license') + metadata.keywords = data.get('keywords', []) + metadata.summary = data.get('summary') + dist = Distribution(metadata) + if urls: + info = urls[0] + metadata.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[v] = dist + for info in urls: + url = info['url'] + digest = self._get_digest(info) + result['urls'].setdefault(v, set()).add(url) + result['digests'][url] = digest + return result + +class PyPIJSONLocator(Locator): + """ + This locator uses PyPI's JSON interface. It's very limited in functionality + and probably not worth using. + """ + def __init__(self, url, **kwargs): + super(PyPIJSONLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + url = urljoin(self.base_url, '%s/json' % quote(name)) + try: + resp = self.opener.open(url) + data = resp.read().decode() # for now + d = json.loads(data) + md = Metadata(scheme=self.scheme) + data = d['info'] + md.name = data['name'] + md.version = data['version'] + md.license = data.get('license') + md.keywords = data.get('keywords', []) + md.summary = data.get('summary') + dist = Distribution(md) + dist.locator = self + urls = d['urls'] + result[md.version] = dist + for info in d['urls']: + url = info['url'] + dist.download_urls.add(url) + dist.digests[url] = self._get_digest(info) + result['urls'].setdefault(md.version, set()).add(url) + result['digests'][url] = self._get_digest(info) + # Now get other releases + for version, infos in d['releases'].items(): + if version == md.version: + continue # already done + omd = Metadata(scheme=self.scheme) + omd.name = md.name + omd.version = version + odist = Distribution(omd) + odist.locator = self + result[version] = odist + for info in infos: + url = info['url'] + odist.download_urls.add(url) + odist.digests[url] = self._get_digest(info) + result['urls'].setdefault(version, set()).add(url) + result['digests'][url] = self._get_digest(info) +# for info in urls: +# md.source_url = info['url'] +# dist.digest = self._get_digest(info) +# dist.locator = self +# for info in urls: +# url = info['url'] +# result['urls'].setdefault(md.version, set()).add(url) +# result['digests'][url] = self._get_digest(info) + except Exception as e: + self.errors.put(text_type(e)) + logger.exception('JSON fetch failed: %s', e) + return result + + +class Page(object): + """ + This class represents a scraped HTML page. + """ + # The following slightly hairy-looking regex just looks for the contents of + # an anchor link, which has an attribute "href" either immediately preceded + # or immediately followed by a "rel" attribute. The attribute values can be + # declared with double quotes, single quotes or no quotes - which leads to + # the length of the expression. + _href = re.compile(""" +(rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*))\\s+)? +href\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)) +(\\s+rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""]+)""", re.I | re.S) + + def __init__(self, data, url): + """ + Initialise an instance with the Unicode page contents and the URL they + came from. + """ + self.data = data + self.base_url = self.url = url + m = self._base.search(self.data) + if m: + self.base_url = m.group(1) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + @cached_property + def links(self): + """ + Return the URLs of all the links on a page together with information + about their "rel" attribute, for determining which ones to treat as + downloads and which ones to queue for further scraping. + """ + def clean(url): + "Tidy up an URL." + scheme, netloc, path, params, query, frag = urlparse(url) + return urlunparse((scheme, netloc, quote(path), + params, query, frag)) + + result = set() + for match in self._href.finditer(self.data): + d = match.groupdict('') + rel = (d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6']) + url = d['url1'] or d['url2'] or d['url3'] + url = urljoin(self.base_url, url) + url = unescape(url) + url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) + result.add((url, rel)) + # We sort the result, hoping to bring the most recent versions + # to the front + result = sorted(result, key=lambda t: t[0], reverse=True) + return result + + +class SimpleScrapingLocator(Locator): + """ + A locator which scrapes HTML pages to locate downloads for a distribution. + This runs multiple threads to do the I/O; performance is at least as good + as pip's PackageFinder, which works in an analogous fashion. + """ + + # These are used to deal with various Content-Encoding schemes. + decoders = { + 'deflate': zlib.decompress, + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'none': lambda b: b, + } + + def __init__(self, url, timeout=None, num_workers=10, **kwargs): + """ + Initialise an instance. + :param url: The root URL to use for scraping. + :param timeout: The timeout, in seconds, to be applied to requests. + This defaults to ``None`` (no timeout specified). + :param num_workers: The number of worker threads you want to do I/O, + This defaults to 10. + :param kwargs: Passed to the superclass. + """ + super(SimpleScrapingLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + self.timeout = timeout + self._page_cache = {} + self._seen = set() + self._to_fetch = queue.Queue() + self._bad_hosts = set() + self.skip_externals = False + self.num_workers = num_workers + self._lock = threading.RLock() + # See issue #45: we need to be resilient when the locator is used + # in a thread, e.g. with concurrent.futures. We can't use self._lock + # as it is for coordinating our internal threads - the ones created + # in _prepare_threads. + self._gplock = threading.RLock() + self.platform_check = False # See issue #112 + + def _prepare_threads(self): + """ + Threads are created only when get_project is called, and terminate + before it returns. They are there primarily to parallelise I/O (i.e. + fetching web pages). + """ + self._threads = [] + for i in range(self.num_workers): + t = threading.Thread(target=self._fetch) + t.setDaemon(True) + t.start() + self._threads.append(t) + + def _wait_threads(self): + """ + Tell all the threads to terminate (by sending a sentinel value) and + wait for them to do so. + """ + # Note that you need two loops, since you can't say which + # thread will get each sentinel + for t in self._threads: + self._to_fetch.put(None) # sentinel + for t in self._threads: + t.join() + self._threads = [] + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + with self._gplock: + self.result = result + self.project_name = name + url = urljoin(self.base_url, '%s/' % quote(name)) + self._seen.clear() + self._page_cache.clear() + self._prepare_threads() + try: + logger.debug('Queueing %s', url) + self._to_fetch.put(url) + self._to_fetch.join() + finally: + self._wait_threads() + del self.result + return result + + platform_dependent = re.compile(r'\b(linux_(i\d86|x86_64|arm\w+)|' + r'win(32|_amd64)|macosx_?\d+)\b', re.I) + + def _is_platform_dependent(self, url): + """ + Does an URL refer to a platform-specific download? + """ + return self.platform_dependent.search(url) + + def _process_download(self, url): + """ + See if an URL is a suitable download for a project. + + If it is, register information in the result dictionary (for + _get_project) about the specific version it's for. + + Note that the return value isn't actually used other than as a boolean + value. + """ + if self.platform_check and self._is_platform_dependent(url): + info = None + else: + info = self.convert_url_to_download_info(url, self.project_name) + logger.debug('process_download: %s -> %s', url, info) + if info: + with self._lock: # needed because self.result is shared + self._update_version_data(self.result, info) + return info + + def _should_queue(self, link, referrer, rel): + """ + Determine whether a link URL from a referring page and with a + particular "rel" attribute should be queued for scraping. + """ + scheme, netloc, path, _, _, _ = urlparse(link) + if path.endswith(self.source_extensions + self.binary_extensions + + self.excluded_extensions): + result = False + elif self.skip_externals and not link.startswith(self.base_url): + result = False + elif not referrer.startswith(self.base_url): + result = False + elif rel not in ('homepage', 'download'): + result = False + elif scheme not in ('http', 'https', 'ftp'): + result = False + elif self._is_platform_dependent(link): + result = False + else: + host = netloc.split(':', 1)[0] + if host.lower() == 'localhost': + result = False + else: + result = True + logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result) + return result + + def _fetch(self): + """ + Get a URL to fetch from the work queue, get the HTML page, examine its + links for download candidates and candidates for further scraping. + + This is a handy method to run in a thread. + """ + while True: + url = self._to_fetch.get() + try: + if url: + page = self.get_page(url) + if page is None: # e.g. after an error + continue + for link, rel in page.links: + if link not in self._seen: + try: + self._seen.add(link) + if (not self._process_download(link) and + self._should_queue(link, url, rel)): + logger.debug('Queueing %s from %s', link, url) + self._to_fetch.put(link) + except MetadataInvalidError: # e.g. invalid versions + pass + except Exception as e: # pragma: no cover + self.errors.put(text_type(e)) + finally: + # always do this, to avoid hangs :-) + self._to_fetch.task_done() + if not url: + #logger.debug('Sentinel seen, quitting.') + break + + def get_page(self, url): + """ + Get the HTML for an URL, possibly from an in-memory cache. + + XXX TODO Note: this cache is never actually cleared. It's assumed that + the data won't get stale over the lifetime of a locator instance (not + necessarily true for the default_locator). + """ + # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api + scheme, netloc, path, _, _, _ = urlparse(url) + if scheme == 'file' and os.path.isdir(url2pathname(path)): + url = urljoin(ensure_slash(url), 'index.html') + + if url in self._page_cache: + result = self._page_cache[url] + logger.debug('Returning %s from cache: %s', url, result) + else: + host = netloc.split(':', 1)[0] + result = None + if host in self._bad_hosts: + logger.debug('Skipping %s due to bad host %s', url, host) + else: + req = Request(url, headers={'Accept-encoding': 'identity'}) + try: + logger.debug('Fetching %s', url) + resp = self.opener.open(req, timeout=self.timeout) + logger.debug('Fetched %s', url) + headers = resp.info() + content_type = headers.get('Content-Type', '') + if HTML_CONTENT_TYPE.match(content_type): + final_url = resp.geturl() + data = resp.read() + encoding = headers.get('Content-Encoding') + if encoding: + decoder = self.decoders[encoding] # fail if not found + data = decoder(data) + encoding = 'utf-8' + m = CHARSET.search(content_type) + if m: + encoding = m.group(1) + try: + data = data.decode(encoding) + except UnicodeError: # pragma: no cover + data = data.decode('latin-1') # fallback + result = Page(data, final_url) + self._page_cache[final_url] = result + except HTTPError as e: + if e.code != 404: + logger.exception('Fetch failed: %s: %s', url, e) + except URLError as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + with self._lock: + self._bad_hosts.add(host) + except Exception as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + finally: + self._page_cache[url] = result # even if None (failure) + return result + + _distname_re = re.compile(']*>([^<]+)<') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + page = self.get_page(self.base_url) + if not page: + raise DistlibException('Unable to get %s' % self.base_url) + for match in self._distname_re.finditer(page.data): + result.add(match.group(1)) + return result + +class DirectoryLocator(Locator): + """ + This class locates distributions in a directory tree. + """ + + def __init__(self, path, **kwargs): + """ + Initialise an instance. + :param path: The root of the directory tree to search. + :param kwargs: Passed to the superclass constructor, + except for: + * recursive - if True (the default), subdirectories are + recursed into. If False, only the top-level directory + is searched, + """ + self.recursive = kwargs.pop('recursive', True) + super(DirectoryLocator, self).__init__(**kwargs) + path = os.path.abspath(path) + if not os.path.isdir(path): # pragma: no cover + raise DistlibException('Not a directory: %r' % path) + self.base_dir = path + + def should_include(self, filename, parent): + """ + Should a filename be considered as a candidate for a distribution + archive? As well as the filename, the directory which contains it + is provided, though not used by the current implementation. + """ + return filename.endswith(self.downloadable_extensions) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, name) + if info: + self._update_version_data(result, info) + if not self.recursive: + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, None) + if info: + result.add(info['name']) + if not self.recursive: + break + return result + +class JSONLocator(Locator): + """ + This locator uses special extended metadata (not available on PyPI) and is + the basis of performant dependency resolution in distlib. Other locators + require archive downloads before dependencies can be determined! As you + might imagine, that can be slow. + """ + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + data = get_project_data(name) + if data: + for info in data.get('files', []): + if info['ptype'] != 'sdist' or info['pyversion'] != 'source': + continue + # We don't store summary in project metadata as it makes + # the data bigger for no benefit during dependency + # resolution + dist = make_dist(data['name'], info['version'], + summary=data.get('summary', + 'Placeholder for summary'), + scheme=self.scheme) + md = dist.metadata + md.source_url = info['url'] + # TODO SHA256 digest + if 'digest' in info and info['digest']: + dist.digest = ('md5', info['digest']) + md.dependencies = info.get('requirements', {}) + dist.exports = info.get('exports', {}) + result[dist.version] = dist + result['urls'].setdefault(dist.version, set()).add(info['url']) + return result + +class DistPathLocator(Locator): + """ + This locator finds installed distributions in a path. It can be useful for + adding to an :class:`AggregatingLocator`. + """ + def __init__(self, distpath, **kwargs): + """ + Initialise an instance. + + :param distpath: A :class:`DistributionPath` instance to search. + """ + super(DistPathLocator, self).__init__(**kwargs) + assert isinstance(distpath, DistributionPath) + self.distpath = distpath + + def _get_project(self, name): + dist = self.distpath.get_distribution(name) + if dist is None: + result = {'urls': {}, 'digests': {}} + else: + result = { + dist.version: dist, + 'urls': {dist.version: set([dist.source_url])}, + 'digests': {dist.version: set([None])} + } + return result + + +class AggregatingLocator(Locator): + """ + This class allows you to chain and/or merge a list of locators. + """ + def __init__(self, *locators, **kwargs): + """ + Initialise an instance. + + :param locators: The list of locators to search. + :param kwargs: Passed to the superclass constructor, + except for: + * merge - if False (the default), the first successful + search from any of the locators is returned. If True, + the results from all locators are merged (this can be + slow). + """ + self.merge = kwargs.pop('merge', False) + self.locators = locators + super(AggregatingLocator, self).__init__(**kwargs) + + def clear_cache(self): + super(AggregatingLocator, self).clear_cache() + for locator in self.locators: + locator.clear_cache() + + def _set_scheme(self, value): + self._scheme = value + for locator in self.locators: + locator.scheme = value + + scheme = property(Locator.scheme.fget, _set_scheme) + + def _get_project(self, name): + result = {} + for locator in self.locators: + d = locator.get_project(name) + if d: + if self.merge: + files = result.get('urls', {}) + digests = result.get('digests', {}) + # next line could overwrite result['urls'], result['digests'] + result.update(d) + df = result.get('urls') + if files and df: + for k, v in files.items(): + if k in df: + df[k] |= v + else: + df[k] = v + dd = result.get('digests') + if digests and dd: + dd.update(digests) + else: + # See issue #18. If any dists are found and we're looking + # for specific constraints, we only return something if + # a match is found. For example, if a DirectoryLocator + # returns just foo (1.0) while we're looking for + # foo (>= 2.0), we'll pretend there was nothing there so + # that subsequent locators can be queried. Otherwise we + # would just return foo (1.0) which would then lead to a + # failure to find foo (>= 2.0), because other locators + # weren't searched. Note that this only matters when + # merge=False. + if self.matcher is None: + found = True + else: + found = False + for k in d: + if self.matcher.match(k): + found = True + break + if found: + result = d + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for locator in self.locators: + try: + result |= locator.get_distribution_names() + except NotImplementedError: + pass + return result + + +# We use a legacy scheme simply because most of the dists on PyPI use legacy +# versions which don't conform to PEP 426 / PEP 440. +default_locator = AggregatingLocator( + JSONLocator(), + SimpleScrapingLocator('https://pypi.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P[\w-]+)\s*' + r'\(\s*(==\s*)?(?P[^)]+)\)$') + +class DependencyFinder(object): + """ + Locate dependencies for distributions. + """ + + def __init__(self, locator=None): + """ + Initialise an instance, using the specified locator + to locate distributions. + """ + self.locator = locator or default_locator + self.scheme = get_scheme(self.locator.scheme) + + def add_distribution(self, dist): + """ + Add a distribution to the finder. This will update internal information + about who provides what. + :param dist: The distribution to add. + """ + logger.debug('adding distribution %s', dist) + name = dist.key + self.dists_by_name[name] = dist + self.dists[(name, dist.version)] = dist + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + self.provided.setdefault(name, set()).add((version, dist)) + + def remove_distribution(self, dist): + """ + Remove a distribution from the finder. This will update internal + information about who provides what. + :param dist: The distribution to remove. + """ + logger.debug('removing distribution %s', dist) + name = dist.key + del self.dists_by_name[name] + del self.dists[(name, dist.version)] + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Remove from provided: %s, %s, %s', name, version, dist) + s = self.provided[name] + s.remove((version, dist)) + if not s: + del self.provided[name] + + def get_matcher(self, reqt): + """ + Get a version matcher for a requirement. + :param reqt: The requirement + :type reqt: str + :return: A version matcher (an instance of + :class:`distlib.version.Matcher`). + """ + try: + matcher = self.scheme.matcher(reqt) + except UnsupportedVersionError: # pragma: no cover + # XXX compat-mode if cannot read the version + name = reqt.split()[0] + matcher = self.scheme.matcher(name) + return matcher + + def find_providers(self, reqt): + """ + Find the distributions which can fulfill a requirement. + + :param reqt: The requirement. + :type reqt: str + :return: A set of distribution which can fulfill the requirement. + """ + matcher = self.get_matcher(reqt) + name = matcher.key # case-insensitive + result = set() + provided = self.provided + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + result.add(provider) + break + return result + + def try_to_replace(self, provider, other, problems): + """ + Attempt to replace one provider with another. This is typically used + when resolving dependencies from multiple sources, e.g. A requires + (B >= 1.0) while C requires (B >= 1.1). + + For successful replacement, ``provider`` must meet all the requirements + which ``other`` fulfills. + + :param provider: The provider we are trying to replace with. + :param other: The provider we're trying to replace. + :param problems: If False is returned, this will contain what + problems prevented replacement. This is currently + a tuple of the literal string 'cantreplace', + ``provider``, ``other`` and the set of requirements + that ``provider`` couldn't fulfill. + :return: True if we can replace ``other`` with ``provider``, else + False. + """ + rlist = self.reqts[other] + unmatched = set() + for s in rlist: + matcher = self.get_matcher(s) + if not matcher.match(provider.version): + unmatched.add(s) + if unmatched: + # can't replace other with provider + problems.add(('cantreplace', provider, other, + frozenset(unmatched))) + result = False + else: + # can replace other with provider + self.remove_distribution(other) + del self.reqts[other] + for s in rlist: + self.reqts.setdefault(provider, set()).add(s) + self.add_distribution(provider) + result = True + return result + + def find(self, requirement, meta_extras=None, prereleases=False): + """ + Find a distribution and all distributions it depends on. + + :param requirement: The requirement specifying the distribution to + find, or a Distribution instance. + :param meta_extras: A list of meta extras such as :test:, :build: and + so on. + :param prereleases: If ``True``, allow pre-release versions to be + returned - otherwise, don't return prereleases + unless they're all that's available. + + Return a set of :class:`Distribution` instances and a set of + problems. + + The distributions returned should be such that they have the + :attr:`required` attribute set to ``True`` if they were + from the ``requirement`` passed to ``find()``, and they have the + :attr:`build_time_dependency` attribute set to ``True`` unless they + are post-installation dependencies of the ``requirement``. + + The problems should be a tuple consisting of the string + ``'unsatisfied'`` and the requirement which couldn't be satisfied + by any distribution known to the locator. + """ + + self.provided = {} + self.dists = {} + self.dists_by_name = {} + self.reqts = {} + + meta_extras = set(meta_extras or []) + if ':*:' in meta_extras: + meta_extras.remove(':*:') + # :meta: and :run: are implicitly included + meta_extras |= set([':test:', ':build:', ':dev:']) + + if isinstance(requirement, Distribution): + dist = odist = requirement + logger.debug('passed %s as requirement', odist) + else: + dist = odist = self.locator.locate(requirement, + prereleases=prereleases) + if dist is None: + raise DistlibException('Unable to locate %r' % requirement) + logger.debug('located %s', odist) + dist.requested = True + problems = set() + todo = set([dist]) + install_dists = set([odist]) + while todo: + dist = todo.pop() + name = dist.key # case-insensitive + if name not in self.dists_by_name: + self.add_distribution(dist) + else: + #import pdb; pdb.set_trace() + other = self.dists_by_name[name] + if other != dist: + self.try_to_replace(dist, other, problems) + + ireqts = dist.run_requires | dist.meta_requires + sreqts = dist.build_requires + ereqts = set() + if meta_extras and dist in install_dists: + for key in ('test', 'build', 'dev'): + e = ':%s:' % key + if e in meta_extras: + ereqts |= getattr(dist, '%s_requires' % key) + all_reqts = ireqts | sreqts | ereqts + for r in all_reqts: + providers = self.find_providers(r) + if not providers: + logger.debug('No providers found for %r', r) + provider = self.locator.locate(r, prereleases=prereleases) + # If no provider is found and we didn't consider + # prereleases, consider them now. + if provider is None and not prereleases: + provider = self.locator.locate(r, prereleases=True) + if provider is None: + logger.debug('Cannot satisfy %r', r) + problems.add(('unsatisfied', r)) + else: + n, v = provider.key, provider.version + if (n, v) not in self.dists: + todo.add(provider) + providers.add(provider) + if r in ireqts and dist in install_dists: + install_dists.add(provider) + logger.debug('Adding %s to install_dists', + provider.name_and_version) + for p in providers: + name = p.key + if name not in self.dists_by_name: + self.reqts.setdefault(p, set()).add(r) + else: + other = self.dists_by_name[name] + if other != p: + # see if other can be replaced by p + self.try_to_replace(p, other, problems) + + dists = set(self.dists.values()) + for dist in dists: + dist.build_time_dependency = dist not in install_dists + if dist.build_time_dependency: + logger.debug('%s is a build-time dependency only.', + dist.name_and_version) + logger.debug('find done for %s', odist) + return dists, problems diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..ca0fe44 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Class representing the list of files in a distribution. + +Equivalent to distutils.filelist, but fixes some problems. +""" +import fnmatch +import logging +import os +import re +import sys + +from . import DistlibException +from .compat import fsdecode +from .util import convert_path + + +__all__ = ['Manifest'] + +logger = logging.getLogger(__name__) + +# a \ followed by some spaces + EOL +_COLLAPSE_PATTERN = re.compile('\\\\w*\n', re.M) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) + +# +# Due to the different results returned by fnmatch.translate, we need +# to do slightly different processing for Python 2.7 and 3.2 ... this needed +# to be brought in for Python 3.6 onwards. +# +_PYTHON_VERSION = sys.version_info[:2] + +class Manifest(object): + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory to explore under. + """ + self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) + self.prefix = self.base + os.sep + self.allfiles = None + self.files = set() + + # + # Public API + # + + def findall(self): + """Find all files under the base and set ``allfiles`` to the absolute + pathnames of files found. + """ + from stat import S_ISREG, S_ISDIR, S_ISLNK + + self.allfiles = allfiles = [] + root = self.base + stack = [root] + pop = stack.pop + push = stack.append + + while stack: + root = pop() + names = os.listdir(root) + + for name in names: + fullname = os.path.join(root, name) + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat.st_mode + if S_ISREG(mode): + allfiles.append(fsdecode(fullname)) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + + def add(self, item): + """ + Add a file to the manifest. + + :param item: The pathname to add. This can be relative to the base. + """ + if not item.startswith(self.prefix): + item = os.path.join(self.base, item) + self.files.add(os.path.normpath(item)) + + def add_many(self, items): + """ + Add a list of files to the manifest. + + :param items: The pathnames to add. These can be relative to the base. + """ + for item in items: + self.add(item) + + def sorted(self, wantdirs=False): + """ + Return sorted files in directory order + """ + + def add_dir(dirs, d): + dirs.add(d) + logger.debug('add_dir added %s', d) + if d != self.base: + parent, _ = os.path.split(d) + assert parent not in ('', '/') + add_dir(dirs, parent) + + result = set(self.files) # make a copy! + if wantdirs: + dirs = set() + for f in result: + add_dir(dirs, os.path.dirname(f)) + result |= dirs + return [os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result)] + + def clear(self): + """Clear all collected files.""" + self.files = set() + self.allfiles = [] + + def process_directive(self, directive): + """ + Process a directive which either adds some files from ``allfiles`` to + ``files``, or removes some files from ``files``. + + :param directive: The directive to process. This should be in a format + compatible with distutils ``MANIFEST.in`` files: + + http://docs.python.org/distutils/sourcedist.html#commands + """ + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dirpattern). + action, patterns, thedir, dirpattern = self._parse_directive(directive) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=True): + logger.warning('no files found matching %r', pattern) + + elif action == 'exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=True) + #if not found: + # logger.warning('no previously-included files ' + # 'found matching %r', pattern) + + elif action == 'global-include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=False): + logger.warning('no files found matching %r ' + 'anywhere in distribution', pattern) + + elif action == 'global-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=False) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found anywhere in ' + # 'distribution', pattern) + + elif action == 'recursive-include': + for pattern in patterns: + if not self._include_pattern(pattern, prefix=thedir): + logger.warning('no files found matching %r ' + 'under directory %r', pattern, thedir) + + elif action == 'recursive-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, prefix=thedir) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found under directory %r', + # pattern, thedir) + + elif action == 'graft': + if not self._include_pattern(None, prefix=dirpattern): + logger.warning('no directories found matching %r', + dirpattern) + + elif action == 'prune': + if not self._exclude_pattern(None, prefix=dirpattern): + logger.warning('no previously-included directories found ' + 'matching %r', dirpattern) + else: # pragma: no cover + # This should never happen, as it should be caught in + # _parse_template_line + raise DistlibException( + 'invalid action %r' % action) + + # + # Private API + # + + def _parse_directive(self, directive): + """ + Validate a directive. + :param directive: The directive to validate. + :return: A tuple of action, patterns, thedir, dir_patterns + """ + words = directive.split() + if len(words) == 1 and words[0] not in ('include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune'): + # no action given, let's use the default 'include' + words.insert(0, 'include') + + action = words[0] + patterns = thedir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistlibException( + '%r expects ...' % action) + + patterns = [convert_path(word) for word in words[1:]] + + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistlibException( + '%r expects ...' % action) + + thedir = convert_path(words[1]) + patterns = [convert_path(word) for word in words[2:]] + + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistlibException( + '%r expects a single ' % action) + + dir_pattern = convert_path(words[1]) + + else: + raise DistlibException('unknown action %r' % action) + + return action, patterns, thedir, dir_pattern + + def _include_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found. + """ + # XXX docstring lying about what the special chars are? + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.files.add(name) + found = True + return found + + def _exclude_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return True if files are + found. + + This API is public to allow e.g. exclusion of SCM subdirs, e.g. when + packaging source distributions + """ + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + for f in list(self.files): + if pattern_re.search(f): + self.files.remove(f) + found = True + return found + + def _translate_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Translate a shell-like wildcard pattern to a compiled regular + expression. + + Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if isinstance(pattern, str): + return re.compile(pattern) + else: + return pattern + + if _PYTHON_VERSION > (3, 2): + # ditch start and end characters + start, _, end = self._glob_to_re('_').partition('_') + + if pattern: + pattern_re = self._glob_to_re(pattern) + if _PYTHON_VERSION > (3, 2): + assert pattern_re.startswith(start) and pattern_re.endswith(end) + else: + pattern_re = '' + + base = re.escape(os.path.join(self.base, '')) + if prefix is not None: + # ditch end of pattern character + if _PYTHON_VERSION <= (3, 2): + empty_pattern = self._glob_to_re('') + prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] + else: + prefix_re = self._glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] + sep = os.sep + if os.sep == '\\': + sep = r'\\' + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + sep.join((prefix_re, + '.*' + pattern_re)) + else: + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, + pattern_re, end) + else: # no prefix -- respect anchor flag + if anchor: + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + pattern_re + else: + pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) + + return re.compile(pattern_re) + + def _glob_to_re(self, pattern): + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((? y, + '!=': lambda x, y: x != y, + '<': lambda x, y: x < y, + '<=': lambda x, y: x == y or x < y, + '>': lambda x, y: x > y, + '>=': lambda x, y: x == y or x > y, + 'and': lambda x, y: x and y, + 'or': lambda x, y: x or y, + 'in': lambda x, y: x in y, + 'not in': lambda x, y: x not in y, + } + + def evaluate(self, expr, context): + """ + Evaluate a marker expression returned by the :func:`parse_requirement` + function in the specified context. + """ + if isinstance(expr, string_types): + if expr[0] in '\'"': + result = expr[1:-1] + else: + if expr not in context: + raise SyntaxError('unknown variable: %s' % expr) + result = context[expr] + else: + assert isinstance(expr, dict) + op = expr['op'] + if op not in self.operations: + raise NotImplementedError('op not implemented: %s' % op) + elhs = expr['lhs'] + erhs = expr['rhs'] + if _is_literal(expr['lhs']) and _is_literal(expr['rhs']): + raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs)) + + lhs = self.evaluate(elhs, context) + rhs = self.evaluate(erhs, context) + result = self.operations[op](lhs, rhs) + return result + +def default_context(): + def format_full_version(info): + version = '%s.%s.%s' % (info.major, info.minor, info.micro) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + if hasattr(sys, 'implementation'): + implementation_version = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + implementation_version = '0' + implementation_name = '' + + result = { + 'implementation_name': implementation_name, + 'implementation_version': implementation_version, + 'os_name': os.name, + 'platform_machine': platform.machine(), + 'platform_python_implementation': platform.python_implementation(), + 'platform_release': platform.release(), + 'platform_system': platform.system(), + 'platform_version': platform.version(), + 'platform_in_venv': str(in_venv()), + 'python_full_version': platform.python_version(), + 'python_version': platform.python_version()[:3], + 'sys_platform': sys.platform, + } + return result + +DEFAULT_CONTEXT = default_context() +del default_context + +evaluator = Evaluator() + +def interpret(marker, execution_context=None): + """ + Interpret a marker and return a result depending on environment. + + :param marker: The marker to interpret. + :type marker: str + :param execution_context: The context used for name lookup. + :type execution_context: mapping + """ + try: + expr, rest = parse_marker(marker) + except Exception as e: + raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e)) + if rest and rest[0] != '#': + raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest)) + context = dict(DEFAULT_CONTEXT) + if execution_context: + context.update(execution_context) + return evaluator.evaluate(expr, context) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..6d5e236 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1056 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Implementation of the Metadata for Python packages PEPs. + +Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and withdrawn 2.0). +""" +from __future__ import unicode_literals + +import codecs +from email import message_from_file +import json +import logging +import re + + +from . import DistlibException, __version__ +from .compat import StringIO, string_types, text_type +from .markers import interpret +from .util import extract_by_key, get_extras +from .version import get_scheme, PEP440_VERSION_RE + +logger = logging.getLogger(__name__) + + +class MetadataMissingError(DistlibException): + """A required metadata is missing""" + + +class MetadataConflictError(DistlibException): + """Attempt to read or write metadata fields that are conflictual.""" + + +class MetadataUnrecognizedVersionError(DistlibException): + """Unknown metadata version number.""" + + +class MetadataInvalidError(DistlibException): + """A metadata value is invalid""" + +# public API of this module +__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] + +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + +# preferred version. Hopefully will be changed +# to 1.2 once PEP 345 is supported everywhere +PKG_INFO_PREFERRED_VERSION = '1.1' + +_LINE_PREFIX_1_2 = re.compile('\n \\|') +_LINE_PREFIX_PRE_1_2 = re.compile('\n ') +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License') + +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires') + +_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL') + +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External') + +_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL') + +_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra') + +_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension') + +# See issue #106: Sometimes 'Requires' and 'Provides' occur wrongly in +# the metadata. Include them in the tuple literal below to allow them +# (for now). +_566_FIELDS = _426_FIELDS + ('Description-Content-Type', + 'Requires', 'Provides') + +_566_MARKERS = ('Description-Content-Type',) + +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) +_ALL_FIELDS.update(_426_FIELDS) +_ALL_FIELDS.update(_566_FIELDS) + +EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') + + +def _version2fieldlist(version): + if version == '1.0': + return _241_FIELDS + elif version == '1.1': + return _314_FIELDS + elif version == '1.2': + return _345_FIELDS + elif version in ('1.3', '2.1'): + return _345_FIELDS + _566_FIELDS + elif version == '2.0': + return _426_FIELDS + raise MetadataUnrecognizedVersionError(version) + + +def _best_version(fields): + """Detect the best version depending on the fields used.""" + def _has_marker(keys, markers): + for marker in markers: + if marker in keys: + return True + return False + + keys = [] + for key, value in fields.items(): + if value in ([], 'UNKNOWN', None): + continue + keys.append(key) + + possible_versions = ['1.0', '1.1', '1.2', '1.3', '2.0', '2.1'] + + # first let's try to see if a field is not part of one of the version + for key in keys: + if key not in _241_FIELDS and '1.0' in possible_versions: + possible_versions.remove('1.0') + logger.debug('Removed 1.0 due to %s', key) + if key not in _314_FIELDS and '1.1' in possible_versions: + possible_versions.remove('1.1') + logger.debug('Removed 1.1 due to %s', key) + if key not in _345_FIELDS and '1.2' in possible_versions: + possible_versions.remove('1.2') + logger.debug('Removed 1.2 due to %s', key) + if key not in _566_FIELDS and '1.3' in possible_versions: + possible_versions.remove('1.3') + logger.debug('Removed 1.3 due to %s', key) + if key not in _566_FIELDS and '2.1' in possible_versions: + if key != 'Description': # In 2.1, description allowed after headers + possible_versions.remove('2.1') + logger.debug('Removed 2.1 due to %s', key) + if key not in _426_FIELDS and '2.0' in possible_versions: + possible_versions.remove('2.0') + logger.debug('Removed 2.0 due to %s', key) + + # possible_version contains qualified versions + if len(possible_versions) == 1: + return possible_versions[0] # found ! + elif len(possible_versions) == 0: + logger.debug('Out of options - unknown metadata set: %s', fields) + raise MetadataConflictError('Unknown metadata set') + + # let's see if one unique marker is found + is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) + is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) + is_2_1 = '2.1' in possible_versions and _has_marker(keys, _566_MARKERS) + is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) + if int(is_1_1) + int(is_1_2) + int(is_2_1) + int(is_2_0) > 1: + raise MetadataConflictError('You used incompatible 1.1/1.2/2.0/2.1 fields') + + # we have the choice, 1.0, or 1.2, or 2.0 + # - 1.0 has a broken Summary field but works with all tools + # - 1.1 is to avoid + # - 1.2 fixes Summary but has little adoption + # - 2.0 adds more features and is very new + if not is_1_1 and not is_1_2 and not is_2_1 and not is_2_0: + # we couldn't find any specific marker + if PKG_INFO_PREFERRED_VERSION in possible_versions: + return PKG_INFO_PREFERRED_VERSION + if is_1_1: + return '1.1' + if is_1_2: + return '1.2' + if is_2_1: + return '2.1' + + return '2.0' + +# This follows the rules about transforming keys as described in +# https://www.python.org/dev/peps/pep-0566/#id17 +_ATTR2FIELD = { + name.lower().replace("-", "_"): name for name in _ALL_FIELDS +} +_FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()} + +_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') +_VERSIONS_FIELDS = ('Requires-Python',) +_VERSION_FIELDS = ('Version',) +_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension') +_LISTTUPLEFIELDS = ('Project-URL',) + +_ELEMENTSFIELD = ('Keywords',) + +_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') + +_MISSING = object() + +_FILESAFE = re.compile('[^A-Za-z0-9.]+') + + +def _get_name_and_version(name, version, for_filename=False): + """Return the distribution name with version. + + If for_filename is true, return a filename-escaped form.""" + if for_filename: + # For both name and version any runs of non-alphanumeric or '.' + # characters are replaced with a single '-'. Additionally any + # spaces in the version string become '.' + name = _FILESAFE.sub('-', name) + version = _FILESAFE.sub('-', version.replace(' ', '.')) + return '%s-%s' % (name, version) + + +class LegacyMetadata(object): + """The legacy metadata of a release. + + Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a metadata file + - *fileobj* give a file-like object with metadata as content + - *mapping* is a dict-like object + - *scheme* is a version scheme name + """ + # TODO document the mapping API and UNKNOWN default key + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._fields = {} + self.requires_files = [] + self._dependencies = None + self.scheme = scheme + if path is not None: + self.read(path) + elif fileobj is not None: + self.read_file(fileobj) + elif mapping is not None: + self.update(mapping) + self.set_metadata_version() + + def set_metadata_version(self): + self._fields['Metadata-Version'] = _best_version(self._fields) + + def _write_field(self, fileobj, name, value): + fileobj.write('%s: %s\n' % (name, value)) + + def __getitem__(self, name): + return self.get(name) + + def __setitem__(self, name, value): + return self.set(name, value) + + def __delitem__(self, name): + field_name = self._convert_name(name) + try: + del self._fields[field_name] + except KeyError: + raise KeyError(name) + + def __contains__(self, name): + return (name in self._fields or + self._convert_name(name) in self._fields) + + def _convert_name(self, name): + if name in _ALL_FIELDS: + return name + name = name.replace('-', '_').lower() + return _ATTR2FIELD.get(name, name) + + def _default_value(self, name): + if name in _LISTFIELDS or name in _ELEMENTSFIELD: + return [] + return 'UNKNOWN' + + def _remove_line_prefix(self, value): + if self.metadata_version in ('1.0', '1.1'): + return _LINE_PREFIX_PRE_1_2.sub('\n', value) + else: + return _LINE_PREFIX_1_2.sub('\n', value) + + def __getattr__(self, name): + if name in _ATTR2FIELD: + return self[name] + raise AttributeError(name) + + # + # Public API + # + +# dependencies = property(_get_dependencies, _set_dependencies) + + def get_fullname(self, filesafe=False): + """Return the distribution name with version. + + If filesafe is true, return a filename-escaped form.""" + return _get_name_and_version(self['Name'], self['Version'], filesafe) + + def is_field(self, name): + """return True if name is a valid metadata key""" + name = self._convert_name(name) + return name in _ALL_FIELDS + + def is_multi_field(self, name): + name = self._convert_name(name) + return name in _LISTFIELDS + + def read(self, filepath): + """Read the metadata values from a file path.""" + fp = codecs.open(filepath, 'r', encoding='utf-8') + try: + self.read_file(fp) + finally: + fp.close() + + def read_file(self, fileob): + """Read the metadata values from a file object.""" + msg = message_from_file(fileob) + self._fields['Metadata-Version'] = msg['metadata-version'] + + # When reading, get all the fields we can + for field in _ALL_FIELDS: + if field not in msg: + continue + if field in _LISTFIELDS: + # we can have multiple lines + values = msg.get_all(field) + if field in _LISTTUPLEFIELDS and values is not None: + values = [tuple(value.split(',')) for value in values] + self.set(field, values) + else: + # single line + value = msg[field] + if value is not None and value != 'UNKNOWN': + self.set(field, value) + + # PEP 566 specifies that the body be used for the description, if + # available + body = msg.get_payload() + self["Description"] = body if body else self["Description"] + # logger.debug('Attempting to set metadata for %s', self) + # self.set_metadata_version() + + def write(self, filepath, skip_unknown=False): + """Write the metadata fields to filepath.""" + fp = codecs.open(filepath, 'w', encoding='utf-8') + try: + self.write_file(fp, skip_unknown) + finally: + fp.close() + + def write_file(self, fileobject, skip_unknown=False): + """Write the PKG-INFO format data to a file object.""" + self.set_metadata_version() + + for field in _version2fieldlist(self['Metadata-Version']): + values = self.get(field) + if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): + continue + if field in _ELEMENTSFIELD: + self._write_field(fileobject, field, ','.join(values)) + continue + if field not in _LISTFIELDS: + if field == 'Description': + if self.metadata_version in ('1.0', '1.1'): + values = values.replace('\n', '\n ') + else: + values = values.replace('\n', '\n |') + values = [values] + + if field in _LISTTUPLEFIELDS: + values = [','.join(value) for value in values] + + for value in values: + self._write_field(fileobject, field, value) + + def update(self, other=None, **kwargs): + """Set metadata values from the given iterable `other` and kwargs. + + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. + + Keys that don't match a metadata field or that have an empty value are + dropped. + """ + def _set(key, value): + if key in _ATTR2FIELD and value: + self.set(self._convert_name(key), value) + + if not other: + # other is None or empty container + pass + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, other[k]) + else: + for k, v in other: + _set(k, v) + + if kwargs: + for k, v in kwargs.items(): + _set(k, v) + + def set(self, name, value): + """Control then set a metadata field.""" + name = self._convert_name(name) + + if ((name in _ELEMENTSFIELD or name == 'Platform') and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [v.strip() for v in value.split(',')] + else: + value = [] + elif (name in _LISTFIELDS and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [value] + else: + value = [] + + if logger.isEnabledFor(logging.WARNING): + project_name = self['Name'] + + scheme = get_scheme(self.scheme) + if name in _PREDICATE_FIELDS and value is not None: + for v in value: + # check that the values are valid + if not scheme.is_valid_matcher(v.split(';')[0]): + logger.warning( + "'%s': '%s' is not valid (field '%s')", + project_name, v, name) + # FIXME this rejects UNKNOWN, is that right? + elif name in _VERSIONS_FIELDS and value is not None: + if not scheme.is_valid_constraint_list(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + elif name in _VERSION_FIELDS and value is not None: + if not scheme.is_valid_version(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + + if name in _UNICODEFIELDS: + if name == 'Description': + value = self._remove_line_prefix(value) + + self._fields[name] = value + + def get(self, name, default=_MISSING): + """Get a metadata field.""" + name = self._convert_name(name) + if name not in self._fields: + if default is _MISSING: + default = self._default_value(name) + return default + if name in _UNICODEFIELDS: + value = self._fields[name] + return value + elif name in _LISTFIELDS: + value = self._fields[name] + if value is None: + return [] + res = [] + for val in value: + if name not in _LISTTUPLEFIELDS: + res.append(val) + else: + # That's for Project-URL + res.append((val[0], val[1])) + return res + + elif name in _ELEMENTSFIELD: + value = self._fields[name] + if isinstance(value, string_types): + return value.split(',') + return self._fields[name] + + def check(self, strict=False): + """Check if the metadata is compliant. If strict is True then raise if + no Name or Version are provided""" + self.set_metadata_version() + + # XXX should check the versions (if the file was loaded) + missing, warnings = [], [] + + for attr in ('Name', 'Version'): # required by PEP 345 + if attr not in self: + missing.append(attr) + + if strict and missing != []: + msg = 'missing required metadata: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + + for attr in ('Home-page', 'Author'): + if attr not in self: + missing.append(attr) + + # checking metadata 1.2 (XXX needs to check 1.1, 1.0) + if self['Metadata-Version'] != '1.2': + return missing, warnings + + scheme = get_scheme(self.scheme) + + def are_valid_constraints(value): + for v in value: + if not scheme.is_valid_matcher(v.split(';')[0]): + return False + return True + + for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), + (_VERSIONS_FIELDS, + scheme.is_valid_constraint_list), + (_VERSION_FIELDS, + scheme.is_valid_version)): + for field in fields: + value = self.get(field, None) + if value is not None and not controller(value): + warnings.append("Wrong value for '%s': %s" % (field, value)) + + return missing, warnings + + def todict(self, skip_missing=False): + """Return fields as a dict. + + Field names will be converted to use the underscore-lowercase style + instead of hyphen-mixed case (i.e. home_page instead of Home-page). + This is as per https://www.python.org/dev/peps/pep-0566/#id17. + """ + self.set_metadata_version() + + fields = _version2fieldlist(self['Metadata-Version']) + + data = {} + + for field_name in fields: + if not skip_missing or field_name in self._fields: + key = _FIELD2ATTR[field_name] + if key != 'project_url': + data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] + + return data + + def add_requirements(self, requirements): + if self['Metadata-Version'] == '1.1': + # we can't have 1.1 metadata *and* Setuptools requires + for field in ('Obsoletes', 'Requires', 'Provides'): + if field in self: + del self[field] + self['Requires-Dist'] += requirements + + # Mapping API + # TODO could add iter* variants + + def keys(self): + return list(_version2fieldlist(self['Metadata-Version'])) + + def __iter__(self): + for key in self.keys(): + yield key + + def values(self): + return [self[key] for key in self.keys()] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __repr__(self): + return '<%s %s %s>' % (self.__class__.__name__, self.name, + self.version) + + +METADATA_FILENAME = 'pydist.json' +WHEEL_METADATA_FILENAME = 'metadata.json' +LEGACY_METADATA_FILENAME = 'METADATA' + + +class Metadata(object): + """ + The metadata of a release. This implementation uses 2.0 (JSON) + metadata where possible. If not possible, it wraps a LegacyMetadata + instance which handles the key-value metadata format. + """ + + METADATA_VERSION_MATCHER = re.compile(r'^\d+(\.\d+)*$') + + NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) + + VERSION_MATCHER = PEP440_VERSION_RE + + SUMMARY_MATCHER = re.compile('.{1,2047}') + + METADATA_VERSION = '2.0' + + GENERATOR = 'distlib (%s)' % __version__ + + MANDATORY_KEYS = { + 'name': (), + 'version': (), + 'summary': ('legacy',), + } + + INDEX_KEYS = ('name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url') + + DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments') + + SYNTAX_VALIDATORS = { + 'metadata_version': (METADATA_VERSION_MATCHER, ()), + 'name': (NAME_MATCHER, ('legacy',)), + 'version': (VERSION_MATCHER, ('legacy',)), + 'summary': (SUMMARY_MATCHER, ('legacy',)), + } + + __slots__ = ('_legacy', '_data', 'scheme') + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._legacy = None + self._data = None + self.scheme = scheme + #import pdb; pdb.set_trace() + if mapping is not None: + try: + self._validate_mapping(mapping, scheme) + self._data = mapping + except MetadataUnrecognizedVersionError: + self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) + self.validate() + else: + data = None + if path: + with open(path, 'rb') as f: + data = f.read() + elif fileobj: + data = fileobj.read() + if data is None: + # Initialised with no args - to be added + self._data = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + else: + if not isinstance(data, text_type): + data = data.decode('utf-8') + try: + self._data = json.loads(data) + self._validate_mapping(self._data, scheme) + except ValueError: + # Note: MetadataUnrecognizedVersionError does not + # inherit from ValueError (it's a DistlibException, + # which should not inherit from ValueError). + # The ValueError comes from the json.load - if that + # succeeds and we get a validation error, we want + # that to propagate + self._legacy = LegacyMetadata(fileobj=StringIO(data), + scheme=scheme) + self.validate() + + common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + + none_list = (None, list) + none_dict = (None, dict) + + mapped_keys = { + 'run_requires': ('Requires-Dist', list), + 'build_requires': ('Setup-Requires-Dist', list), + 'dev_requires': none_list, + 'test_requires': none_list, + 'meta_requires': none_list, + 'extras': ('Provides-Extra', list), + 'modules': none_list, + 'namespaces': none_list, + 'exports': none_dict, + 'commands': none_dict, + 'classifiers': ('Classifier', list), + 'source_url': ('Download-URL', None), + 'metadata_version': ('Metadata-Version', None), + } + + del none_list, none_dict + + def __getattribute__(self, key): + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, maker = mapped[key] + if self._legacy: + if lk is None: + result = None if maker is None else maker() + else: + result = self._legacy.get(lk) + else: + value = None if maker is None else maker() + if key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + result = self._data.get(key, value) + else: + # special cases for PEP 459 + sentinel = object() + result = sentinel + d = self._data.get('extensions') + if d: + if key == 'commands': + result = d.get('python.commands', value) + elif key == 'classifiers': + d = d.get('python.details') + if d: + result = d.get(key, value) + else: + d = d.get('python.exports') + if not d: + d = self._data.get('python.exports') + if d: + result = d.get(key, value) + if result is sentinel: + result = value + elif key not in common: + result = object.__getattribute__(self, key) + elif self._legacy: + result = self._legacy.get(key) + else: + result = self._data.get(key) + return result + + def _validate_value(self, key, value, scheme=None): + if key in self.SYNTAX_VALIDATORS: + pattern, exclusions = self.SYNTAX_VALIDATORS[key] + if (scheme or self.scheme) not in exclusions: + m = pattern.match(value) + if not m: + raise MetadataInvalidError("'%s' is an invalid value for " + "the '%s' property" % (value, + key)) + + def __setattr__(self, key, value): + self._validate_value(key, value) + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, _ = mapped[key] + if self._legacy: + if lk is None: + raise NotImplementedError + self._legacy[lk] = value + elif key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + self._data[key] = value + else: + # special cases for PEP 459 + d = self._data.setdefault('extensions', {}) + if key == 'commands': + d['python.commands'] = value + elif key == 'classifiers': + d = d.setdefault('python.details', {}) + d[key] = value + else: + d = d.setdefault('python.exports', {}) + d[key] = value + elif key not in common: + object.__setattr__(self, key, value) + else: + if key == 'keywords': + if isinstance(value, string_types): + value = value.strip() + if value: + value = value.split() + else: + value = [] + if self._legacy: + self._legacy[key] = value + else: + self._data[key] = value + + @property + def name_and_version(self): + return _get_name_and_version(self.name, self.version, True) + + @property + def provides(self): + if self._legacy: + result = self._legacy['Provides-Dist'] + else: + result = self._data.setdefault('provides', []) + s = '%s (%s)' % (self.name, self.version) + if s not in result: + result.append(s) + return result + + @provides.setter + def provides(self, value): + if self._legacy: + self._legacy['Provides-Dist'] = value + else: + self._data['provides'] = value + + def get_requirements(self, reqts, extras=None, env=None): + """ + Base method to get dependencies, given a set of extras + to satisfy and an optional environment context. + :param reqts: A list of sometimes-wanted dependencies, + perhaps dependent on extras and environment. + :param extras: A list of optional components being requested. + :param env: An optional environment for marker evaluation. + """ + if self._legacy: + result = reqts + else: + result = [] + extras = get_extras(extras or [], self.extras) + for d in reqts: + if 'extra' not in d and 'environment' not in d: + # unconditional + include = True + else: + if 'extra' not in d: + # Not extra-dependent - only environment-dependent + include = True + else: + include = d.get('extra') in extras + if include: + # Not excluded because of extras, check environment + marker = d.get('environment') + if marker: + include = interpret(marker, env) + if include: + result.extend(d['requires']) + for key in ('build', 'dev', 'test'): + e = ':%s:' % key + if e in extras: + extras.remove(e) + # A recursive call, but it should terminate since 'test' + # has been removed from the extras + reqts = self._data.get('%s_requires' % key, []) + result.extend(self.get_requirements(reqts, extras=extras, + env=env)) + return result + + @property + def dictionary(self): + if self._legacy: + return self._from_legacy() + return self._data + + @property + def dependencies(self): + if self._legacy: + raise NotImplementedError + else: + return extract_by_key(self._data, self.DEPENDENCY_KEYS) + + @dependencies.setter + def dependencies(self, value): + if self._legacy: + raise NotImplementedError + else: + self._data.update(value) + + def _validate_mapping(self, mapping, scheme): + if mapping.get('metadata_version') != self.METADATA_VERSION: + raise MetadataUnrecognizedVersionError() + missing = [] + for key, exclusions in self.MANDATORY_KEYS.items(): + if key not in mapping: + if scheme not in exclusions: + missing.append(key) + if missing: + msg = 'Missing metadata items: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + for k, v in mapping.items(): + self._validate_value(k, v, scheme) + + def validate(self): + if self._legacy: + missing, warnings = self._legacy.check(True) + if missing or warnings: + logger.warning('Metadata: missing: %s, warnings: %s', + missing, warnings) + else: + self._validate_mapping(self._data, self.scheme) + + def todict(self): + if self._legacy: + return self._legacy.todict(True) + else: + result = extract_by_key(self._data, self.INDEX_KEYS) + return result + + def _from_legacy(self): + assert self._legacy and not self._data + result = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + lmd = self._legacy.todict(True) # skip missing ones + for k in ('name', 'version', 'license', 'summary', 'description', + 'classifier'): + if k in lmd: + if k == 'classifier': + nk = 'classifiers' + else: + nk = k + result[nk] = lmd[k] + kw = lmd.get('Keywords', []) + if kw == ['']: + kw = [] + result['keywords'] = kw + keys = (('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires')) + for ok, nk in keys: + if ok in lmd and lmd[ok]: + result[nk] = [{'requires': lmd[ok]}] + result['provides'] = self.provides + author = {} + maintainer = {} + return result + + LEGACY_MAPPING = { + 'name': 'Name', + 'version': 'Version', + ('extensions', 'python.details', 'license'): 'License', + 'summary': 'Summary', + 'description': 'Description', + ('extensions', 'python.project', 'project_urls', 'Home'): 'Home-page', + ('extensions', 'python.project', 'contacts', 0, 'name'): 'Author', + ('extensions', 'python.project', 'contacts', 0, 'email'): 'Author-email', + 'source_url': 'Download-URL', + ('extensions', 'python.details', 'classifiers'): 'Classifier', + } + + def _to_legacy(self): + def process_entries(entries): + reqts = set() + for e in entries: + extra = e.get('extra') + env = e.get('environment') + rlist = e['requires'] + for r in rlist: + if not env and not extra: + reqts.add(r) + else: + marker = '' + if extra: + marker = 'extra == "%s"' % extra + if env: + if marker: + marker = '(%s) and %s' % (env, marker) + else: + marker = env + reqts.add(';'.join((r, marker))) + return reqts + + assert self._data and not self._legacy + result = LegacyMetadata() + nmd = self._data + # import pdb; pdb.set_trace() + for nk, ok in self.LEGACY_MAPPING.items(): + if not isinstance(nk, tuple): + if nk in nmd: + result[ok] = nmd[nk] + else: + d = nmd + found = True + for k in nk: + try: + d = d[k] + except (KeyError, IndexError): + found = False + break + if found: + result[ok] = d + r1 = process_entries(self.run_requires + self.meta_requires) + r2 = process_entries(self.build_requires + self.dev_requires) + if self.extras: + result['Provides-Extra'] = sorted(self.extras) + result['Requires-Dist'] = sorted(r1) + result['Setup-Requires-Dist'] = sorted(r2) + # TODO: any other fields wanted + return result + + def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): + if [path, fileobj].count(None) != 1: + raise ValueError('Exactly one of path and fileobj is needed') + self.validate() + if legacy: + if self._legacy: + legacy_md = self._legacy + else: + legacy_md = self._to_legacy() + if path: + legacy_md.write(path, skip_unknown=skip_unknown) + else: + legacy_md.write_file(fileobj, skip_unknown=skip_unknown) + else: + if self._legacy: + d = self._from_legacy() + else: + d = self._data + if fileobj: + json.dump(d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True) + else: + with codecs.open(path, 'w', 'utf-8') as f: + json.dump(d, f, ensure_ascii=True, indent=2, + sort_keys=True) + + def add_requirements(self, requirements): + if self._legacy: + self._legacy.add_requirements(requirements) + else: + run_requires = self._data.setdefault('run_requires', []) + always = None + for entry in run_requires: + if 'environment' not in entry and 'extra' not in entry: + always = entry + break + if always is None: + always = { 'requires': requirements } + run_requires.insert(0, always) + else: + rset = set(always['requires']) | set(requirements) + always['requires'] = sorted(rset) + + def __repr__(self): + name = self.name or '(no name)' + version = self.version or 'no version' + return '<%s %s %s (%s)>' % (self.__class__.__name__, + self.metadata_version, name, version) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..1884016 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import bisect +import io +import logging +import os +import pkgutil +import shutil +import sys +import types +import zipimport + +from . import DistlibException +from .util import cached_property, get_cache_base, path_to_cache_dir, Cache + +logger = logging.getLogger(__name__) + + +cache = None # created when needed + + +class ResourceCache(Cache): + def __init__(self, base=None): + if base is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('resource-cache')) + super(ResourceCache, self).__init__(base) + + def is_stale(self, resource, path): + """ + Is the cache stale for the given resource? + + :param resource: The :class:`Resource` being cached. + :param path: The path of the resource in the cache. + :return: True if the cache is stale. + """ + # Cache invalidation is a hard problem :-) + return True + + def get(self, resource): + """ + Get a resource into the cache, + + :param resource: A :class:`Resource` instance. + :return: The pathname of the resource in the cache. + """ + prefix, path = resource.finder.get_cache_info(resource) + if prefix is None: + result = path + else: + result = os.path.join(self.base, self.prefix_to_dir(prefix), path) + dirname = os.path.dirname(result) + if not os.path.isdir(dirname): + os.makedirs(dirname) + if not os.path.exists(result): + stale = True + else: + stale = self.is_stale(resource, path) + if stale: + # write the bytes of the resource to the cache location + with open(result, 'wb') as f: + f.write(resource.bytes) + return result + + +class ResourceBase(object): + def __init__(self, finder, name): + self.finder = finder + self.name = name + + +class Resource(ResourceBase): + """ + A class representing an in-package resource, such as a data file. This is + not normally instantiated by user code, but rather by a + :class:`ResourceFinder` which manages the resource. + """ + is_container = False # Backwards compatibility + + def as_stream(self): + """ + Get the resource as a stream. + + This is not a property to make it obvious that it returns a new stream + each time. + """ + return self.finder.get_stream(self) + + @cached_property + def file_path(self): + global cache + if cache is None: + cache = ResourceCache() + return cache.get(self) + + @cached_property + def bytes(self): + return self.finder.get_bytes(self) + + @cached_property + def size(self): + return self.finder.get_size(self) + + +class ResourceContainer(ResourceBase): + is_container = True # Backwards compatibility + + @cached_property + def resources(self): + return self.finder.get_resources(self) + + +class ResourceFinder(object): + """ + Resource finder for file system resources. + """ + + if sys.platform.startswith('java'): + skipped_extensions = ('.pyc', '.pyo', '.class') + else: + skipped_extensions = ('.pyc', '.pyo') + + def __init__(self, module): + self.module = module + self.loader = getattr(module, '__loader__', None) + self.base = os.path.dirname(getattr(module, '__file__', '')) + + def _adjust_path(self, path): + return os.path.realpath(path) + + def _make_path(self, resource_name): + # Issue #50: need to preserve type of path on Python 2.x + # like os.path._get_sep + if isinstance(resource_name, bytes): # should only happen on 2.x + sep = b'/' + else: + sep = '/' + parts = resource_name.split(sep) + parts.insert(0, self.base) + result = os.path.join(*parts) + return self._adjust_path(result) + + def _find(self, path): + return os.path.exists(path) + + def get_cache_info(self, resource): + return None, resource.path + + def find(self, resource_name): + path = self._make_path(resource_name) + if not self._find(path): + result = None + else: + if self._is_directory(path): + result = ResourceContainer(self, resource_name) + else: + result = Resource(self, resource_name) + result.path = path + return result + + def get_stream(self, resource): + return open(resource.path, 'rb') + + def get_bytes(self, resource): + with open(resource.path, 'rb') as f: + return f.read() + + def get_size(self, resource): + return os.path.getsize(resource.path) + + def get_resources(self, resource): + def allowed(f): + return (f != '__pycache__' and not + f.endswith(self.skipped_extensions)) + return set([f for f in os.listdir(resource.path) if allowed(f)]) + + def is_container(self, resource): + return self._is_directory(resource.path) + + _is_directory = staticmethod(os.path.isdir) + + def iterator(self, resource_name): + resource = self.find(resource_name) + if resource is not None: + todo = [resource] + while todo: + resource = todo.pop(0) + yield resource + if resource.is_container: + rname = resource.name + for name in resource.resources: + if not rname: + new_name = name + else: + new_name = '/'.join([rname, name]) + child = self.find(new_name) + if child.is_container: + todo.append(child) + else: + yield child + + +class ZipResourceFinder(ResourceFinder): + """ + Resource finder for resources in .zip files. + """ + def __init__(self, module): + super(ZipResourceFinder, self).__init__(module) + archive = self.loader.archive + self.prefix_len = 1 + len(archive) + # PyPy doesn't have a _files attr on zipimporter, and you can't set one + if hasattr(self.loader, '_files'): + self._files = self.loader._files + else: + self._files = zipimport._zip_directory_cache[archive] + self.index = sorted(self._files) + + def _adjust_path(self, path): + return path + + def _find(self, path): + path = path[self.prefix_len:] + if path in self._files: + result = True + else: + if path and path[-1] != os.sep: + path = path + os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + if not result: + logger.debug('_find failed: %r %r', path, self.loader.prefix) + else: + logger.debug('_find worked: %r %r', path, self.loader.prefix) + return result + + def get_cache_info(self, resource): + prefix = self.loader.archive + path = resource.path[1 + len(prefix):] + return prefix, path + + def get_bytes(self, resource): + return self.loader.get_data(resource.path) + + def get_stream(self, resource): + return io.BytesIO(self.get_bytes(resource)) + + def get_size(self, resource): + path = resource.path[self.prefix_len:] + return self._files[path][3] + + def get_resources(self, resource): + path = resource.path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + plen = len(path) + result = set() + i = bisect.bisect(self.index, path) + while i < len(self.index): + if not self.index[i].startswith(path): + break + s = self.index[i][plen:] + result.add(s.split(os.sep, 1)[0]) # only immediate children + i += 1 + return result + + def _is_directory(self, path): + path = path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + return result + +_finder_registry = { + type(None): ResourceFinder, + zipimport.zipimporter: ZipResourceFinder +} + +try: + # In Python 3.6, _frozen_importlib -> _frozen_importlib_external + try: + import _frozen_importlib_external as _fi + except ImportError: + import _frozen_importlib as _fi + _finder_registry[_fi.SourceFileLoader] = ResourceFinder + _finder_registry[_fi.FileFinder] = ResourceFinder + del _fi +except (ImportError, AttributeError): + pass + + +def register_finder(loader, finder_maker): + _finder_registry[type(loader)] = finder_maker + +_finder_cache = {} + + +def finder(package): + """ + Return a resource finder for a package. + :param package: The name of the package. + :return: A :class:`ResourceFinder` instance for the package. + """ + if package in _finder_cache: + result = _finder_cache[package] + else: + if package not in sys.modules: + __import__(package) + module = sys.modules[package] + path = getattr(module, '__path__', None) + if path is None: + raise DistlibException('You cannot get a finder for a module, ' + 'only for a package') + loader = getattr(module, '__loader__', None) + finder_maker = _finder_registry.get(type(loader)) + if finder_maker is None: + raise DistlibException('Unable to locate finder for %r' % package) + result = finder_maker(module) + _finder_cache[package] = result + return result + + +_dummy_module = types.ModuleType(str('__dummy__')) + + +def finder_for_path(path): + """ + Return a resource finder for a path, which should represent a container. + + :param path: The path. + :return: A :class:`ResourceFinder` instance for the path. + """ + result = None + # calls any path hooks, gets importer into cache + pkgutil.get_importer(path) + loader = sys.path_importer_cache.get(path) + finder = _finder_registry.get(type(loader)) + if finder: + module = _dummy_module + module.__file__ = os.path.join(path, '') + module.__loader__ = loader + result = finder(module) + return result diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..03f8f21 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py @@ -0,0 +1,419 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys + +from .compat import sysconfig, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' + + + + + + + + + + + + +'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- +import re +import sys +from %(module)s import %(import_name)s +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +''' + + +def enquote_executable(executable): + if ' ' in executable: + # make sure we quote only the executable in case of env + # for example /usr/bin/env "/dir with spaces/bin/jython" + # instead of "/usr/bin/env /dir with spaces/bin/jython" + # otherwise whole + if executable.startswith('/usr/bin/env '): + env, _executable = executable.split(' ', 1) + if ' ' in _executable and not _executable.startswith('"'): + executable = '%s "%s"' % (env, _executable) + else: + if not executable.startswith('"'): + executable = '"%s"' % executable + return executable + +# Keep the old name around (for now), as there is at least one project using it! +_enquote_executable = enquote_executable + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') or (os.name == 'java' and + os._name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + self._is_nt = os.name == 'nt' or ( + os.name == 'java' and os._name == 'nt') + self.version_info = sys.version_info + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and self._is_nt: # pragma: no cover + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + if sys.platform.startswith('java'): # pragma: no cover + def _is_shell(self, executable): + """ + Determine if the specified executable is a script + (contains a #! line) + """ + try: + with open(executable) as fp: + return fp.read(2) == '#!' + except (OSError, IOError): + logger.warning('Failed to open %s', executable) + return False + + def _fix_jython_executable(self, executable): + if self._is_shell(executable): + # Workaround for Jython is not needed on Linux systems. + import java + + if java.lang.System.getProperty('os.name') == 'Linux': + return executable + elif executable.lower().endswith('jython.exe'): + # Use wrapper exe for Jython on Windows + return executable + return '/usr/bin/env %s' % executable + + def _build_shebang(self, executable, post_interp): + """ + Build a shebang line. In the simple case (on Windows, or a shebang line + which is not too long or contains spaces) use a simple formulation for + the shebang. Otherwise, use /bin/sh as the executable, with a contrived + shebang which allows the script to run either under Python or sh, using + suitable quoting. Thanks to Harald Nordgren for his input. + + See also: http://www.in-ulm.de/~mascheck/various/shebang/#length + https://hg.mozilla.org/mozilla-central/file/tip/mach + """ + if os.name != 'posix': + simple_shebang = True + else: + # Add 3 for '#!' prefix and newline suffix. + shebang_length = len(executable) + len(post_interp) + 3 + if sys.platform == 'darwin': + max_shebang_length = 512 + else: + max_shebang_length = 127 + simple_shebang = ((b' ' not in executable) and + (shebang_length <= max_shebang_length)) + + if simple_shebang: + result = b'#!' + executable + post_interp + b'\n' + else: + result = b'#!/bin/sh\n' + result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' + result += b"' '''" + return result + + def _get_shebang(self, encoding, post_interp=b'', options=None): + enquote = True + if self.executable: + executable = self.executable + enquote = False # assume this will be taken care of + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): # pragma: no cover + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: # pragma: no cover + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + if sys.platform.startswith('java'): # pragma: no cover + executable = self._fix_jython_executable(executable) + + # Normalise case for Windows - COMMENTED OUT + # executable = os.path.normcase(executable) + # N.B. The normalising operation above has been commented out: See + # issue #124. Although paths in Windows are generally case-insensitive, + # they aren't always. For example, a path containing a ẞ (which is a + # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a + # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by + # Windows as equivalent in path names. + + # If the user didn't specify an executable, it may be necessary to + # cater for executable paths with spaces (not uncommon on Windows) + if enquote: + executable = enquote_executable(executable) + # Issue #51: don't use fsencode, since we later try to + # check that the shebang is decodable using utf-8. + executable = executable.encode('utf-8') + # in case of IronPython, play safe and enable frames support + if (sys.platform == 'cli' and '-X:Frames' not in post_interp + and '-X:FullFrames' not in post_interp): # pragma: no cover + post_interp += b' -X:Frames' + shebang = self._build_shebang(executable, post_interp) + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + import_name=entry.suffix.split('.')[0], + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and self._is_nt + linesep = os.linesep.encode('utf-8') + if not shebang.endswith(linesep): + shebang += linesep + if not use_launcher: + script_bytes = shebang + script_bytes + else: # pragma: no cover + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: # pragma: no cover + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + def _make_script(self, entry, filenames, options=None): + post_interp = b'' + if options: + args = options.get('interpreter_args', []) + if args: + args = ' %s' % ' '.join(args) + post_interp = args.encode('utf-8') + shebang = self._get_shebang('utf-8', post_interp, options=options) + script = self._get_script_text(entry).encode('utf-8') + name = entry.name + scriptnames = set() + if '' in self.variants: + scriptnames.add(name) + if 'X' in self.variants: + scriptnames.add('%s%s' % (name, self.version_info[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s.%s' % (name, self.version_info[0], + self.version_info[1])) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: # pragma: no cover + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: # pragma: no cover + logger.warning('%s: %s is an empty file (skipping)', + self.get_command_name(), script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: # pragma: no cover + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + name = '%s%s.exe' % (kind, bits) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + resource = finder(distlib_package).find(name) + if not resource: + msg = ('Unable to find resource %s in package %s' % (name, + distlib_package)) + raise ValueError(msg) + return resource.bytes + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t32.exe b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000000000000000000000000000000000000..8932a18e4596952373a38c60b81b7116d4ef9ee8 GIT binary patch literal 96768 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 F{{j_Hb~FG0 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t64.exe b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000000000000000000000000000000000000..325b8057c08cf7113d4fd889991fa5638d443793 GIT binary patch literal 105984 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

      ^h^N4aZyJe%k(7BL*-gn=9``8`j0CuR45%cHI z_uuQ8!-|TEvJ}r=zF@%uKc8U@W1eNxUymJ(e45Tb6KDNieQcKe?L-gR8zZj^wFmi= z{5sAxrfP3BOZz~T$3h=Gi%jFg1%D>!6t*l^`zH2G#1PiYtvOBSI#q&y?8qN57P^LA zq9U)rQU+*y!XEgsGMCJM7yWOS+9lW~^axz>9gyv{Pu^qsBg%ZkfzkaN`$zV#>=oFn zwANnf4&g*eu~pAMC~1dl8FZ-^aeQgZ7=osPU=58@oke z55pen;eU@Z`iL!`$;1-VA&$VF4gN7ttU>relx5d-_x=|95B47HeeiYJZ$$38(ddJH zcrW3>{OR2@KF^H}gAbdZDX=AzyZHSizB_(9`v&$-69)D4WBjSaY@YVD`kl8;nl#Cu z5h1U}Tp}-l|Nde9w|3Pc@Aps8-X~fh_EGq!b-~*$a&nv>05-_n;)z{t+vW|PpX{Oj zKE#i|Gsq9Jhpor%Fqiu6y5}jjnz?*$b)h|UO;3NGd-k|9?ZqeVXL!9~vaIO0E8bVb zejzv5ZG0}~1A7{a3!hob11v4ihxvzh!S5>3I?4E~N9+^m8@sHve^M+wb{f3t2VUsD z*C*C&;z_`=&t~mbE@mHC`k7cGl3rKU9U84p?fzGxTg1q z)-Ai@eQSs49?#VDZ(BQ5_sXt#*VAn)X1Lk5l>kvHP6SDZX>#ITM7@`jxR;sQ>OG+Pe$CuXbGOdj zGq+|zTtQMnhk{-O{R=KF7*}vZ!OVhr1xpLo6l^NkTCk^}W?@_*Z|>hH7&o`>+{q8j zm_Kv=-1+n7FPgt}{>u4l=C7T3KtbFEnHc+rVzeuEi5hE<2hHiD6S}>D5l co{tw5U0O7=?|={2,3}|[~!]=)\s*') +MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*') +OR = re.compile(r'^or\b\s*') +AND = re.compile(r'^and\b\s*') +NON_SPACE = re.compile(r'(\S+)\s*') +STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)') + + +def parse_marker(marker_string): + """ + Parse a marker string and return a dictionary containing a marker expression. + + The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in + the expression grammar, or strings. A string contained in quotes is to be + interpreted as a literal string, and a string not contained in quotes is a + variable (such as os_name). + """ + def marker_var(remaining): + # either identifier, or literal string + m = IDENTIFIER.match(remaining) + if m: + result = m.groups()[0] + remaining = remaining[m.end():] + elif not remaining: + raise SyntaxError('unexpected end of input') + else: + q = remaining[0] + if q not in '\'"': + raise SyntaxError('invalid expression: %s' % remaining) + oq = '\'"'.replace(q, '') + remaining = remaining[1:] + parts = [q] + while remaining: + # either a string chunk, or oq, or q to terminate + if remaining[0] == q: + break + elif remaining[0] == oq: + parts.append(oq) + remaining = remaining[1:] + else: + m = STRING_CHUNK.match(remaining) + if not m: + raise SyntaxError('error in string literal: %s' % remaining) + parts.append(m.groups()[0]) + remaining = remaining[m.end():] + else: + s = ''.join(parts) + raise SyntaxError('unterminated string: %s' % s) + parts.append(q) + result = ''.join(parts) + remaining = remaining[1:].lstrip() # skip past closing quote + return result, remaining + + def marker_expr(remaining): + if remaining and remaining[0] == '(': + result, remaining = marker(remaining[1:].lstrip()) + if remaining[0] != ')': + raise SyntaxError('unterminated parenthesis: %s' % remaining) + remaining = remaining[1:].lstrip() + else: + lhs, remaining = marker_var(remaining) + while remaining: + m = MARKER_OP.match(remaining) + if not m: + break + op = m.groups()[0] + remaining = remaining[m.end():] + rhs, remaining = marker_var(remaining) + lhs = {'op': op, 'lhs': lhs, 'rhs': rhs} + result = lhs + return result, remaining + + def marker_and(remaining): + lhs, remaining = marker_expr(remaining) + while remaining: + m = AND.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_expr(remaining) + lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + def marker(remaining): + lhs, remaining = marker_and(remaining) + while remaining: + m = OR.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_and(remaining) + lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + return marker(marker_string) + + +def parse_requirement(req): + """ + Parse a requirement passed in as a string. Return a Container + whose attributes contain the various parts of the requirement. + """ + remaining = req.strip() + if not remaining or remaining.startswith('#'): + return None + m = IDENTIFIER.match(remaining) + if not m: + raise SyntaxError('name expected: %s' % remaining) + distname = m.groups()[0] + remaining = remaining[m.end():] + extras = mark_expr = versions = uri = None + if remaining and remaining[0] == '[': + i = remaining.find(']', 1) + if i < 0: + raise SyntaxError('unterminated extra: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + extras = [] + while s: + m = IDENTIFIER.match(s) + if not m: + raise SyntaxError('malformed extra: %s' % s) + extras.append(m.groups()[0]) + s = s[m.end():] + if not s: + break + if s[0] != ',': + raise SyntaxError('comma expected in extras: %s' % s) + s = s[1:].lstrip() + if not extras: + extras = None + if remaining: + if remaining[0] == '@': + # it's a URI + remaining = remaining[1:].lstrip() + m = NON_SPACE.match(remaining) + if not m: + raise SyntaxError('invalid URI: %s' % remaining) + uri = m.groups()[0] + t = urlparse(uri) + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not (t.scheme and t.netloc): + raise SyntaxError('Invalid URL: %s' % uri) + remaining = remaining[m.end():].lstrip() + else: + + def get_versions(ver_remaining): + """ + Return a list of operator, version tuples if any are + specified, else None. + """ + m = COMPARE_OP.match(ver_remaining) + versions = None + if m: + versions = [] + while True: + op = m.groups()[0] + ver_remaining = ver_remaining[m.end():] + m = VERSION_IDENTIFIER.match(ver_remaining) + if not m: + raise SyntaxError('invalid version: %s' % ver_remaining) + v = m.groups()[0] + versions.append((op, v)) + ver_remaining = ver_remaining[m.end():] + if not ver_remaining or ver_remaining[0] != ',': + break + ver_remaining = ver_remaining[1:].lstrip() + m = COMPARE_OP.match(ver_remaining) + if not m: + raise SyntaxError('invalid constraint: %s' % ver_remaining) + if not versions: + versions = None + return versions, ver_remaining + + if remaining[0] != '(': + versions, remaining = get_versions(remaining) + else: + i = remaining.find(')', 1) + if i < 0: + raise SyntaxError('unterminated parenthesis: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + # As a special diversion from PEP 508, allow a version number + # a.b.c in parentheses as a synonym for ~= a.b.c (because this + # is allowed in earlier PEPs) + if COMPARE_OP.match(s): + versions, _ = get_versions(s) + else: + m = VERSION_IDENTIFIER.match(s) + if not m: + raise SyntaxError('invalid constraint: %s' % s) + v = m.groups()[0] + s = s[m.end():].lstrip() + if s: + raise SyntaxError('invalid constraint: %s' % s) + versions = [('~=', v)] + + if remaining: + if remaining[0] != ';': + raise SyntaxError('invalid requirement: %s' % remaining) + remaining = remaining[1:].lstrip() + + mark_expr, remaining = parse_marker(remaining) + + if remaining and remaining[0] != '#': + raise SyntaxError('unexpected trailing data: %s' % remaining) + + if not versions: + rs = distname + else: + rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions])) + return Container(name=distname, extras=extras, constraints=versions, + marker=mark_expr, url=uri, requirement=rs) + + +def get_resources_dests(resources_root, rules): + """Find destinations for resources files""" + + def get_rel_path(root, path): + # normalizes and returns a lstripped-/-separated path + root = root.replace(os.path.sep, '/') + path = path.replace(os.path.sep, '/') + assert path.startswith(root) + return path[len(root):].lstrip('/') + + destinations = {} + for base, suffix, dest in rules: + prefix = os.path.join(resources_root, base) + for abs_base in iglob(prefix): + abs_glob = os.path.join(abs_base, suffix) + for abs_path in iglob(abs_glob): + resource_file = get_rel_path(resources_root, abs_path) + if dest is None: # remove the entry if it was here + destinations.pop(resource_file, None) + else: + rel_path = get_rel_path(abs_base, abs_path) + rel_dest = dest.replace(os.path.sep, '/').rstrip('/') + destinations[resource_file] = rel_dest + '/' + rel_path + return destinations + + +def in_venv(): + if hasattr(sys, 'real_prefix'): + # virtualenv venvs + result = True + else: + # PEP 405 venvs + result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) + return result + + +def get_executable(): +# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as +# changes to the stub launcher mean that sys.executable always points +# to the stub on OS X +# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' +# in os.environ): +# result = os.environ['__PYVENV_LAUNCHER__'] +# else: +# result = sys.executable +# return result + result = os.path.normcase(sys.executable) + if not isinstance(result, text_type): + result = fsdecode(result) + return result + + +def proceed(prompt, allowed_chars, error_prompt=None, default=None): + p = prompt + while True: + s = raw_input(p) + p = prompt + if not s and default: + s = default + if s: + c = s[0].lower() + if c in allowed_chars: + break + if error_prompt: + p = '%c: %s\n%s' % (c, error_prompt, prompt) + return c + + +def extract_by_key(d, keys): + if isinstance(keys, string_types): + keys = keys.split() + result = {} + for key in keys: + if key in d: + result[key] = d[key] + return result + +def read_exports(stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + # Try to load as JSON, falling back on legacy format + data = stream.read() + stream = StringIO(data) + try: + jdata = json.load(stream) + result = jdata['extensions']['python.exports']['exports'] + for group, entries in result.items(): + for k, v in entries.items(): + s = '%s = %s' % (k, v) + entry = get_export_entry(s) + assert entry is not None + entries[k] = entry + return result + except Exception: + stream.seek(0, 0) + + def read_stream(cp, stream): + if hasattr(cp, 'read_file'): + cp.read_file(stream) + else: + cp.readfp(stream) + + cp = configparser.ConfigParser() + try: + read_stream(cp, stream) + except configparser.MissingSectionHeaderError: + stream.close() + data = textwrap.dedent(data) + stream = StringIO(data) + read_stream(cp, stream) + + result = {} + for key in cp.sections(): + result[key] = entries = {} + for name, value in cp.items(key): + s = '%s = %s' % (name, value) + entry = get_export_entry(s) + assert entry is not None + #entry.dist = self + entries[name] = entry + return result + + +def write_exports(exports, stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getwriter('utf-8')(stream) + cp = configparser.ConfigParser() + for k, v in exports.items(): + # TODO check k, v for valid values + cp.add_section(k) + for entry in v.values(): + if entry.suffix is None: + s = entry.prefix + else: + s = '%s:%s' % (entry.prefix, entry.suffix) + if entry.flags: + s = '%s [%s]' % (s, ', '.join(entry.flags)) + cp.set(k, entry.name, s) + cp.write(stream) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +@contextlib.contextmanager +def chdir(d): + cwd = os.getcwd() + try: + os.chdir(d) + yield + finally: + os.chdir(cwd) + + +@contextlib.contextmanager +def socket_timeout(seconds=15): + cto = socket.getdefaulttimeout() + try: + socket.setdefaulttimeout(seconds) + yield + finally: + socket.setdefaulttimeout(cto) + + +class cached_property(object): + def __init__(self, func): + self.func = func + #for attr in ('__name__', '__module__', '__doc__'): + # setattr(self, attr, getattr(func, attr, None)) + + def __get__(self, obj, cls=None): + if obj is None: + return self + value = self.func(obj) + object.__setattr__(obj, self.func.__name__, value) + #obj.__dict__[self.func.__name__] = value = self.func(obj) + return value + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. + + The path is split on '/' and put back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while os.curdir in paths: + paths.remove(os.curdir) + if not paths: + return os.curdir + return os.path.join(*paths) + + +class FileOperator(object): + def __init__(self, dry_run=False): + self.dry_run = dry_run + self.ensured = set() + self._init_record() + + def _init_record(self): + self.record = False + self.files_written = set() + self.dirs_created = set() + + def record_as_written(self, path): + if self.record: + self.files_written.add(path) + + def newer(self, source, target): + """Tell if the target is newer than the source. + + Returns true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Returns false if both exist and 'target' is the same age or younger + than 'source'. Raise PackagingFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same + second will have the same "age". + """ + if not os.path.exists(source): + raise DistlibException("file '%r' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return True + + return os.stat(source).st_mtime > os.stat(target).st_mtime + + def copy_file(self, infile, outfile, check=True): + """Copy a file respecting dry-run and force flags. + """ + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying %s to %s', infile, outfile) + if not self.dry_run: + msg = None + if check: + if os.path.islink(outfile): + msg = '%s is a symlink' % outfile + elif os.path.exists(outfile) and not os.path.isfile(outfile): + msg = '%s is a non-regular file' % outfile + if msg: + raise ValueError(msg + ' which would be overwritten') + shutil.copyfile(infile, outfile) + self.record_as_written(outfile) + + def copy_stream(self, instream, outfile, encoding=None): + assert not os.path.isdir(outfile) + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying stream %s to %s', instream, outfile) + if not self.dry_run: + if encoding is None: + outstream = open(outfile, 'wb') + else: + outstream = codecs.open(outfile, 'w', encoding=encoding) + try: + shutil.copyfileobj(instream, outstream) + finally: + outstream.close() + self.record_as_written(outfile) + + def write_binary_file(self, path, data): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + if os.path.exists(path): + os.remove(path) + with open(path, 'wb') as f: + f.write(data) + self.record_as_written(path) + + def write_text_file(self, path, data, encoding): + self.write_binary_file(path, data.encode(encoding)) + + def set_mode(self, bits, mask, files): + if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'): + # Set the executable bits (owner, group, and world) on + # all the files specified. + for f in files: + if self.dry_run: + logger.info("changing mode of %s", f) + else: + mode = (os.stat(f).st_mode | bits) & mask + logger.info("changing mode of %s to %o", f, mode) + os.chmod(f, mode) + + set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + + def ensure_dir(self, path): + path = os.path.abspath(path) + if path not in self.ensured and not os.path.exists(path): + self.ensured.add(path) + d, f = os.path.split(path) + self.ensure_dir(d) + logger.info('Creating %s' % path) + if not self.dry_run: + os.mkdir(path) + if self.record: + self.dirs_created.add(path) + + def byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False): + dpath = cache_from_source(path, not optimize) + logger.info('Byte-compiling %s to %s', path, dpath) + if not self.dry_run: + if force or self.newer(path, dpath): + if not prefix: + diagpath = None + else: + assert path.startswith(prefix) + diagpath = path[len(prefix):] + compile_kwargs = {} + if hashed_invalidation and hasattr(py_compile, 'PycInvalidationMode'): + compile_kwargs['invalidation_mode'] = py_compile.PycInvalidationMode.CHECKED_HASH + py_compile.compile(path, dpath, diagpath, True, **compile_kwargs) # raise error + self.record_as_written(dpath) + return dpath + + def ensure_removed(self, path): + if os.path.exists(path): + if os.path.isdir(path) and not os.path.islink(path): + logger.debug('Removing directory tree at %s', path) + if not self.dry_run: + shutil.rmtree(path) + if self.record: + if path in self.dirs_created: + self.dirs_created.remove(path) + else: + if os.path.islink(path): + s = 'link' + else: + s = 'file' + logger.debug('Removing %s %s', s, path) + if not self.dry_run: + os.remove(path) + if self.record: + if path in self.files_written: + self.files_written.remove(path) + + def is_writable(self, path): + result = False + while not result: + if os.path.exists(path): + result = os.access(path, os.W_OK) + break + parent = os.path.dirname(path) + if parent == path: + break + path = parent + return result + + def commit(self): + """ + Commit recorded changes, turn off recording, return + changes. + """ + assert self.record + result = self.files_written, self.dirs_created + self._init_record() + return result + + def rollback(self): + if not self.dry_run: + for f in list(self.files_written): + if os.path.exists(f): + os.remove(f) + # dirs should all be empty now, except perhaps for + # __pycache__ subdirs + # reverse so that subdirs appear before their parents + dirs = sorted(self.dirs_created, reverse=True) + for d in dirs: + flist = os.listdir(d) + if flist: + assert flist == ['__pycache__'] + sd = os.path.join(d, flist[0]) + os.rmdir(sd) + os.rmdir(d) # should fail if non-empty + self._init_record() + +def resolve(module_name, dotted_path): + if module_name in sys.modules: + mod = sys.modules[module_name] + else: + mod = __import__(module_name) + if dotted_path is None: + result = mod + else: + parts = dotted_path.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + +class ExportEntry(object): + def __init__(self, name, prefix, suffix, flags): + self.name = name + self.prefix = prefix + self.suffix = suffix + self.flags = flags + + @cached_property + def value(self): + return resolve(self.prefix, self.suffix) + + def __repr__(self): # pragma: no cover + return '' % (self.name, self.prefix, + self.suffix, self.flags) + + def __eq__(self, other): + if not isinstance(other, ExportEntry): + result = False + else: + result = (self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags) + return result + + __hash__ = object.__hash__ + + +ENTRY_RE = re.compile(r'''(?P(\w|[-.+])+) + \s*=\s*(?P(\w+)([:\.]\w+)*) + \s*(\[\s*(?P[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? + ''', re.VERBOSE) + +def get_export_entry(specification): + m = ENTRY_RE.search(specification) + if not m: + result = None + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + else: + d = m.groupdict() + name = d['name'] + path = d['callable'] + colons = path.count(':') + if colons == 0: + prefix, suffix = path, None + else: + if colons != 1: + raise DistlibException("Invalid specification " + "'%s'" % specification) + prefix, suffix = path.split(':') + flags = d['flags'] + if flags is None: + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + flags = [] + else: + flags = [f.strip() for f in flags.split(',')] + result = ExportEntry(name, prefix, suffix, flags) + return result + + +def get_cache_base(suffix=None): + """ + Return the default base location for distlib caches. If the directory does + not exist, it is created. Use the suffix provided for the base directory, + and default to '.distlib' if it isn't provided. + + On Windows, if LOCALAPPDATA is defined in the environment, then it is + assumed to be a directory, and will be the parent directory of the result. + On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home + directory - using os.expanduser('~') - will be the parent directory of + the result. + + The result is just the directory '.distlib' in the parent directory as + determined above, or with the name specified with ``suffix``. + """ + if suffix is None: + suffix = '.distlib' + if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: + result = os.path.expandvars('$localappdata') + else: + # Assume posix, or old Windows + result = os.path.expanduser('~') + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if os.path.isdir(result): + usable = os.access(result, os.W_OK) + if not usable: + logger.warning('Directory exists but is not writable: %s', result) + else: + try: + os.makedirs(result) + usable = True + except OSError: + logger.warning('Unable to create %s', result, exc_info=True) + usable = False + if not usable: + result = tempfile.mkdtemp() + logger.warning('Default location unusable, using %s', result) + return os.path.join(result, suffix) + + +def path_to_cache_dir(path): + """ + Convert an absolute path to a directory name for use in a cache. + + The algorithm used is: + + #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. + #. Any occurrence of ``os.sep`` is replaced with ``'--'``. + #. ``'.cache'`` is appended. + """ + d, p = os.path.splitdrive(os.path.abspath(path)) + if d: + d = d.replace(':', '---') + p = p.replace(os.sep, '--') + return d + p + '.cache' + + +def ensure_slash(s): + if not s.endswith('/'): + return s + '/' + return s + + +def parse_credentials(netloc): + username = password = None + if '@' in netloc: + prefix, netloc = netloc.rsplit('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + if username: + username = unquote(username) + if password: + password = unquote(password) + return username, password, netloc + + +def get_process_umask(): + result = os.umask(0o22) + os.umask(result) + return result + +def is_string_sequence(seq): + result = True + i = None + for i, s in enumerate(seq): + if not isinstance(s, string_types): + result = False + break + assert i is not None + return result + +PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I) +PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') + + +def split_filename(filename, project_name=None): + """ + Extract name, version, python version from a filename (no extension) + + Return name, version, pyver or None + """ + result = None + pyver = None + filename = unquote(filename).replace(' ', '-') + m = PYTHON_VERSION.search(filename) + if m: + pyver = m.group(1) + filename = filename[:m.start()] + if project_name and len(filename) > len(project_name) + 1: + m = re.match(re.escape(project_name) + r'\b', filename) + if m: + n = m.end() + result = filename[:n], filename[n + 1:], pyver + if result is None: + m = PROJECT_NAME_AND_VERSION.match(filename) + if m: + result = m.group(1), m.group(3), pyver + return result + +# Allow spaces in name because of legacy dists like "Twisted Core" +NAME_VERSION_RE = re.compile(r'(?P[\w .-]+)\s*' + r'\(\s*(?P[^\s)]+)\)$') + +def parse_name_and_version(p): + """ + A utility method used to get name and version from a string. + + From e.g. a Provides-Dist value. + + :param p: A value in a form 'foo (1.0)' + :return: The name and version as a tuple. + """ + m = NAME_VERSION_RE.match(p) + if not m: + raise DistlibException('Ill-formed name/version string: \'%s\'' % p) + d = m.groupdict() + return d['name'].strip().lower(), d['ver'] + +def get_extras(requested, available): + result = set() + requested = set(requested or []) + available = set(available or []) + if '*' in requested: + requested.remove('*') + result |= available + for r in requested: + if r == '-': + result.add(r) + elif r.startswith('-'): + unwanted = r[1:] + if unwanted not in available: + logger.warning('undeclared extra: %s' % unwanted) + if unwanted in result: + result.remove(unwanted) + else: + if r not in available: + logger.warning('undeclared extra: %s' % r) + result.add(r) + return result +# +# Extended metadata functionality +# + +def _get_external_data(url): + result = {} + try: + # urlopen might fail if it runs into redirections, + # because of Python issue #13696. Fixed in locators + # using a custom redirect handler. + resp = urlopen(url) + headers = resp.info() + ct = headers.get('Content-Type') + if not ct.startswith('application/json'): + logger.debug('Unexpected response for JSON request: %s', ct) + else: + reader = codecs.getreader('utf-8')(resp) + #data = reader.read().decode('utf-8') + #result = json.loads(data) + result = json.load(reader) + except Exception as e: + logger.exception('Failed to get external data for %s: %s', url, e) + return result + +_external_data_base_url = 'https://www.red-dove.com/pypi/projects/' + +def get_project_data(name): + url = '%s/%s/project.json' % (name[0].upper(), name) + url = urljoin(_external_data_base_url, url) + result = _get_external_data(url) + return result + +def get_package_data(name, version): + url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) + url = urljoin(_external_data_base_url, url) + return _get_external_data(url) + + +class Cache(object): + """ + A class implementing a cache for resources that need to live in the file system + e.g. shared libraries. This class was moved from resources to here because it + could be used by other modules, e.g. the wheel module. + """ + + def __init__(self, base): + """ + Initialise an instance. + + :param base: The base directory where the cache should be located. + """ + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if not os.path.isdir(base): # pragma: no cover + os.makedirs(base) + if (os.stat(base).st_mode & 0o77) != 0: + logger.warning('Directory \'%s\' is not private', base) + self.base = os.path.abspath(os.path.normpath(base)) + + def prefix_to_dir(self, prefix): + """ + Converts a resource prefix to a directory name in the cache. + """ + return path_to_cache_dir(prefix) + + def clear(self): + """ + Clear the cache. + """ + not_removed = [] + for fn in os.listdir(self.base): + fn = os.path.join(self.base, fn) + try: + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + except Exception: + not_removed.append(fn) + return not_removed + + +class EventMixin(object): + """ + A very simple publish/subscribe system. + """ + def __init__(self): + self._subscribers = {} + + def add(self, event, subscriber, append=True): + """ + Add a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be added (and called when the + event is published). + :param append: Whether to append or prepend the subscriber to an + existing subscriber list for the event. + """ + subs = self._subscribers + if event not in subs: + subs[event] = deque([subscriber]) + else: + sq = subs[event] + if append: + sq.append(subscriber) + else: + sq.appendleft(subscriber) + + def remove(self, event, subscriber): + """ + Remove a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be removed. + """ + subs = self._subscribers + if event not in subs: + raise ValueError('No subscribers: %r' % event) + subs[event].remove(subscriber) + + def get_subscribers(self, event): + """ + Return an iterator for the subscribers for an event. + :param event: The event to return subscribers for. + """ + return iter(self._subscribers.get(event, ())) + + def publish(self, event, *args, **kwargs): + """ + Publish a event and return a list of values returned by its + subscribers. + + :param event: The event to publish. + :param args: The positional arguments to pass to the event's + subscribers. + :param kwargs: The keyword arguments to pass to the event's + subscribers. + """ + result = [] + for subscriber in self.get_subscribers(event): + try: + value = subscriber(event, *args, **kwargs) + except Exception: + logger.exception('Exception during event publication') + value = None + result.append(value) + logger.debug('publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result) + return result + +# +# Simple sequencing +# +class Sequencer(object): + def __init__(self): + self._preds = {} + self._succs = {} + self._nodes = set() # nodes with no preds/succs + + def add_node(self, node): + self._nodes.add(node) + + def remove_node(self, node, edges=False): + if node in self._nodes: + self._nodes.remove(node) + if edges: + for p in set(self._preds.get(node, ())): + self.remove(p, node) + for s in set(self._succs.get(node, ())): + self.remove(node, s) + # Remove empties + for k, v in list(self._preds.items()): + if not v: + del self._preds[k] + for k, v in list(self._succs.items()): + if not v: + del self._succs[k] + + def add(self, pred, succ): + assert pred != succ + self._preds.setdefault(succ, set()).add(pred) + self._succs.setdefault(pred, set()).add(succ) + + def remove(self, pred, succ): + assert pred != succ + try: + preds = self._preds[succ] + succs = self._succs[pred] + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of anything' % succ) + try: + preds.remove(pred) + succs.remove(succ) + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of %r' % (succ, pred)) + + def is_step(self, step): + return (step in self._preds or step in self._succs or + step in self._nodes) + + def get_steps(self, final): + if not self.is_step(final): + raise ValueError('Unknown: %r' % final) + result = [] + todo = [] + seen = set() + todo.append(final) + while todo: + step = todo.pop(0) + if step in seen: + # if a step was already seen, + # move it to the end (so it will appear earlier + # when reversed on return) ... but not for the + # final step, as that would be confusing for + # users + if step != final: + result.remove(step) + result.append(step) + else: + seen.add(step) + result.append(step) + preds = self._preds.get(step, ()) + todo.extend(preds) + return reversed(result) + + @property + def strong_connections(self): + #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + index_counter = [0] + stack = [] + lowlinks = {} + index = {} + result = [] + + graph = self._succs + + def strongconnect(node): + # set the depth index for this node to the smallest unused index + index[node] = index_counter[0] + lowlinks[node] = index_counter[0] + index_counter[0] += 1 + stack.append(node) + + # Consider successors + try: + successors = graph[node] + except Exception: + successors = [] + for successor in successors: + if successor not in lowlinks: + # Successor has not yet been visited + strongconnect(successor) + lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + elif successor in stack: + # the successor is in the stack and hence in the current + # strongly connected component (SCC) + lowlinks[node] = min(lowlinks[node],index[successor]) + + # If `node` is a root node, pop the stack and generate an SCC + if lowlinks[node] == index[node]: + connected_component = [] + + while True: + successor = stack.pop() + connected_component.append(successor) + if successor == node: break + component = tuple(connected_component) + # storing the result + result.append(component) + + for node in graph: + if node not in lowlinks: + strongconnect(node) + + return result + + @property + def dot(self): + result = ['digraph G {'] + for succ in self._preds: + preds = self._preds[succ] + for pred in preds: + result.append(' %s -> %s;' % (pred, succ)) + for node in self._nodes: + result.append(' %s;' % node) + result.append('}') + return '\n'.join(result) + +# +# Unarchiving functionality for zip, tar, tgz, tbz, whl +# + +ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl') + +def unarchive(archive_filename, dest_dir, format=None, check=True): + + def check_path(path): + if not isinstance(path, text_type): + path = path.decode('utf-8') + p = os.path.abspath(os.path.join(dest_dir, path)) + if not p.startswith(dest_dir) or p[plen] != os.sep: + raise ValueError('path outside destination: %r' % p) + + dest_dir = os.path.abspath(dest_dir) + plen = len(dest_dir) + archive = None + if format is None: + if archive_filename.endswith(('.zip', '.whl')): + format = 'zip' + elif archive_filename.endswith(('.tar.gz', '.tgz')): + format = 'tgz' + mode = 'r:gz' + elif archive_filename.endswith(('.tar.bz2', '.tbz')): + format = 'tbz' + mode = 'r:bz2' + elif archive_filename.endswith('.tar'): + format = 'tar' + mode = 'r' + else: # pragma: no cover + raise ValueError('Unknown format for %r' % archive_filename) + try: + if format == 'zip': + archive = ZipFile(archive_filename, 'r') + if check: + names = archive.namelist() + for name in names: + check_path(name) + else: + archive = tarfile.open(archive_filename, mode) + if check: + names = archive.getnames() + for name in names: + check_path(name) + if format != 'zip' and sys.version_info[0] < 3: + # See Python issue 17153. If the dest path contains Unicode, + # tarfile extraction fails on Python 2.x if a member path name + # contains non-ASCII characters - it leads to an implicit + # bytes -> unicode conversion using ASCII to decode. + for tarinfo in archive.getmembers(): + if not isinstance(tarinfo.name, text_type): + tarinfo.name = tarinfo.name.decode('utf-8') + archive.extractall(dest_dir) + + finally: + if archive: + archive.close() + + +def zip_dir(directory): + """zip a directory tree into a BytesIO object""" + result = io.BytesIO() + dlen = len(directory) + with ZipFile(result, "w") as zf: + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + rel = root[dlen:] + dest = os.path.join(rel, name) + zf.write(full, dest) + return result + +# +# Simple progress bar +# + +UNITS = ('', 'K', 'M', 'G','T','P') + + +class Progress(object): + unknown = 'UNKNOWN' + + def __init__(self, minval=0, maxval=100): + assert maxval is None or maxval >= minval + self.min = self.cur = minval + self.max = maxval + self.started = None + self.elapsed = 0 + self.done = False + + def update(self, curval): + assert self.min <= curval + assert self.max is None or curval <= self.max + self.cur = curval + now = time.time() + if self.started is None: + self.started = now + else: + self.elapsed = now - self.started + + def increment(self, incr): + assert incr >= 0 + self.update(self.cur + incr) + + def start(self): + self.update(self.min) + return self + + def stop(self): + if self.max is not None: + self.update(self.max) + self.done = True + + @property + def maximum(self): + return self.unknown if self.max is None else self.max + + @property + def percentage(self): + if self.done: + result = '100 %' + elif self.max is None: + result = ' ?? %' + else: + v = 100.0 * (self.cur - self.min) / (self.max - self.min) + result = '%3d %%' % v + return result + + def format_duration(self, duration): + if (duration <= 0) and self.max is None or self.cur == self.min: + result = '??:??:??' + #elif duration < 1: + # result = '--:--:--' + else: + result = time.strftime('%H:%M:%S', time.gmtime(duration)) + return result + + @property + def ETA(self): + if self.done: + prefix = 'Done' + t = self.elapsed + #import pdb; pdb.set_trace() + else: + prefix = 'ETA ' + if self.max is None: + t = -1 + elif self.elapsed == 0 or (self.cur == self.min): + t = 0 + else: + #import pdb; pdb.set_trace() + t = float(self.max - self.min) + t /= self.cur - self.min + t = (t - 1) * self.elapsed + return '%s: %s' % (prefix, self.format_duration(t)) + + @property + def speed(self): + if self.elapsed == 0: + result = 0.0 + else: + result = (self.cur - self.min) / self.elapsed + for unit in UNITS: + if result < 1000: + break + result /= 1000.0 + return '%d %sB/s' % (result, unit) + +# +# Glob functionality +# + +RICH_GLOB = re.compile(r'\{([^}]*)\}') +_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') +_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') + + +def iglob(path_glob): + """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" + if _CHECK_RECURSIVE_GLOB.search(path_glob): + msg = """invalid glob %r: recursive glob "**" must be used alone""" + raise ValueError(msg % path_glob) + if _CHECK_MISMATCH_SET.search(path_glob): + msg = """invalid glob %r: mismatching set marker '{' or '}'""" + raise ValueError(msg % path_glob) + return _iglob(path_glob) + + +def _iglob(path_glob): + rich_path_glob = RICH_GLOB.split(path_glob, 1) + if len(rich_path_glob) > 1: + assert len(rich_path_glob) == 3, rich_path_glob + prefix, set, suffix = rich_path_glob + for item in set.split(','): + for path in _iglob(''.join((prefix, item, suffix))): + yield path + else: + if '**' not in path_glob: + for item in std_iglob(path_glob): + yield item + else: + prefix, radical = path_glob.split('**', 1) + if prefix == '': + prefix = '.' + if radical == '': + radical = '*' + else: + # we support both + radical = radical.lstrip('/') + radical = radical.lstrip('\\') + for path, dir, files in os.walk(prefix): + path = os.path.normpath(path) + for fn in _iglob(os.path.join(path, radical)): + yield fn + +if ssl: + from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, + CertificateError) + + +# +# HTTPSConnection which verifies certificates/matches domains +# + + class HTTPSConnection(httplib.HTTPSConnection): + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None + + # noinspection PyPropertyAccess + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if getattr(self, '_tunnel_host', False): + self.sock = sock + self._tunnel() + + if not hasattr(ssl, 'SSLContext'): + # For 2.x + if self.ca_certs: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs) + else: # pragma: no cover + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + if hasattr(ssl, 'OP_NO_SSLv2'): + context.options |= ssl.OP_NO_SSLv2 + if self.cert_file: + context.load_cert_chain(self.cert_file, self.key_file) + kwargs = {} + if self.ca_certs: + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=self.ca_certs) + if getattr(ssl, 'HAS_SNI', False): + kwargs['server_hostname'] = self.host + self.sock = context.wrap_socket(sock, **kwargs) + if self.ca_certs and self.check_domain: + try: + match_hostname(self.sock.getpeercert(), self.host) + logger.debug('Host verified: %s', self.host) + except CertificateError: # pragma: no cover + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + + class HTTPSHandler(BaseHTTPSHandler): + def __init__(self, ca_certs, check_domain=True): + BaseHTTPSHandler.__init__(self) + self.ca_certs = ca_certs + self.check_domain = check_domain + + def _conn_maker(self, *args, **kwargs): + """ + This is called to create a connection instance. Normally you'd + pass a connection class to do_open, but it doesn't actually check for + a class, and just expects a callable. As long as we behave just as a + constructor would have, we should be OK. If it ever changes so that + we *must* pass a class, we'll create an UnsafeHTTPSConnection class + which just sets check_domain to False in the class definition, and + choose which one to pass to do_open. + """ + result = HTTPSConnection(*args, **kwargs) + if self.ca_certs: + result.ca_certs = self.ca_certs + result.check_domain = self.check_domain + return result + + def https_open(self, req): + try: + return self.do_open(self._conn_maker, req) + except URLError as e: + if 'certificate verify failed' in str(e.reason): + raise CertificateError('Unable to verify server certificate ' + 'for %s' % req.host) + else: + raise + + # + # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- + # Middle proxy using HTTP listens on port 443, or an index mistakenly serves + # HTML containing a http://xyz link when it should be https://xyz), + # you can use the following handler class, which does not allow HTTP traffic. + # + # It works by inheriting from HTTPHandler - so build_opener won't add a + # handler for HTTP itself. + # + class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): + def http_open(self, req): + raise URLError('Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req) + +# +# XML-RPC with timeouts +# + +_ver_info = sys.version_info[:2] + +if _ver_info == (2, 6): + class HTTP(httplib.HTTP): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + + if ssl: + class HTTPS(httplib.HTTPS): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + +class Transport(xmlrpclib.Transport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.Transport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, x509 = self.get_host_info(host) + if _ver_info == (2, 6): + result = HTTP(h, timeout=self.timeout) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPConnection(h) + result = self._connection[1] + return result + +if ssl: + class SafeTransport(xmlrpclib.SafeTransport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.SafeTransport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, kwargs = self.get_host_info(host) + if not kwargs: + kwargs = {} + kwargs['timeout'] = self.timeout + if _ver_info == (2, 6): + result = HTTPS(host, None, **kwargs) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPSConnection(h, None, + **kwargs) + result = self._connection[1] + return result + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, **kwargs): + self.timeout = timeout = kwargs.pop('timeout', None) + # The above classes only come into play if a timeout + # is specified + if timeout is not None: + scheme, _ = splittype(uri) + use_datetime = kwargs.get('use_datetime', 0) + if scheme == 'https': + tcls = SafeTransport + else: + tcls = Transport + kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) + self.transport = t + xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) + +# +# CSV functionality. This is provided because on 2.x, the csv module can't +# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. +# + +def _csv_open(fn, mode, **kwargs): + if sys.version_info[0] < 3: + mode += 'b' + else: + kwargs['newline'] = '' + # Python 3 determines encoding from locale. Force 'utf-8' + # file encoding to match other forced utf-8 encoding + kwargs['encoding'] = 'utf-8' + return open(fn, mode, **kwargs) + + +class CSVBase(object): + defaults = { + 'delimiter': str(','), # The strs are used because we need native + 'quotechar': str('"'), # str in the csv API (2.x won't take + 'lineterminator': str('\n') # Unicode) + } + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stream.close() + + +class CSVReader(CSVBase): + def __init__(self, **kwargs): + if 'stream' in kwargs: + stream = kwargs['stream'] + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + self.stream = stream + else: + self.stream = _csv_open(kwargs['path'], 'r') + self.reader = csv.reader(self.stream, **self.defaults) + + def __iter__(self): + return self + + def next(self): + result = next(self.reader) + if sys.version_info[0] < 3: + for i, item in enumerate(result): + if not isinstance(item, text_type): + result[i] = item.decode('utf-8') + return result + + __next__ = next + +class CSVWriter(CSVBase): + def __init__(self, fn, **kwargs): + self.stream = _csv_open(fn, 'w') + self.writer = csv.writer(self.stream, **self.defaults) + + def writerow(self, row): + if sys.version_info[0] < 3: + r = [] + for item in row: + if isinstance(item, text_type): + item = item.encode('utf-8') + r.append(item) + row = r + self.writer.writerow(row) + +# +# Configurator functionality +# + +class Configurator(BaseConfigurator): + + value_converters = dict(BaseConfigurator.value_converters) + value_converters['inc'] = 'inc_convert' + + def __init__(self, config, base=None): + super(Configurator, self).__init__(config) + self.base = base or os.getcwd() + + def configure_custom(self, config): + def convert(o): + if isinstance(o, (list, tuple)): + result = type(o)([convert(i) for i in o]) + elif isinstance(o, dict): + if '()' in o: + result = self.configure_custom(o) + else: + result = {} + for k in o: + result[k] = convert(o[k]) + else: + result = self.convert(o) + return result + + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + args = config.pop('[]', ()) + if args: + args = tuple([convert(o) for o in args]) + items = [(k, convert(config[k])) for k in config if valid_ident(k)] + kwargs = dict(items) + result = c(*args, **kwargs) + if props: + for n, v in props.items(): + setattr(result, n, convert(v)) + return result + + def __getitem__(self, key): + result = self.config[key] + if isinstance(result, dict) and '()' in result: + self.config[key] = result = self.configure_custom(result) + return result + + def inc_convert(self, value): + """Default converter for the inc:// protocol.""" + if not os.path.isabs(value): + value = os.path.join(self.base, value) + with codecs.open(value, 'r', encoding='utf-8') as f: + result = json.load(f) + return result + + +class SubprocessMixin(object): + """ + Mixin for running subprocesses and capturing their output + """ + def __init__(self, verbose=False, progress=None): + self.verbose = verbose + self.progress = progress + + def reader(self, stream, context): + """ + Read lines from a subprocess' output stream and either pass to a progress + callable (if specified) or write progress information to sys.stderr. + """ + progress = self.progress + verbose = self.verbose + while True: + s = stream.readline() + if not s: + break + if progress is not None: + progress(s, context) + else: + if not verbose: + sys.stderr.write('.') + else: + sys.stderr.write(s.decode('utf-8')) + sys.stderr.flush() + stream.close() + + def run_command(self, cmd, **kwargs): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs) + t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) + t1.start() + t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) + t2.start() + p.wait() + t1.join() + t2.join() + if self.progress is not None: + self.progress('done.', 'main') + elif self.verbose: + sys.stderr.write('done.\n') + return p + + +def normalize_name(name): + """Normalize a python package name a la PEP 503""" + # https://www.python.org/dev/peps/pep-0503/#normalized-names + return re.sub('[-_.]+', '-', name).lower() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/version.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..3eebe18 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/version.py @@ -0,0 +1,736 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Implementation of a flexible versioning scheme providing support for PEP-440, +setuptools-compatible and semantic versioning. +""" + +import logging +import re + +from .compat import string_types +from .util import parse_requirement + +__all__ = ['NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme'] + +logger = logging.getLogger(__name__) + + +class UnsupportedVersionError(ValueError): + """This is an unsupported version.""" + pass + + +class Version(object): + def __init__(self, s): + self._string = s = s.strip() + self._parts = parts = self.parse(s) + assert isinstance(parts, tuple) + assert len(parts) > 0 + + def parse(self, s): + raise NotImplementedError('please implement in a subclass') + + def _check_compatible(self, other): + if type(self) != type(other): + raise TypeError('cannot compare %r and %r' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + self._check_compatible(other) + return self._parts < other._parts + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__lt__(other) or self.__eq__(other) + + def __ge__(self, other): + return self.__gt__(other) or self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self._parts) + + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + @property + def is_prerelease(self): + raise NotImplementedError('Please implement in subclasses.') + + +class Matcher(object): + version_class = None + + # value is either a callable or the name of a method + _operators = { + '<': lambda v, c, p: v < c, + '>': lambda v, c, p: v > c, + '<=': lambda v, c, p: v == c or v < c, + '>=': lambda v, c, p: v == c or v > c, + '==': lambda v, c, p: v == c, + '===': lambda v, c, p: v == c, + # by default, compatible => >=. + '~=': lambda v, c, p: v == c or v > c, + '!=': lambda v, c, p: v != c, + } + + # this is a method only to support alternative implementations + # via overriding + def parse_requirement(self, s): + return parse_requirement(s) + + def __init__(self, s): + if self.version_class is None: + raise ValueError('Please specify a version class') + self._string = s = s.strip() + r = self.parse_requirement(s) + if not r: + raise ValueError('Not valid: %r' % s) + self.name = r.name + self.key = self.name.lower() # for case-insensitive comparisons + clist = [] + if r.constraints: + # import pdb; pdb.set_trace() + for op, s in r.constraints: + if s.endswith('.*'): + if op not in ('==', '!='): + raise ValueError('\'.*\' not allowed for ' + '%r constraints' % op) + # Could be a partial version (e.g. for '2.*') which + # won't parse as a version, so keep it as a string + vn, prefix = s[:-2], True + # Just to check that vn is a valid version + self.version_class(vn) + else: + # Should parse as a version, so we can create an + # instance for the comparison + vn, prefix = self.version_class(s), False + clist.append((op, vn, prefix)) + self._parts = tuple(clist) + + def match(self, version): + """ + Check if the provided version matches the constraints. + + :param version: The version to match against this instance. + :type version: String or :class:`Version` instance. + """ + if isinstance(version, string_types): + version = self.version_class(version) + for operator, constraint, prefix in self._parts: + f = self._operators.get(operator) + if isinstance(f, string_types): + f = getattr(self, f) + if not f: + msg = ('%r not implemented ' + 'for %s' % (operator, self.__class__.__name__)) + raise NotImplementedError(msg) + if not f(version, constraint, prefix): + return False + return True + + @property + def exact_version(self): + result = None + if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): + result = self._parts[0][1] + return result + + def _check_compatible(self, other): + if type(self) != type(other) or self.name != other.name: + raise TypeError('cannot compare %s and %s' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self.key == other.key and self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self.key) + hash(self._parts) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + +PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') + + +def _pep_440_key(s): + s = s.strip() + m = PEP440_VERSION_RE.match(s) + if not m: + raise UnsupportedVersionError('Not a valid version: %s' % s) + groups = m.groups() + nums = tuple(int(v) for v in groups[1].split('.')) + while len(nums) > 1 and nums[-1] == 0: + nums = nums[:-1] + + if not groups[0]: + epoch = 0 + else: + epoch = int(groups[0]) + pre = groups[4:6] + post = groups[7:9] + dev = groups[10:12] + local = groups[13] + if pre == (None, None): + pre = () + else: + pre = pre[0], int(pre[1]) + if post == (None, None): + post = () + else: + post = post[0], int(post[1]) + if dev == (None, None): + dev = () + else: + dev = dev[0], int(dev[1]) + if local is None: + local = () + else: + parts = [] + for part in local.split('.'): + # to ensure that numeric compares as > lexicographic, avoid + # comparing them directly, but encode a tuple which ensures + # correct sorting + if part.isdigit(): + part = (1, int(part)) + else: + part = (0, part) + parts.append(part) + local = tuple(parts) + if not pre: + # either before pre-release, or final release and after + if not post and dev: + # before pre-release + pre = ('a', -1) # to sort before a0 + else: + pre = ('z',) # to sort after all pre-releases + # now look at the state of post and dev. + if not post: + post = ('_',) # sort before 'a' + if not dev: + dev = ('final',) + + #print('%s -> %s' % (s, m.groups())) + return epoch, nums, pre, post, dev, local + + +_normalized_key = _pep_440_key + + +class NormalizedVersion(Version): + """A rational version. + + Good: + 1.2 # equivalent to "1.2.0" + 1.2.0 + 1.2a1 + 1.2.3a2 + 1.2.3b1 + 1.2.3c1 + 1.2.3.4 + TODO: fill this out + + Bad: + 1 # minimum two numbers + 1.2a # release level must have a release serial + 1.2.3b + """ + def parse(self, s): + result = _normalized_key(s) + # _normalized_key loses trailing zeroes in the release + # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 + # However, PEP 440 prefix matching needs it: for example, + # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). + m = PEP440_VERSION_RE.match(s) # must succeed + groups = m.groups() + self._release_clause = tuple(int(v) for v in groups[1].split('.')) + return result + + PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + + @property + def is_prerelease(self): + return any(t[0] in self.PREREL_TAGS for t in self._parts if t) + + +def _match_prefix(x, y): + x = str(x) + y = str(y) + if x == y: + return True + if not x.startswith(y): + return False + n = len(y) + return x[n] == '.' + + +class NormalizedMatcher(Matcher): + version_class = NormalizedVersion + + # value is either a callable or the name of a method + _operators = { + '~=': '_match_compatible', + '<': '_match_lt', + '>': '_match_gt', + '<=': '_match_le', + '>=': '_match_ge', + '==': '_match_eq', + '===': '_match_arbitrary', + '!=': '_match_ne', + } + + def _adjust_local(self, version, constraint, prefix): + if prefix: + strip_local = '+' not in constraint and version._parts[-1] + else: + # both constraint and version are + # NormalizedVersion instances. + # If constraint does not have a local component, + # ensure the version doesn't, either. + strip_local = not constraint._parts[-1] and version._parts[-1] + if strip_local: + s = version._string.split('+', 1)[0] + version = self.version_class(s) + return version, constraint + + def _match_lt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version >= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_gt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version <= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_le(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version <= constraint + + def _match_ge(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version >= constraint + + def _match_eq(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version == constraint) + else: + result = _match_prefix(version, constraint) + return result + + def _match_arbitrary(self, version, constraint, prefix): + return str(version) == str(constraint) + + def _match_ne(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version != constraint) + else: + result = not _match_prefix(version, constraint) + return result + + def _match_compatible(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version == constraint: + return True + if version < constraint: + return False +# if not prefix: +# return True + release_clause = constraint._release_clause + if len(release_clause) > 1: + release_clause = release_clause[:-1] + pfx = '.'.join([str(i) for i in release_clause]) + return _match_prefix(version, pfx) + +_REPLACEMENTS = ( + (re.compile('[.+-]$'), ''), # remove trailing puncts + (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start + (re.compile('^[.-]'), ''), # remove leading puncts + (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses + (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha + (re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha'), # standardise + (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses +) + +_SUFFIX_REPLACEMENTS = ( + (re.compile('^[:~._+-]+'), ''), # remove leading puncts + (re.compile('[,*")([\\]]'), ''), # remove unwanted chars + (re.compile('[~:+_ -]'), '.'), # replace illegal chars + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\.$'), ''), # trailing '.' +) + +_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') + + +def _suggest_semantic_version(s): + """ + Try to suggest a semantic form for a version for which + _suggest_normalized_version couldn't come up with anything. + """ + result = s.strip().lower() + for pat, repl in _REPLACEMENTS: + result = pat.sub(repl, result) + if not result: + result = '0.0.0' + + # Now look for numeric prefix, and separate it out from + # the rest. + #import pdb; pdb.set_trace() + m = _NUMERIC_PREFIX.match(result) + if not m: + prefix = '0.0.0' + suffix = result + else: + prefix = m.groups()[0].split('.') + prefix = [int(i) for i in prefix] + while len(prefix) < 3: + prefix.append(0) + if len(prefix) == 3: + suffix = result[m.end():] + else: + suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] + prefix = prefix[:3] + prefix = '.'.join([str(i) for i in prefix]) + suffix = suffix.strip() + if suffix: + #import pdb; pdb.set_trace() + # massage the suffix. + for pat, repl in _SUFFIX_REPLACEMENTS: + suffix = pat.sub(repl, suffix) + + if not suffix: + result = prefix + else: + sep = '-' if 'dev' in suffix else '+' + result = prefix + sep + suffix + if not is_semver(result): + result = None + return result + + +def _suggest_normalized_version(s): + """Suggest a normalized version close to the given version string. + + If you have a version string that isn't rational (i.e. NormalizedVersion + doesn't like it) then you might be able to get an equivalent (or close) + rational version from this function. + + This does a number of simple normalizations to the given string, based + on observation of versions currently in use on PyPI. Given a dump of + those version during PyCon 2009, 4287 of them: + - 2312 (53.93%) match NormalizedVersion without change + with the automatic suggestion + - 3474 (81.04%) match when using this suggestion method + + @param s {str} An irrational version string. + @returns A rational version string, or None, if couldn't determine one. + """ + try: + _normalized_key(s) + return s # already rational + except UnsupportedVersionError: + pass + + rs = s.lower() + + # part of this could use maketrans + for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', '')): + rs = rs.replace(orig, repl) + + # if something ends with dev or pre, we add a 0 + rs = re.sub(r"pre$", r"pre0", rs) + rs = re.sub(r"dev$", r"dev0", rs) + + # if we have something like "b-2" or "a.2" at the end of the + # version, that is probably beta, alpha, etc + # let's remove the dash or dot + rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + + # 1.0-dev-r371 -> 1.0.dev371 + # 0.1-dev-r79 -> 0.1.dev79 + rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + + # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 + rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + + # Clean: v0.3, v1.0 + if rs.startswith('v'): + rs = rs[1:] + + # Clean leading '0's on numbers. + #TODO: unintended side-effect on, e.g., "2003.05.09" + # PyPI stats: 77 (~2%) better + rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + + # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers + # zero. + # PyPI stats: 245 (7.56%) better + rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + + # the 'dev-rNNN' tag is a dev tag + rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + + # clean the - when used as a pre delimiter + rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + + # a terminal "dev" or "devel" can be changed into ".dev0" + rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + + # a terminal "dev" can be changed into ".dev0" + rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + + # a terminal "final" or "stable" can be removed + rs = re.sub(r"(final|stable)$", "", rs) + + # The 'r' and the '-' tags are post release tags + # 0.4a1.r10 -> 0.4a1.post10 + # 0.9.33-17222 -> 0.9.33.post17222 + # 0.9.33-r17222 -> 0.9.33.post17222 + rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + + # Clean 'r' instead of 'dev' usage: + # 0.9.33+r17222 -> 0.9.33.dev17222 + # 1.0dev123 -> 1.0.dev123 + # 1.0.git123 -> 1.0.dev123 + # 1.0.bzr123 -> 1.0.dev123 + # 0.1a0dev.123 -> 0.1a0.dev123 + # PyPI stats: ~150 (~4%) better + rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + + # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: + # 0.2.pre1 -> 0.2c1 + # 0.2-c1 -> 0.2c1 + # 1.0preview123 -> 1.0c123 + # PyPI stats: ~21 (0.62%) better + rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + + # Tcl/Tk uses "px" for their post release markers + rs = re.sub(r"p(\d+)$", r".post\1", rs) + + try: + _normalized_key(rs) + except UnsupportedVersionError: + rs = None + return rs + +# +# Legacy version processing (distribute-compatible) +# + +_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) +_VERSION_REPLACE = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + '': None, + '.': None, +} + + +def _legacy_key(s): + def get_parts(s): + result = [] + for p in _VERSION_PART.split(s.lower()): + p = _VERSION_REPLACE.get(p, p) + if p: + if '0' <= p[:1] <= '9': + p = p.zfill(8) + else: + p = '*' + p + result.append(p) + result.append('*final') + return result + + result = [] + for p in get_parts(s): + if p.startswith('*'): + if p < '*final': + while result and result[-1] == '*final-': + result.pop() + while result and result[-1] == '00000000': + result.pop() + result.append(p) + return tuple(result) + + +class LegacyVersion(Version): + def parse(self, s): + return _legacy_key(s) + + @property + def is_prerelease(self): + result = False + for x in self._parts: + if (isinstance(x, string_types) and x.startswith('*') and + x < '*final'): + result = True + break + return result + + +class LegacyMatcher(Matcher): + version_class = LegacyVersion + + _operators = dict(Matcher._operators) + _operators['~='] = '_match_compatible' + + numeric_re = re.compile(r'^(\d+(\.\d+)*)') + + def _match_compatible(self, version, constraint, prefix): + if version < constraint: + return False + m = self.numeric_re.match(str(constraint)) + if not m: + logger.warning('Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint) + return True + s = m.groups()[0] + if '.' in s: + s = s.rsplit('.', 1)[0] + return _match_prefix(version, s) + +# +# Semantic versioning +# + +_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + + +def is_semver(s): + return _SEMVER_RE.match(s) + + +def _semantic_key(s): + def make_tuple(s, absent): + if s is None: + result = (absent,) + else: + parts = s[1:].split('.') + # We can't compare ints and strings on Python 3, so fudge it + # by zero-filling numeric values so simulate a numeric comparison + result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) + return result + + m = is_semver(s) + if not m: + raise UnsupportedVersionError(s) + groups = m.groups() + major, minor, patch = [int(i) for i in groups[:3]] + # choose the '|' and '*' so that versions sort correctly + pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') + return (major, minor, patch), pre, build + + +class SemanticVersion(Version): + def parse(self, s): + return _semantic_key(s) + + @property + def is_prerelease(self): + return self._parts[1][0] != '|' + + +class SemanticMatcher(Matcher): + version_class = SemanticVersion + + +class VersionScheme(object): + def __init__(self, key, matcher, suggester=None): + self.key = key + self.matcher = matcher + self.suggester = suggester + + def is_valid_version(self, s): + try: + self.matcher.version_class(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_matcher(self, s): + try: + self.matcher(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_constraint_list(self, s): + """ + Used for processing some metadata fields + """ + return self.is_valid_matcher('dummy_name (%s)' % s) + + def suggest(self, s): + if self.suggester is None: + result = None + else: + result = self.suggester(s) + return result + +_SCHEMES = { + 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, + _suggest_normalized_version), + 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), + 'semantic': VersionScheme(_semantic_key, SemanticMatcher, + _suggest_semantic_version), +} + +_SCHEMES['default'] = _SCHEMES['normalized'] + + +def get_scheme(name): + if name not in _SCHEMES: + raise ValueError('unknown scheme name: %r' % name) + return _SCHEMES[name] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w32.exe b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000000000000000000000000000000000000..e6439e9e45897365d5ac6a85a46864c158a225fd GIT binary patch literal 90112 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�gPVRVBXt_FM7e4<}XM;HRH{62JZSn^<*L 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 OB+yBqlfeHwf&T)jA)JE% literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w64.exe b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000000000000000000000000000000000000..46139dbf9400b7bc0b64e6756ce17b4eb5fd7436 GIT binary patch literal 99840 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[^-]+) +-(?P\d+[^-]*) +(-(?P\d+[^-]*))? +-(?P\w+\d+(\.\w+\d+)*) +-(?P\w+) +-(?P\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P[^-]+) +-(?P\d+[^-]*) +(-(?P\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') +SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') +SHEBANG_PYTHON = b'#!python' +SHEBANG_PYTHONW = b'#!pythonw' + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = imp.load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.should_verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + # Reinstate the local version separator + self.version = info['vn'].replace('_', '-') + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + # replace - with _ as a local version separator + version = self.version.replace('-', '_') + return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, + pyver, abi, arch) + + @property + def exists(self): + path = os.path.join(self.dirname, self.filename) + return os.path.isfile(path) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + wheel_metadata = self.get_wheel_metadata(zf) + wv = wheel_metadata['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # if file_version < (1, 1): + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, + # LEGACY_METADATA_FILENAME] + # else: + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] + result = None + for fn in fns: + try: + metadata_filename = posixpath.join(info_dir, fn) + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + if result: + break + except KeyError: + pass + if not result: + raise ValueError('Invalid wheel, because metadata is ' + 'missing: looked in %s' % ', '.join(fns)) + return result + + def get_wheel_metadata(self, zf): + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + with zf.open(metadata_filename) as bf: + wf = codecs.getreader('utf-8')(bf) + message = message_from_file(wf) + return dict(message) + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'r') as zf: + result = self.get_wheel_metadata(zf) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + end = m.end() + shebang, data_after_shebang = data[:end], data[end:] + # Preserve any arguments after the interpreter + if b'pythonw' in shebang.lower(): + shebang_python = SHEBANG_PYTHONW + else: + shebang_python = SHEBANG_PYTHON + m = SHEBANG_DETAIL_RE.match(shebang) + if m: + args = b' ' + m.groups()[-1] + else: + args = b'' + shebang = shebang_python + args + data = shebang + data_after_shebang + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = SHEBANG_PYTHON + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, base): + records = list(records) # make a copy, as mutated + p = to_posix(os.path.relpath(record_path, base)) + records.append((p, '', '')) + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + + def write_records(self, info, libdir, archive_paths): + records = [] + distinfo, info_dir = info + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + self.write_record(records, p, libdir) + ap = to_posix(os.path.join(info_dir, 'RECORD')) + archive_paths.append((ap, p)) + + def build_zip(self, pathname, archive_paths): + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # sort the entries by archive path. Not needed by any spec, but it + # keeps the archive listing and RECORD tidier than they would otherwise + # be. Use the number of path segments to keep directory entries together, + # and keep the dist-info stuff at the end. + def sorter(t): + ap = t[0] + n = ap.count('/') + if '.dist-info' in ap: + n += 10000 + return (n, ap) + archive_paths = sorted(archive_paths, key=sorter) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + self.write_records((distinfo, info_dir), libdir, archive_paths) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + self.build_zip(pathname, archive_paths) + return pathname + + def skip_entry(self, arcname): + """ + Determine whether an archive entry should be skipped when verifying + or installing. + """ + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + # We also skip directories, as they won't be in RECORD + # either. See: + # + # https://github.com/pypa/wheel/issues/294 + # https://github.com/pypa/wheel/issues/287 + # https://github.com/pypa/wheel/pull/289 + # + return arcname.endswith(('/', '/RECORD.jws')) + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. If kwarg ``bytecode_hashed_invalidation`` is True, written + bytecode will try to use file-hash based invalidation (PEP-552) on + supported interpreter versions (CPython 2.7+). + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if self.skip_entry(u_arcname): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile, + hashed_invalidation=bc_hashed_invalidation) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' [%s]' % ','.join(v.flags) + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('extensions') + if commands: + commands = commands.get('python.commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + global cache + if cache is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('dylib-cache'), + '%s.%s' % sys.version_info[:2]) + cache = Cache(base) + return cache + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache = self._get_dylib_cache() + prefix = cache.prefix_to_dir(pathname) + cache_base = os.path.join(cache.base, prefix) + if not os.path.isdir(cache_base): + os.makedirs(cache_base) + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def is_compatible(self): + """ + Determine if a wheel is compatible with the running system. + """ + return is_compatible(self) + + def is_mountable(self): + """ + Determine if a wheel is asserted as mountable by its metadata. + """ + return True # for now - metadata details TBD + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not self.is_compatible(): + msg = 'Wheel %s not compatible with this Python.' % pathname + raise DistlibException(msg) + if not self.is_mountable(): + msg = 'Wheel %s is marked as not mountable.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + def verify(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # TODO version verification + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # See issue #115: some wheels have .. in their entries, but + # in the filename ... e.g. __main__..py ! So the check is + # updated to look for .. in the directory portions + p = u_arcname.split('/') + if '..' in p: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + if self.skip_entry(u_arcname): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + def update(self, modifier, dest_dir=None, **kwargs): + """ + Update the contents of a wheel in a generic way. The modifier should + be a callable which expects a dictionary argument: its keys are + archive-entry paths, and its values are absolute filesystem paths + where the contents the corresponding archive entries can be found. The + modifier is free to change the contents of the files pointed to, add + new entries and remove entries, before returning. This method will + extract the entire contents of the wheel to a temporary location, call + the modifier, and then use the passed (and possibly updated) + dictionary to write a new wheel. If ``dest_dir`` is specified, the new + wheel is written there -- otherwise, the original wheel is overwritten. + + The modifier should return True if it updated the wheel, else False. + This method returns the same value the modifier returns. + """ + + def get_version(path_map, info_dir): + version = path = None + key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) + if key not in path_map: + key = '%s/PKG-INFO' % info_dir + if key in path_map: + path = path_map[key] + version = Metadata(path=path).version + return version, path + + def update_version(version, path): + updated = None + try: + v = NormalizedVersion(version) + i = version.find('-') + if i < 0: + updated = '%s+1' % version + else: + parts = [int(s) for s in version[i + 1:].split('.')] + parts[-1] += 1 + updated = '%s+%s' % (version[:i], + '.'.join(str(i) for i in parts)) + except UnsupportedVersionError: + logger.debug('Cannot update non-compliant (PEP-440) ' + 'version %r', version) + if updated: + md = Metadata(path=path) + md.version = updated + legacy = path.endswith(LEGACY_METADATA_FILENAME) + md.write(path=path, legacy=legacy) + logger.debug('Version updated from %r to %r', version, + updated) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + record_name = posixpath.join(info_dir, 'RECORD') + with tempdir() as workdir: + with ZipFile(pathname, 'r') as zf: + path_map = {} + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if u_arcname == record_name: + continue + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + zf.extract(zinfo, workdir) + path = os.path.join(workdir, convert_path(u_arcname)) + path_map[u_arcname] = path + + # Remember the version. + original_version, _ = get_version(path_map, info_dir) + # Files extracted. Call the modifier. + modified = modifier(path_map, **kwargs) + if modified: + # Something changed - need to build a new wheel. + current_version, path = get_version(path_map, info_dir) + if current_version and (current_version == original_version): + # Add or update local version to signify changes. + update_version(current_version, path) + # Decide where the new wheel goes. + if dest_dir is None: + fd, newpath = tempfile.mkstemp(suffix='.whl', + prefix='wheel-update-', + dir=workdir) + os.close(fd) + else: + if not os.path.isdir(dest_dir): + raise DistlibException('Not a directory: %r' % dest_dir) + newpath = os.path.join(dest_dir, self.filename) + archive_paths = list(path_map.items()) + distinfo = os.path.join(workdir, info_dir) + info = distinfo, info_dir + self.write_records(info, workdir, archive_paths) + self.build_zip(newpath, archive_paths) + if dest_dir is None: + shutil.copyfile(newpath, pathname) + return modified + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + arches = [ARCH] + if sys.platform == 'darwin': + m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) + if m: + name, major, minor, arch = m.groups() + minor = int(minor) + matches = [arch] + if arch in ('i386', 'ppc'): + matches.append('fat') + if arch in ('i386', 'ppc', 'x86_64'): + matches.append('fat3') + if arch in ('ppc64', 'x86_64'): + matches.append('fat64') + if arch in ('i386', 'x86_64'): + matches.append('intel') + if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): + matches.append('universal') + while minor >= 0: + for match in matches: + s = '%s_%s_%s_%s' % (name, major, minor, match) + if s != ARCH: # already there + arches.append(s) + minor -= 1 + + # Most specific - our Python version, ABI and arch + for abi in abis: + for arch in arches: + result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + return set(result) + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distro.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distro.py new file mode 100644 index 0000000..0611b62 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/distro.py @@ -0,0 +1,1230 @@ +# Copyright 2015,2016,2017 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is the recommended replacement for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.8 will remove it altogether. +Its predecessor function :py:func:`platform.dist` was already +deprecated since Python 2.6 and will also be removed in Python 3.8. +Still, there are many cases in which access to OS distribution information +is needed. See `Python issue 1322 `_ for +more information. +""" + +import os +import re +import sys +import json +import shlex +import logging +import argparse +import subprocess + + +_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') +_OS_RELEASE_BASENAME = 'os-release' + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = { + 'ol': 'oracle', # Oracle Linux +} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + 'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4 + 'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5 + 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation + 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server + 'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + 'redhat': 'rhel', # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( + r'(\w+)[-_](release|version)$') + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + 'debian_version', + 'lsb-release', + 'oem-release', + _OS_RELEASE_BASENAME, + 'system-release', + 'plesk-release', +) + + +def linux_distribution(full_distribution_name=True): + """ + Return information about the current OS distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the OS distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular OS distributions. + """ + return _distro.linux_distribution(full_distribution_name) + + +def id(): + """ + Return the distro ID of the current distribution, as a + machine-readable string. + + For a number of OS distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amazon" Amazon Linux + "arch" Arch Linux + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + "openbsd" OpenBSD + "netbsd" NetBSD + "freebsd" FreeBSD + "midnightbsd" MidnightBSD + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the OS distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty=False): + """ + Return the name of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "" field of the distro release file, appended + with the value of the pretty version ("" and "" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty=False, best=False): + """ + Return the version of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best=False): + """ + Return the version of the current OS distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best=False): + """ + Return the major version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best=False): + """ + Return the minor version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best=False): + """ + Return the build number of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like(): + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current OS distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + `_. + """ + return _distro.like() + + +def codename(): + """ + Return the codename for the release of the current OS distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty=False, best=False): + """ + Return certain machine-readable information items about the current OS + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current OS distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current OS distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def uname_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + """ + return _distro.uname_info() + + +def os_release_attr(attribute): + """ + Return a single named information item from the os-release file data source + of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute): + """ + Return a single named information item from the lsb_release command output + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +def uname_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + """ + return _distro.uname_attr(attribute) + + +class cached_property(object): + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + def __init__(self, f): + self._fname = f.__name__ + self._f = f + + def __get__(self, obj, owner): + assert obj is not None, 'call {} on an instance'.format(self._fname) + ret = obj.__dict__[self._fname] = self._f(obj) + return ret + + +class LinuxDistribution(object): + """ + Provides information about a OS distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current OS distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__(self, + include_lsb=True, + os_release_file='', + distro_release_file='', + include_uname=True): + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + * ``include_uname`` (bool): Controls whether uname command output is + included as a data source. If the uname command is not available in + the program execution path the data source for the uname command will + be empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. + This controls whether the lsb information will be loaded. + + * ``include_uname`` (bool): The result of the ``include_uname`` + parameter. This controls whether the uname information will + be loaded. + + Raises: + + * :py:exc:`IOError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had + some issue (other than not being available in the program execution + path). + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.os_release_file = os_release_file or \ + os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) + self.distro_release_file = distro_release_file or '' # updated later + self.include_lsb = include_lsb + self.include_uname = include_uname + + def __repr__(self): + """Return repr of all info + """ + return \ + "LinuxDistribution(" \ + "os_release_file={self.os_release_file!r}, " \ + "distro_release_file={self.distro_release_file!r}, " \ + "include_lsb={self.include_lsb!r}, " \ + "include_uname={self.include_uname!r}, " \ + "_os_release_info={self._os_release_info!r}, " \ + "_lsb_release_info={self._lsb_release_info!r}, " \ + "_distro_release_info={self._distro_release_info!r}, " \ + "_uname_info={self._uname_info!r})".format( + self=self) + + def linux_distribution(self, full_distribution_name=True): + """ + Return information about the OS distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self.codename() + ) + + def id(self): + """Return the distro ID of the OS distribution, as a string. + + For details, see :func:`distro.id`. + """ + def normalize(distro_id, table): + distro_id = distro_id.lower().replace(' ', '_') + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr('distributor_id') + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + distro_id = self.uname_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return '' + + def name(self, pretty=False): + """ + Return the name of the OS distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = self.os_release_attr('name') \ + or self.lsb_release_attr('distributor_id') \ + or self.distro_release_attr('name') \ + or self.uname_attr('name') + if pretty: + name = self.os_release_attr('pretty_name') \ + or self.lsb_release_attr('description') + if not name: + name = self.distro_release_attr('name') \ + or self.uname_attr('name') + version = self.version(pretty=True) + if version: + name = name + ' ' + version + return name or '' + + def version(self, pretty=False, best=False): + """ + Return the version of the OS distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr('version_id'), + self.lsb_release_attr('release'), + self.distro_release_attr('version_id'), + self._parse_distro_release_content( + self.os_release_attr('pretty_name')).get('version_id', ''), + self._parse_distro_release_content( + self.lsb_release_attr('description')).get('version_id', ''), + self.uname_attr('release') + ] + version = '' + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == '': + version = v + else: + for v in versions: + if v != '': + version = v + break + if pretty and version and self.codename(): + version = '{0} ({1})'.format(version, self.codename()) + return version + + def version_parts(self, best=False): + """ + Return the version of the OS distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or '', build_number or '' + return '', '', '' + + def major_version(self, best=False): + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best=False): + """ + Return the minor version number of the current distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best=False): + """ + Return the build number of the current distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self): + """ + Return the IDs of distributions that are like the OS distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr('id_like') or '' + + def codename(self): + """ + Return the codename of the OS distribution. + + For details, see :func:`distro.codename`. + """ + try: + # Handle os_release specially since distros might purposefully set + # this to empty string to have no codename + return self._os_release_info['codename'] + except KeyError: + return self.lsb_release_attr('codename') \ + or self.distro_release_attr('codename') \ + or '' + + def info(self, pretty=False, best=False): + """ + Return certain machine-readable information about the OS + distribution. + + For details, see :func:`distro.info`. + """ + return dict( + id=self.id(), + version=self.version(pretty, best), + version_parts=dict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best) + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the OS distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the OS + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the OS + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def uname_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the uname command data source of the OS distribution. + + For details, see :func:`distro.uname_info`. + """ + return self._uname_info + + def os_release_attr(self, attribute): + """ + Return a single named information item from the os-release file data + source of the OS distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, '') + + def lsb_release_attr(self, attribute): + """ + Return a single named information item from the lsb_release command + output data source of the OS distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, '') + + def distro_release_attr(self, attribute): + """ + Return a single named information item from the distro release file + data source of the OS distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, '') + + def uname_attr(self, attribute): + """ + Return a single named information item from the uname command + output data source of the OS distribution. + + For details, see :func:`distro.uname_release_attr`. + """ + return self._uname_info.get(attribute, '') + + @cached_property + def _os_release_info(self): + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file) as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines): + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + # The shlex module defines its `wordchars` variable using literals, + # making it dependent on the encoding of the Python source file. + # In Python 2.6 and 2.7, the shlex source file is encoded in + # 'iso-8859-1', and the `wordchars` variable is defined as a byte + # string. This causes a UnicodeDecodeError to be raised when the + # parsed content is a unicode object. The following fix resolves that + # (... but it should be fixed in shlex...): + if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): + lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + if '=' in token: + k, v = token.split('=', 1) + props[k.lower()] = v + else: + # Ignore any tokens that are not variable assignments + pass + + if 'version_codename' in props: + # os-release added a version_codename field. Use that in + # preference to anything else Note that some distros purposefully + # do not have code names. They should be setting + # version_codename="" + props['codename'] = props['version_codename'] + elif 'ubuntu_codename' in props: + # Same as above but a non-standard field name used on older Ubuntus + props['codename'] = props['ubuntu_codename'] + elif 'version' in props: + # If there is no version_codename, parse it from the version + codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) + if codename: + codename = codename.group() + codename = codename.strip('()') + codename = codename.strip(',') + codename = codename.strip() + # codename appears within paranthese. + props['codename'] = codename + + return props + + @cached_property + def _lsb_release_info(self): + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + if not self.include_lsb: + return {} + with open(os.devnull, 'w') as devnull: + try: + cmd = ('lsb_release', '-a') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: # Command not found + return {} + content = self._to_str(stdout).splitlines() + return self._parse_lsb_release_content(content) + + @staticmethod + def _parse_lsb_release_content(lines): + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + kv = line.strip('\n').split(':', 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(' ', '_').lower(): v.strip()}) + return props + + @cached_property + def _uname_info(self): + with open(os.devnull, 'w') as devnull: + try: + cmd = ('uname', '-rs') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: + return {} + content = self._to_str(stdout).splitlines() + return self._parse_uname_content(content) + + @staticmethod + def _parse_uname_content(lines): + props = {} + match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) + if match: + name, version = match.groups() + + # This is to prevent the Linux kernel version from + # appearing as the 'best' version on otherwise + # identifiable distributions. + if name == 'Linux': + return {} + props['id'] = name.lower() + props['name'] = name + props['release'] = version + return props + + @staticmethod + def _to_str(text): + encoding = sys.getfilesystemencoding() + encoding = 'utf-8' if encoding == 'ascii' else encoding + + if sys.version_info[0] >= 3: + if isinstance(text, bytes): + return text.decode(encoding) + else: + if isinstance(text, unicode): # noqa + return text.encode(encoding) + + return text + + @cached_property + def _distro_release_info(self): + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file( + self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if 'name' in distro_info \ + and 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' + elif match: + distro_info['id'] = match.group(1) + return distro_info + else: + try: + basenames = os.listdir(_UNIXCONFDIR) + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + except OSError: + # This may occur when /etc is not readable but we can't be + # sure about the *-release files. Check common entries of + # /etc for information. If they turn out to not be there the + # error is handled in `_parse_distro_release_file()`. + basenames = ['SuSE-release', + 'arch-release', + 'base-release', + 'centos-release', + 'fedora-release', + 'gentoo-release', + 'mageia-release', + 'mandrake-release', + 'mandriva-release', + 'mandrivalinux-release', + 'manjaro-release', + 'oracle-release', + 'redhat-release', + 'sl-release', + 'slackware-version'] + for basename in basenames: + if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: + continue + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + filepath = os.path.join(_UNIXCONFDIR, basename) + distro_info = self._parse_distro_release_file(filepath) + if 'name' in distro_info: + # The name is always present if the pattern matches + self.distro_release_file = filepath + distro_info['id'] = match.group(1) + if 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' + return distro_info + return {} + + def _parse_distro_release_file(self, filepath): + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + try: + with open(filepath) as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + except (OSError, IOError): + # Ignore not being able to read a specific, seemingly version + # related file. + # See https://github.com/nir0s/distro/issues/162 + return {} + + @staticmethod + def _parse_distro_release_content(line): + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( + line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info['name'] = matches.group(3)[::-1] + if matches.group(2): + distro_info['version_id'] = matches.group(2)[::-1] + if matches.group(1): + distro_info['codename'] = matches.group(1)[::-1] + elif line: + distro_info['name'] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main(): + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="OS distro info tool") + parser.add_argument( + '--json', + '-j', + help="Output in machine readable format", + action="store_true") + args = parser.parse_args() + + if args.json: + logger.info(json.dumps(info(), indent=4, sort_keys=True)) + else: + logger.info('Name: %s', name(pretty=True)) + distribution_version = version(pretty=True) + logger.info('Version: %s', distribution_version) + distribution_codename = codename() + logger.info('Codename: %s', distribution_codename) + + +if __name__ == '__main__': + main() diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py new file mode 100644 index 0000000..d1d82f1 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py @@ -0,0 +1,35 @@ +""" +HTML parsing library based on the `WHATWG HTML specification +`_. The parser is designed to be compatible with +existing HTML found in the wild and implements well-defined error recovery that +is largely compatible with modern desktop web browsers. + +Example usage:: + + from pip._vendor import html5lib + with open("my_document.html", "rb") as f: + tree = html5lib.parse(f) + +For convenience, this module re-exports the following names: + +* :func:`~.html5parser.parse` +* :func:`~.html5parser.parseFragment` +* :class:`~.html5parser.HTMLParser` +* :func:`~.treebuilders.getTreeBuilder` +* :func:`~.treewalkers.getTreeWalker` +* :func:`~.serializer.serialize` +""" + +from __future__ import absolute_import, division, unicode_literals + +from .html5parser import HTMLParser, parse, parseFragment +from .treebuilders import getTreeBuilder +from .treewalkers import getTreeWalker +from .serializer import serialize + +__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", + "getTreeWalker", "serialize"] + +# this has to be at the top level, see how setup.py parses this +#: Distribution version number. +__version__ = "1.1" diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e37260602e3679f10e41ab7d36c86d252a064466 GIT binary patch literal 1338 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad46a4d31c5646cbafc4a53360f2df53bb02a4c5 GIT binary patch literal 21666 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�#w!}DSIN2dZqrOfYkp1Pzg?= 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 A(*OVf literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb7782e53d0b3b6ec05b504f67e3cd8fbf0c9621 GIT binary patch literal 91047 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py new file mode 100644 index 0000000..3ff803c --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -0,0 +1,289 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +import warnings + +from .constants import DataLossWarning + +baseChar = """ +[#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | +[#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] | [#x0141-#x0148] | +[#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] | +[#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 | +[#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | +[#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | +[#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | +[#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | +[#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | +[#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] | [#x0621-#x063A] | +[#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] | [#x06C0-#x06CE] | +[#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | +[#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] | +[#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] | +[#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | +[#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] | +[#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | +[#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | +[#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | +[#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] | [#x0B2A-#x0B30] | +[#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D | [#x0B5C-#x0B5D] | +[#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | +[#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | +[#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] | +[#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | [#x0C35-#x0C39] | +[#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | +[#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | +[#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] | +[#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] | +[#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] | #x0E8A | +#x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] | [#x0EA1-#x0EA3] | #x0EA5 | +#x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | +#x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] | +[#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] | +[#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] | #x113C | +#x113E | #x1140 | #x114C | #x114E | #x1150 | [#x1154-#x1155] | #x1159 | +[#x115F-#x1161] | #x1163 | #x1165 | #x1167 | #x1169 | [#x116D-#x116E] | +[#x1172-#x1173] | #x1175 | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | +[#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 | +[#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] | [#x1F18-#x1F1D] | +[#x1F20-#x1F45] | [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 | #x1F5B | +#x1F5D | [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | +[#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | +[#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | +[#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] | +[#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3]""" + +ideographic = """[#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]""" + +combiningCharacter = """ +[#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] | [#x0591-#x05A1] | +[#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 | +[#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] | +[#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | +#x093C | [#x093E-#x094C] | #x094D | [#x0951-#x0954] | [#x0962-#x0963] | +[#x0981-#x0983] | #x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | +[#x09C7-#x09C8] | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | +#x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] | +[#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC | +[#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | +#x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | +[#x0B56-#x0B57] | [#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | +[#x0BCA-#x0BCD] | #x0BD7 | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | +[#x0C46-#x0C48] | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] | +[#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | +[#x0D02-#x0D03] | [#x0D3E-#x0D43] | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | +#x0D57 | #x0E31 | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | +[#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | +#x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] | +[#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | +[#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | +#x3099 | #x309A""" + +digit = """ +[#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | [#x0966-#x096F] | +[#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | +[#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] | +[#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29]""" + +extender = """ +#x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | +#[#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]""" + +letter = " | ".join([baseChar, ideographic]) + +# Without the +name = " | ".join([letter, digit, ".", "-", "_", combiningCharacter, + extender]) +nameFirst = " | ".join([letter, "_"]) + +reChar = re.compile(r"#x([\d|A-F]{4,4})") +reCharRange = re.compile(r"\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]") + + +def charStringToList(chars): + charRanges = [item.strip() for item in chars.split(" | ")] + rv = [] + for item in charRanges: + foundMatch = False + for regexp in (reChar, reCharRange): + match = regexp.match(item) + if match is not None: + rv.append([hexToInt(item) for item in match.groups()]) + if len(rv[-1]) == 1: + rv[-1] = rv[-1] * 2 + foundMatch = True + break + if not foundMatch: + assert len(item) == 1 + + rv.append([ord(item)] * 2) + rv = normaliseCharList(rv) + return rv + + +def normaliseCharList(charList): + charList = sorted(charList) + for item in charList: + assert item[1] >= item[0] + rv = [] + i = 0 + while i < len(charList): + j = 1 + rv.append(charList[i]) + while i + j < len(charList) and charList[i + j][0] <= rv[-1][1] + 1: + rv[-1][1] = charList[i + j][1] + j += 1 + i += j + return rv + + +# We don't really support characters above the BMP :( +max_unicode = int("FFFF", 16) + + +def missingRanges(charList): + rv = [] + if charList[0] != 0: + rv.append([0, charList[0][0] - 1]) + for i, item in enumerate(charList[:-1]): + rv.append([item[1] + 1, charList[i + 1][0] - 1]) + if charList[-1][1] != max_unicode: + rv.append([charList[-1][1] + 1, max_unicode]) + return rv + + +def listToRegexpStr(charList): + rv = [] + for item in charList: + if item[0] == item[1]: + rv.append(escapeRegexp(chr(item[0]))) + else: + rv.append(escapeRegexp(chr(item[0])) + "-" + + escapeRegexp(chr(item[1]))) + return "[%s]" % "".join(rv) + + +def hexToInt(hex_str): + return int(hex_str, 16) + + +def escapeRegexp(string): + specialCharacters = (".", "^", "$", "*", "+", "?", "{", "}", + "[", "]", "|", "(", ")", "-") + for char in specialCharacters: + string = string.replace(char, "\\" + char) + + return string + +# output from the above +nonXmlNameBMPRegexp = re.compile('[\x00-,/:-@\\[-\\^`\\{-\xb6\xb8-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u02cf\u02d2-\u02ff\u0346-\u035f\u0362-\u0385\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482\u0487-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u0590\u05a2\u05ba\u05be\u05c0\u05c3\u05c5-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u063f\u0653-\u065f\u066a-\u066f\u06b8-\u06b9\u06bf\u06cf\u06d4\u06e9\u06ee-\u06ef\u06fa-\u0900\u0904\u093a-\u093b\u094e-\u0950\u0955-\u0957\u0964-\u0965\u0970-\u0980\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09bd\u09c5-\u09c6\u09c9-\u09ca\u09ce-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09f2-\u0a01\u0a03-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a58\u0a5d\u0a5f-\u0a65\u0a75-\u0a80\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0adf\u0ae1-\u0ae5\u0af0-\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3b\u0b44-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b62-\u0b65\u0b70-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bd6\u0bd8-\u0be6\u0bf0-\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c3d\u0c45\u0c49\u0c4e-\u0c54\u0c57-\u0c5f\u0c62-\u0c65\u0c70-\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbd\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce2-\u0ce5\u0cf0-\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d3d\u0d44-\u0d45\u0d49\u0d4e-\u0d56\u0d58-\u0d5f\u0d62-\u0d65\u0d70-\u0e00\u0e2f\u0e3b-\u0e3f\u0e4f\u0e5a-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0f17\u0f1a-\u0f1f\u0f2a-\u0f34\u0f36\u0f38\u0f3a-\u0f3d\u0f48\u0f6a-\u0f70\u0f85\u0f8c-\u0f8f\u0f96\u0f98\u0fae-\u0fb0\u0fb8\u0fba-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u20cf\u20dd-\u20e0\u20e2-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3004\u3006\u3008-\u3020\u3030\u3036-\u3040\u3095-\u3098\u309b-\u309c\u309f-\u30a0\u30fb\u30ff-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +nonXmlNameFirstBMPRegexp = re.compile('[\x00-@\\[-\\^`\\{-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u0385\u0387\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u0640\u064b-\u0670\u06b8-\u06b9\u06bf\u06cf\u06d4\u06d6-\u06e4\u06e7-\u0904\u093a-\u093c\u093e-\u0957\u0962-\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09db\u09de\u09e2-\u09ef\u09f2-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a58\u0a5d\u0a5f-\u0a71\u0a75-\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abc\u0abe-\u0adf\u0ae1-\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3c\u0b3e-\u0b5b\u0b5e\u0b62-\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c5f\u0c62-\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cdd\u0cdf\u0ce2-\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d5f\u0d62-\u0e00\u0e2f\u0e31\u0e34-\u0e3f\u0e46-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eb1\u0eb4-\u0ebc\u0ebe-\u0ebf\u0ec5-\u0f3f\u0f48\u0f6a-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3006\u3008-\u3020\u302a-\u3040\u3095-\u30a0\u30fb-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +# Simpler things +nonPubidCharRegexp = re.compile("[^\x20\x0D\x0Aa-zA-Z0-9\\-'()+,./:=?;!*#@$_%]") + + +class InfosetFilter(object): + replacementRegexp = re.compile(r"U[\dA-F]{5,5}") + + def __init__(self, + dropXmlnsLocalName=False, + dropXmlnsAttrNs=False, + preventDoubleDashComments=False, + preventDashAtCommentEnd=False, + replaceFormFeedCharacters=True, + preventSingleQuotePubid=False): + + self.dropXmlnsLocalName = dropXmlnsLocalName + self.dropXmlnsAttrNs = dropXmlnsAttrNs + + self.preventDoubleDashComments = preventDoubleDashComments + self.preventDashAtCommentEnd = preventDashAtCommentEnd + + self.replaceFormFeedCharacters = replaceFormFeedCharacters + + self.preventSingleQuotePubid = preventSingleQuotePubid + + self.replaceCache = {} + + def coerceAttribute(self, name, namespace=None): + if self.dropXmlnsLocalName and name.startswith("xmlns:"): + warnings.warn("Attributes cannot begin with xmlns", DataLossWarning) + return None + elif (self.dropXmlnsAttrNs and + namespace == "http://www.w3.org/2000/xmlns/"): + warnings.warn("Attributes cannot be in the xml namespace", DataLossWarning) + return None + else: + return self.toXmlName(name) + + def coerceElement(self, name): + return self.toXmlName(name) + + def coerceComment(self, data): + if self.preventDoubleDashComments: + while "--" in data: + warnings.warn("Comments cannot contain adjacent dashes", DataLossWarning) + data = data.replace("--", "- -") + if data.endswith("-"): + warnings.warn("Comments cannot end in a dash", DataLossWarning) + data += " " + return data + + def coerceCharacters(self, data): + if self.replaceFormFeedCharacters: + for _ in range(data.count("\x0C")): + warnings.warn("Text cannot contain U+000C", DataLossWarning) + data = data.replace("\x0C", " ") + # Other non-xml characters + return data + + def coercePubid(self, data): + dataOutput = data + for char in nonPubidCharRegexp.findall(data): + warnings.warn("Coercing non-XML pubid", DataLossWarning) + replacement = self.getReplacementCharacter(char) + dataOutput = dataOutput.replace(char, replacement) + if self.preventSingleQuotePubid and dataOutput.find("'") >= 0: + warnings.warn("Pubid cannot contain single quote", DataLossWarning) + dataOutput = dataOutput.replace("'", self.getReplacementCharacter("'")) + return dataOutput + + def toXmlName(self, name): + nameFirst = name[0] + nameRest = name[1:] + m = nonXmlNameFirstBMPRegexp.match(nameFirst) + if m: + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) + nameFirstOutput = self.getReplacementCharacter(nameFirst) + else: + nameFirstOutput = nameFirst + + nameRestOutput = nameRest + replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) + for char in replaceChars: + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) + replacement = self.getReplacementCharacter(char) + nameRestOutput = nameRestOutput.replace(char, replacement) + return nameFirstOutput + nameRestOutput + + def getReplacementCharacter(self, char): + if char in self.replaceCache: + replacement = self.replaceCache[char] + else: + replacement = self.escapeChar(char) + return replacement + + def fromXmlName(self, name): + for item in set(self.replacementRegexp.findall(name)): + name = name.replace(item, self.unescapeChar(item)) + return name + + def escapeChar(self, char): + replacement = "U%05X" % ord(char) + self.replaceCache[char] = replacement + return replacement + + def unescapeChar(self, charcode): + return chr(int(charcode[1:], 16)) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py new file mode 100644 index 0000000..e0bb376 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -0,0 +1,918 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type +from pip._vendor.six.moves import http_client, urllib + +import codecs +import re +from io import BytesIO, StringIO + +from pip._vendor import webencodings + +from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase +from .constants import _ReparseException +from . import _utils + +# Non-unicode versions of constants for use in the pre-parser +spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) +asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) +asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase]) +spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"]) + + +invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]" # noqa + +if _utils.supports_lone_surrogates: + # Use one extra step of indirection and create surrogates with + # eval. Not using this indirection would introduce an illegal + # unicode literal on platforms not supporting such lone + # surrogates. + assert invalid_unicode_no_surrogate[-1] == "]" and invalid_unicode_no_surrogate.count("]") == 1 + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate[:-1] + + eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used + "]") +else: + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) + +non_bmp_invalid_codepoints = {0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF} + +ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") + +# Cache for charsUntil() +charsUntilRegEx = {} + + +class BufferedStream(object): + """Buffering for streams that do not have buffering of their own + + The buffer is implemented as a list of chunks on the assumption that + joining many strings will be slow since it is O(n**2) + """ + + def __init__(self, stream): + self.stream = stream + self.buffer = [] + self.position = [-1, 0] # chunk number, offset + + def tell(self): + pos = 0 + for chunk in self.buffer[:self.position[0]]: + pos += len(chunk) + pos += self.position[1] + return pos + + def seek(self, pos): + assert pos <= self._bufferedBytes() + offset = pos + i = 0 + while len(self.buffer[i]) < offset: + offset -= len(self.buffer[i]) + i += 1 + self.position = [i, offset] + + def read(self, bytes): + if not self.buffer: + return self._readStream(bytes) + elif (self.position[0] == len(self.buffer) and + self.position[1] == len(self.buffer[-1])): + return self._readStream(bytes) + else: + return self._readFromBuffer(bytes) + + def _bufferedBytes(self): + return sum([len(item) for item in self.buffer]) + + def _readStream(self, bytes): + data = self.stream.read(bytes) + self.buffer.append(data) + self.position[0] += 1 + self.position[1] = len(data) + return data + + def _readFromBuffer(self, bytes): + remainingBytes = bytes + rv = [] + bufferIndex = self.position[0] + bufferOffset = self.position[1] + while bufferIndex < len(self.buffer) and remainingBytes != 0: + assert remainingBytes > 0 + bufferedData = self.buffer[bufferIndex] + + if remainingBytes <= len(bufferedData) - bufferOffset: + bytesToRead = remainingBytes + self.position = [bufferIndex, bufferOffset + bytesToRead] + else: + bytesToRead = len(bufferedData) - bufferOffset + self.position = [bufferIndex, len(bufferedData)] + bufferIndex += 1 + rv.append(bufferedData[bufferOffset:bufferOffset + bytesToRead]) + remainingBytes -= bytesToRead + + bufferOffset = 0 + + if remainingBytes: + rv.append(self._readStream(remainingBytes)) + + return b"".join(rv) + + +def HTMLInputStream(source, **kwargs): + # Work around Python bug #20007: read(0) closes the connection. + # http://bugs.python.org/issue20007 + if (isinstance(source, http_client.HTTPResponse) or + # Also check for addinfourl wrapping HTTPResponse + (isinstance(source, urllib.response.addbase) and + isinstance(source.fp, http_client.HTTPResponse))): + isUnicode = False + elif hasattr(source, "read"): + isUnicode = isinstance(source.read(0), text_type) + else: + isUnicode = isinstance(source, text_type) + + if isUnicode: + encodings = [x for x in kwargs if x.endswith("_encoding")] + if encodings: + raise TypeError("Cannot set an encoding with a unicode input, set %r" % encodings) + + return HTMLUnicodeInputStream(source, **kwargs) + else: + return HTMLBinaryInputStream(source, **kwargs) + + +class HTMLUnicodeInputStream(object): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + _defaultChunkSize = 10240 + + def __init__(self, source): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + + if not _utils.supports_lone_surrogates: + # Such platforms will have already checked for such + # surrogate errors, so no need to do this checking. + self.reportCharacterErrors = None + elif len("\U0010FFFF") == 1: + self.reportCharacterErrors = self.characterErrorsUCS4 + else: + self.reportCharacterErrors = self.characterErrorsUCS2 + + # List of where new lines occur + self.newLines = [0] + + self.charEncoding = (lookupEncoding("utf-8"), "certain") + self.dataStream = self.openStream(source) + + self.reset() + + def reset(self): + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + self.errors = [] + + # number of (complete) lines in previous chunks + self.prevNumLines = 0 + # number of columns in the last line of the previous chunk + self.prevNumCols = 0 + + # Deal with CR LF and surrogates split over chunk boundaries + self._bufferedCharacter = None + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = StringIO(source) + + return stream + + def _position(self, offset): + chunk = self.chunk + nLines = chunk.count('\n', 0, offset) + positionLine = self.prevNumLines + nLines + lastLinePos = chunk.rfind('\n', 0, offset) + if lastLinePos == -1: + positionColumn = self.prevNumCols + offset + else: + positionColumn = offset - (lastLinePos + 1) + return (positionLine, positionColumn) + + def position(self): + """Returns (line, col) of the current position in the stream.""" + line, col = self._position(self.chunkOffset) + return (line + 1, col) + + def char(self): + """ Read one character from the stream or queue if available. Return + EOF when EOF is reached. + """ + # Read a new chunk from the input stream if necessary + if self.chunkOffset >= self.chunkSize: + if not self.readChunk(): + return EOF + + chunkOffset = self.chunkOffset + char = self.chunk[chunkOffset] + self.chunkOffset = chunkOffset + 1 + + return char + + def readChunk(self, chunkSize=None): + if chunkSize is None: + chunkSize = self._defaultChunkSize + + self.prevNumLines, self.prevNumCols = self._position(self.chunkSize) + + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + + data = self.dataStream.read(chunkSize) + + # Deal with CR LF and surrogates broken across chunks + if self._bufferedCharacter: + data = self._bufferedCharacter + data + self._bufferedCharacter = None + elif not data: + # We have no more data, bye-bye stream + return False + + if len(data) > 1: + lastv = ord(data[-1]) + if lastv == 0x0D or 0xD800 <= lastv <= 0xDBFF: + self._bufferedCharacter = data[-1] + data = data[:-1] + + if self.reportCharacterErrors: + self.reportCharacterErrors(data) + + # Replace invalid characters + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + + self.chunk = data + self.chunkSize = len(data) + + return True + + def characterErrorsUCS4(self, data): + for _ in range(len(invalid_unicode_re.findall(data))): + self.errors.append("invalid-codepoint") + + def characterErrorsUCS2(self, data): + # Someone picked the wrong compile option + # You lose + skip = False + for match in invalid_unicode_re.finditer(data): + if skip: + continue + codepoint = ord(match.group()) + pos = match.start() + # Pretty sure there should be endianness issues here + if _utils.isSurrogatePair(data[pos:pos + 2]): + # We have a surrogate pair! + char_val = _utils.surrogatePairToCodepoint(data[pos:pos + 2]) + if char_val in non_bmp_invalid_codepoints: + self.errors.append("invalid-codepoint") + skip = True + elif (codepoint >= 0xD800 and codepoint <= 0xDFFF and + pos == len(data) - 1): + self.errors.append("invalid-codepoint") + else: + skip = False + self.errors.append("invalid-codepoint") + + def charsUntil(self, characters, opposite=False): + """ Returns a string of characters from the stream up to but not + including any character in 'characters' or EOF. 'characters' must be + a container that supports the 'in' method and iteration over its + characters. + """ + + # Use a cache of regexps to find the required characters + try: + chars = charsUntilRegEx[(characters, opposite)] + except KeyError: + if __debug__: + for c in characters: + assert(ord(c) < 128) + regex = "".join(["\\x%02x" % ord(c) for c in characters]) + if not opposite: + regex = "^%s" % regex + chars = charsUntilRegEx[(characters, opposite)] = re.compile("[%s]+" % regex) + + rv = [] + + while True: + # Find the longest matching prefix + m = chars.match(self.chunk, self.chunkOffset) + if m is None: + # If nothing matched, and it wasn't because we ran out of chunk, + # then stop + if self.chunkOffset != self.chunkSize: + break + else: + end = m.end() + # If not the whole chunk matched, return everything + # up to the part that didn't match + if end != self.chunkSize: + rv.append(self.chunk[self.chunkOffset:end]) + self.chunkOffset = end + break + # If the whole remainder of the chunk matched, + # use it all and read the next chunk + rv.append(self.chunk[self.chunkOffset:]) + if not self.readChunk(): + # Reached EOF + break + + r = "".join(rv) + return r + + def unget(self, char): + # Only one character is allowed to be ungotten at once - it must + # be consumed again before any further call to unget + if char is not EOF: + if self.chunkOffset == 0: + # unget is called quite rarely, so it's a good idea to do + # more work here if it saves a bit of work in the frequently + # called char and charsUntil. + # So, just prepend the ungotten character onto the current + # chunk: + self.chunk = char + self.chunk + self.chunkSize += 1 + else: + self.chunkOffset -= 1 + assert self.chunk[self.chunkOffset] == char + + +class HTMLBinaryInputStream(HTMLUnicodeInputStream): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + def __init__(self, source, override_encoding=None, transport_encoding=None, + same_origin_parent_encoding=None, likely_encoding=None, + default_encoding="windows-1252", useChardet=True): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + # Raw Stream - for unicode objects this will encode to utf-8 and set + # self.charEncoding as appropriate + self.rawStream = self.openStream(source) + + HTMLUnicodeInputStream.__init__(self, self.rawStream) + + # Encoding Information + # Number of bytes to use when looking for a meta element with + # encoding information + self.numBytesMeta = 1024 + # Number of bytes to use when using detecting encoding using chardet + self.numBytesChardet = 100 + # Things from args + self.override_encoding = override_encoding + self.transport_encoding = transport_encoding + self.same_origin_parent_encoding = same_origin_parent_encoding + self.likely_encoding = likely_encoding + self.default_encoding = default_encoding + + # Determine encoding + self.charEncoding = self.determineEncoding(useChardet) + assert self.charEncoding[0] is not None + + # Call superclass + self.reset() + + def reset(self): + self.dataStream = self.charEncoding[0].codec_info.streamreader(self.rawStream, 'replace') + HTMLUnicodeInputStream.reset(self) + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = BytesIO(source) + + try: + stream.seek(stream.tell()) + except Exception: + stream = BufferedStream(stream) + + return stream + + def determineEncoding(self, chardet=True): + # BOMs take precedence over everything + # This will also read past the BOM if present + charEncoding = self.detectBOM(), "certain" + if charEncoding[0] is not None: + return charEncoding + + # If we've been overridden, we've been overridden + charEncoding = lookupEncoding(self.override_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Now check the transport layer + charEncoding = lookupEncoding(self.transport_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Look for meta elements with encoding information + charEncoding = self.detectEncodingMeta(), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Parent document encoding + charEncoding = lookupEncoding(self.same_origin_parent_encoding), "tentative" + if charEncoding[0] is not None and not charEncoding[0].name.startswith("utf-16"): + return charEncoding + + # "likely" encoding + charEncoding = lookupEncoding(self.likely_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Guess with chardet, if available + if chardet: + try: + from pip._vendor.chardet.universaldetector import UniversalDetector + except ImportError: + pass + else: + buffers = [] + detector = UniversalDetector() + while not detector.done: + buffer = self.rawStream.read(self.numBytesChardet) + assert isinstance(buffer, bytes) + if not buffer: + break + buffers.append(buffer) + detector.feed(buffer) + detector.close() + encoding = lookupEncoding(detector.result['encoding']) + self.rawStream.seek(0) + if encoding is not None: + return encoding, "tentative" + + # Try the default encoding + charEncoding = lookupEncoding(self.default_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Fallback to html5lib's default if even that hasn't worked + return lookupEncoding("windows-1252"), "tentative" + + def changeEncoding(self, newEncoding): + assert self.charEncoding[1] != "certain" + newEncoding = lookupEncoding(newEncoding) + if newEncoding is None: + return + if newEncoding.name in ("utf-16be", "utf-16le"): + newEncoding = lookupEncoding("utf-8") + assert newEncoding is not None + elif newEncoding == self.charEncoding[0]: + self.charEncoding = (self.charEncoding[0], "certain") + else: + self.rawStream.seek(0) + self.charEncoding = (newEncoding, "certain") + self.reset() + raise _ReparseException("Encoding changed from %s to %s" % (self.charEncoding[0], newEncoding)) + + def detectBOM(self): + """Attempts to detect at BOM at the start of the stream. If + an encoding can be determined from the BOM return the name of the + encoding otherwise return None""" + bomDict = { + codecs.BOM_UTF8: 'utf-8', + codecs.BOM_UTF16_LE: 'utf-16le', codecs.BOM_UTF16_BE: 'utf-16be', + codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be' + } + + # Go to beginning of file and read in 4 bytes + string = self.rawStream.read(4) + assert isinstance(string, bytes) + + # Try detecting the BOM using bytes from the string + encoding = bomDict.get(string[:3]) # UTF-8 + seek = 3 + if not encoding: + # Need to detect UTF-32 before UTF-16 + encoding = bomDict.get(string) # UTF-32 + seek = 4 + if not encoding: + encoding = bomDict.get(string[:2]) # UTF-16 + seek = 2 + + # Set the read position past the BOM if one was found, otherwise + # set it to the start of the stream + if encoding: + self.rawStream.seek(seek) + return lookupEncoding(encoding) + else: + self.rawStream.seek(0) + return None + + def detectEncodingMeta(self): + """Report the encoding declared by the meta element + """ + buffer = self.rawStream.read(self.numBytesMeta) + assert isinstance(buffer, bytes) + parser = EncodingParser(buffer) + self.rawStream.seek(0) + encoding = parser.getEncoding() + + if encoding is not None and encoding.name in ("utf-16be", "utf-16le"): + encoding = lookupEncoding("utf-8") + + return encoding + + +class EncodingBytes(bytes): + """String-like object with an associated position and various extra methods + If the position is ever greater than the string length then an exception is + raised""" + def __new__(self, value): + assert isinstance(value, bytes) + return bytes.__new__(self, value.lower()) + + def __init__(self, value): + # pylint:disable=unused-argument + self._position = -1 + + def __iter__(self): + return self + + def __next__(self): + p = self._position = self._position + 1 + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + return self[p:p + 1] + + def next(self): + # Py2 compat + return self.__next__() + + def previous(self): + p = self._position + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + self._position = p = p - 1 + return self[p:p + 1] + + def setPosition(self, position): + if self._position >= len(self): + raise StopIteration + self._position = position + + def getPosition(self): + if self._position >= len(self): + raise StopIteration + if self._position >= 0: + return self._position + else: + return None + + position = property(getPosition, setPosition) + + def getCurrentByte(self): + return self[self.position:self.position + 1] + + currentByte = property(getCurrentByte) + + def skip(self, chars=spaceCharactersBytes): + """Skip past a list of characters""" + p = self.position # use property for the error-checking + while p < len(self): + c = self[p:p + 1] + if c not in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def skipUntil(self, chars): + p = self.position + while p < len(self): + c = self[p:p + 1] + if c in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def matchBytes(self, bytes): + """Look for a sequence of bytes at the start of a string. If the bytes + are found return True and advance the position to the byte after the + match. Otherwise return False and leave the position alone""" + rv = self.startswith(bytes, self.position) + if rv: + self.position += len(bytes) + return rv + + def jumpTo(self, bytes): + """Look for the next sequence of bytes matching a given sequence. If + a match is found advance the position to the last byte of the match""" + try: + self._position = self.index(bytes, self.position) + len(bytes) - 1 + except ValueError: + raise StopIteration + return True + + +class EncodingParser(object): + """Mini parser for detecting character encoding from meta elements""" + + def __init__(self, data): + """string - the data to work on for encoding detection""" + self.data = EncodingBytes(data) + self.encoding = None + + def getEncoding(self): + if b"") + + def handleMeta(self): + if self.data.currentByte not in spaceCharactersBytes: + # if we have ") + + def getAttribute(self): + """Return a name,value pair for the next attribute in the stream, + if one is found, or None""" + data = self.data + # Step 1 (skip chars) + c = data.skip(spaceCharactersBytes | frozenset([b"/"])) + assert c is None or len(c) == 1 + # Step 2 + if c in (b">", None): + return None + # Step 3 + attrName = [] + attrValue = [] + # Step 4 attribute name + while True: + if c == b"=" and attrName: + break + elif c in spaceCharactersBytes: + # Step 6! + c = data.skip() + break + elif c in (b"/", b">"): + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrName.append(c.lower()) + elif c is None: + return None + else: + attrName.append(c) + # Step 5 + c = next(data) + # Step 7 + if c != b"=": + data.previous() + return b"".join(attrName), b"" + # Step 8 + next(data) + # Step 9 + c = data.skip() + # Step 10 + if c in (b"'", b'"'): + # 10.1 + quoteChar = c + while True: + # 10.2 + c = next(data) + # 10.3 + if c == quoteChar: + next(data) + return b"".join(attrName), b"".join(attrValue) + # 10.4 + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + # 10.5 + else: + attrValue.append(c) + elif c == b">": + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + # Step 11 + while True: + c = next(data) + if c in spacesAngleBrackets: + return b"".join(attrName), b"".join(attrValue) + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + + +class ContentAttrParser(object): + def __init__(self, data): + assert isinstance(data, bytes) + self.data = data + + def parse(self): + try: + # Check if the attr name is charset + # otherwise return + self.data.jumpTo(b"charset") + self.data.position += 1 + self.data.skip() + if not self.data.currentByte == b"=": + # If there is no = sign keep looking for attrs + return None + self.data.position += 1 + self.data.skip() + # Look for an encoding between matching quote marks + if self.data.currentByte in (b'"', b"'"): + quoteMark = self.data.currentByte + self.data.position += 1 + oldPosition = self.data.position + if self.data.jumpTo(quoteMark): + return self.data[oldPosition:self.data.position] + else: + return None + else: + # Unquoted value + oldPosition = self.data.position + try: + self.data.skipUntil(spaceCharactersBytes) + return self.data[oldPosition:self.data.position] + except StopIteration: + # Return the whole remaining value + return self.data[oldPosition:] + except StopIteration: + return None + + +def lookupEncoding(encoding): + """Return the python codec name corresponding to an encoding or None if the + string doesn't correspond to a valid encoding.""" + if isinstance(encoding, bytes): + try: + encoding = encoding.decode("ascii") + except UnicodeDecodeError: + return None + + if encoding is not None: + try: + return webencodings.lookup(encoding) + except AttributeError: + return None + else: + return None diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py new file mode 100644 index 0000000..5f00253 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py @@ -0,0 +1,1735 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import unichr as chr + +from collections import deque, OrderedDict +from sys import version_info + +from .constants import spaceCharacters +from .constants import entities +from .constants import asciiLetters, asciiUpper2Lower +from .constants import digits, hexDigits, EOF +from .constants import tokenTypes, tagTokenTypes +from .constants import replacementCharacters + +from ._inputstream import HTMLInputStream + +from ._trie import Trie + +entitiesTrie = Trie(entities) + +if version_info >= (3, 7): + attributeMap = dict +else: + attributeMap = OrderedDict + + +class HTMLTokenizer(object): + """ This class takes care of tokenizing HTML. + + * self.currentToken + Holds the token that is currently being processed. + + * self.state + Holds a reference to the method to be invoked... XXX + + * self.stream + Points to HTMLInputStream object. + """ + + def __init__(self, stream, parser=None, **kwargs): + + self.stream = HTMLInputStream(stream, **kwargs) + self.parser = parser + + # Setup the initial tokenizer state + self.escapeFlag = False + self.lastFourChars = [] + self.state = self.dataState + self.escape = False + + # The current token being created + self.currentToken = None + super(HTMLTokenizer, self).__init__() + + def __iter__(self): + """ This is where the magic happens. + + We do our usually processing through the states and when we have a token + to return we yield the token which pauses processing until the next token + is requested. + """ + self.tokenQueue = deque([]) + # Start processing. When EOF is reached self.state will return False + # instead of True and the loop will terminate. + while self.state(): + while self.stream.errors: + yield {"type": tokenTypes["ParseError"], "data": self.stream.errors.pop(0)} + while self.tokenQueue: + yield self.tokenQueue.popleft() + + def consumeNumberEntity(self, isHex): + """This function returns either U+FFFD or the character based on the + decimal or hexadecimal representation. It also discards ";" if present. + If not present self.tokenQueue.append({"type": tokenTypes["ParseError"]}) is invoked. + """ + + allowed = digits + radix = 10 + if isHex: + allowed = hexDigits + radix = 16 + + charStack = [] + + # Consume all the characters that are in range while making sure we + # don't hit an EOF. + c = self.stream.char() + while c in allowed and c is not EOF: + charStack.append(c) + c = self.stream.char() + + # Convert the set of characters consumed to an int. + charAsInt = int("".join(charStack), radix) + + # Certain characters get replaced with others + if charAsInt in replacementCharacters: + char = replacementCharacters[charAsInt] + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + elif ((0xD800 <= charAsInt <= 0xDFFF) or + (charAsInt > 0x10FFFF)): + char = "\uFFFD" + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + else: + # Should speed up this check somehow (e.g. move the set to a constant) + if ((0x0001 <= charAsInt <= 0x0008) or + (0x000E <= charAsInt <= 0x001F) or + (0x007F <= charAsInt <= 0x009F) or + (0xFDD0 <= charAsInt <= 0xFDEF) or + charAsInt in frozenset([0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, + 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, + 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, + 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, + 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, + 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, + 0xFFFFF, 0x10FFFE, 0x10FFFF])): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + try: + # Try/except needed as UCS-2 Python builds' unichar only works + # within the BMP. + char = chr(charAsInt) + except ValueError: + v = charAsInt - 0x10000 + char = chr(0xD800 | (v >> 10)) + chr(0xDC00 | (v & 0x3FF)) + + # Discard the ; if present. Otherwise, put it back on the queue and + # invoke parseError on parser. + if c != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "numeric-entity-without-semicolon"}) + self.stream.unget(c) + + return char + + def consumeEntity(self, allowedChar=None, fromAttribute=False): + # Initialise to the default output for when no entity is matched + output = "&" + + charStack = [self.stream.char()] + if (charStack[0] in spaceCharacters or charStack[0] in (EOF, "<", "&") or + (allowedChar is not None and allowedChar == charStack[0])): + self.stream.unget(charStack[0]) + + elif charStack[0] == "#": + # Read the next character to see if it's hex or decimal + hex = False + charStack.append(self.stream.char()) + if charStack[-1] in ("x", "X"): + hex = True + charStack.append(self.stream.char()) + + # charStack[-1] should be the first digit + if (hex and charStack[-1] in hexDigits) \ + or (not hex and charStack[-1] in digits): + # At least one digit found, so consume the whole number + self.stream.unget(charStack[-1]) + output = self.consumeNumberEntity(hex) + else: + # No digits found + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "expected-numeric-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + else: + # At this point in the process might have named entity. Entities + # are stored in the global variable "entities". + # + # Consume characters and compare to these to a substring of the + # entity names in the list until the substring no longer matches. + while (charStack[-1] is not EOF): + if not entitiesTrie.has_keys_with_prefix("".join(charStack)): + break + charStack.append(self.stream.char()) + + # At this point we have a string that starts with some characters + # that may match an entity + # Try to find the longest entity the string will match to take care + # of ¬i for instance. + try: + entityName = entitiesTrie.longest_prefix("".join(charStack[:-1])) + entityLength = len(entityName) + except KeyError: + entityName = None + + if entityName is not None: + if entityName[-1] != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "named-entity-without-semicolon"}) + if (entityName[-1] != ";" and fromAttribute and + (charStack[entityLength] in asciiLetters or + charStack[entityLength] in digits or + charStack[entityLength] == "=")): + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + else: + output = entities[entityName] + self.stream.unget(charStack.pop()) + output += "".join(charStack[entityLength:]) + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-named-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + if fromAttribute: + self.currentToken["data"][-1][1] += output + else: + if output in spaceCharacters: + tokenType = "SpaceCharacters" + else: + tokenType = "Characters" + self.tokenQueue.append({"type": tokenTypes[tokenType], "data": output}) + + def processEntityInAttribute(self, allowedChar): + """This method replaces the need for "entityInAttributeValueState". + """ + self.consumeEntity(allowedChar=allowedChar, fromAttribute=True) + + def emitCurrentToken(self): + """This method is a generic handler for emitting the tags. It also sets + the state to "data" because that's what's needed after a token has been + emitted. + """ + token = self.currentToken + # Add token to the queue to be yielded + if (token["type"] in tagTokenTypes): + token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + data = attributeMap(raw) + if len(raw) > len(data): + # we had some duplicated attribute, fix so first wins + data.update(raw[::-1]) + token["data"] = data + + if token["type"] == tokenTypes["EndTag"]: + if token["data"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "attributes-in-end-tag"}) + if token["selfClosing"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "self-closing-flag-on-end-tag"}) + self.tokenQueue.append(token) + self.state = self.dataState + + # Below are the various tokenizer states worked out. + def dataState(self): + data = self.stream.char() + if data == "&": + self.state = self.entityDataState + elif data == "<": + self.state = self.tagOpenState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\u0000"}) + elif data is EOF: + # Tokenization ends. + return False + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def entityDataState(self): + self.consumeEntity() + self.state = self.dataState + return True + + def rcdataState(self): + data = self.stream.char() + if data == "&": + self.state = self.characterReferenceInRcdata + elif data == "<": + self.state = self.rcdataLessThanSignState + elif data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def characterReferenceInRcdata(self): + self.consumeEntity() + self.state = self.rcdataState + return True + + def rawtextState(self): + data = self.stream.char() + if data == "<": + self.state = self.rawtextLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataState(self): + data = self.stream.char() + if data == "<": + self.state = self.scriptDataLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def plaintextState(self): + data = self.stream.char() + if data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + self.stream.charsUntil("\u0000")}) + return True + + def tagOpenState(self): + data = self.stream.char() + if data == "!": + self.state = self.markupDeclarationOpenState + elif data == "/": + self.state = self.closeTagOpenState + elif data in asciiLetters: + self.currentToken = {"type": tokenTypes["StartTag"], + "name": data, "data": [], + "selfClosing": False, + "selfClosingAcknowledged": False} + self.state = self.tagNameState + elif data == ">": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-right-bracket"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<>"}) + self.state = self.dataState + elif data == "?": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-question-mark"}) + self.stream.unget(data) + self.state = self.bogusCommentState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.dataState + return True + + def closeTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.currentToken = {"type": tokenTypes["EndTag"], "name": data, + "data": [], "selfClosing": False} + self.state = self.tagNameState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-right-bracket"}) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-eof"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "": + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-tag-name"}) + self.state = self.dataState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + else: + self.currentToken["name"] += data + # (Don't use charsUntil here, because tag names are + # very short and it's faster to not do anything fancy) + return True + + def rcdataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rcdataEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rcdataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEscapedEndTagOpenState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<" + data}) + self.temporaryBuffer = data + self.state = self.scriptDataDoubleEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer = data + self.state = self.scriptDataEscapedEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": ""))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataDoubleEscapedState + else: + self.state = self.scriptDataEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + return True + + def scriptDataDoubleEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "/"}) + self.temporaryBuffer = "" + self.state = self.scriptDataDoubleEscapeEndState + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapeEndState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataEscapedState + else: + self.state = self.scriptDataDoubleEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def beforeAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data in ("'", '"', "=", "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-name-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def attributeNameState(self): + data = self.stream.char() + leavingThisState = True + emitToken = False + if data == "=": + self.state = self.beforeAttributeValueState + elif data in asciiLetters: + self.currentToken["data"][-1][0] += data +\ + self.stream.charsUntil(asciiLetters, True) + leavingThisState = False + elif data == ">": + # XXX If we emit here the attributes are converted to a dict + # without being checked and when the code below runs we error + # because data is a dict not a list + emitToken = True + elif data in spaceCharacters: + self.state = self.afterAttributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][0] += "\uFFFD" + leavingThisState = False + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"][-1][0] += data + leavingThisState = False + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-attribute-name"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][0] += data + leavingThisState = False + + if leavingThisState: + # Attributes are not dropped at this stage. That happens when the + # start tag token is emitted so values can still be safely appended + # to attributes, but we do want to report the parse error in time. + self.currentToken["data"][-1][0] = ( + self.currentToken["data"][-1][0].translate(asciiUpper2Lower)) + for name, _ in self.currentToken["data"][:-1]: + if self.currentToken["data"][-1][0] == name: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "duplicate-attribute"}) + break + # XXX Fix for above XXX + if emitToken: + self.emitCurrentToken() + return True + + def afterAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "=": + self.state = self.beforeAttributeValueState + elif data == ">": + self.emitCurrentToken() + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-after-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-end-of-tag-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def beforeAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "\"": + self.state = self.attributeValueDoubleQuotedState + elif data == "&": + self.state = self.attributeValueUnQuotedState + self.stream.unget(data) + elif data == "'": + self.state = self.attributeValueSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-right-bracket"}) + self.emitCurrentToken() + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + self.state = self.attributeValueUnQuotedState + elif data in ("=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "equals-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + return True + + def attributeValueDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute('"') + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-double-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("\"", "&", "\u0000")) + return True + + def attributeValueSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute("'") + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-single-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("'", "&", "\u0000")) + return True + + def attributeValueUnQuotedState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == "&": + self.processEntityInAttribute(">") + elif data == ">": + self.emitCurrentToken() + elif data in ('"', "'", "=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-no-quotes"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.stream.charsUntil( + frozenset(("&", ">", '"', "'", "=", "<", "`", "\u0000")) | spaceCharacters) + return True + + def afterAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-EOF-after-attribute-value"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-attribute-value"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def selfClosingStartTagState(self): + data = self.stream.char() + if data == ">": + self.currentToken["selfClosing"] = True + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "unexpected-EOF-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def bogusCommentState(self): + # Make a new comment token and give it as value all the characters + # until the first > or EOF (charsUntil checks for EOF automatically) + # and emit it. + data = self.stream.charsUntil(">") + data = data.replace("\u0000", "\uFFFD") + self.tokenQueue.append( + {"type": tokenTypes["Comment"], "data": data}) + + # Eat the character directly after the bogus comment which is either a + # ">" or an EOF. + self.stream.char() + self.state = self.dataState + return True + + def markupDeclarationOpenState(self): + charStack = [self.stream.char()] + if charStack[-1] == "-": + charStack.append(self.stream.char()) + if charStack[-1] == "-": + self.currentToken = {"type": tokenTypes["Comment"], "data": ""} + self.state = self.commentStartState + return True + elif charStack[-1] in ('d', 'D'): + matched = True + for expected in (('o', 'O'), ('c', 'C'), ('t', 'T'), + ('y', 'Y'), ('p', 'P'), ('e', 'E')): + charStack.append(self.stream.char()) + if charStack[-1] not in expected: + matched = False + break + if matched: + self.currentToken = {"type": tokenTypes["Doctype"], + "name": "", + "publicId": None, "systemId": None, + "correct": True} + self.state = self.doctypeState + return True + elif (charStack[-1] == "[" and + self.parser is not None and + self.parser.tree.openElements and + self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace): + matched = True + for expected in ["C", "D", "A", "T", "A", "["]: + charStack.append(self.stream.char()) + if charStack[-1] != expected: + matched = False + break + if matched: + self.state = self.cdataSectionState + return True + + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-dashes-or-doctype"}) + + while charStack: + self.stream.unget(charStack.pop()) + self.state = self.bogusCommentState + return True + + def commentStartState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentStartDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + self.state = self.commentState + return True + + def commentStartDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + \ + self.stream.charsUntil(("-", "\u0000")) + return True + + def commentEndDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentEndState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--\uFFFD" + self.state = self.commentState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-bang-after-double-dash-in-comment"}) + self.state = self.commentEndBangState + elif data == "-": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-dash-after-double-dash-in-comment"}) + self.currentToken["data"] += data + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-double-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-comment"}) + self.currentToken["data"] += "--" + data + self.state = self.commentState + return True + + def commentEndBangState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "-": + self.currentToken["data"] += "--!" + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--!\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-bang-state"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "--!" + data + self.state = self.commentState + return True + + def doctypeState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "need-space-after-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeNameState + return True + + def beforeDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-right-bracket"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] = "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] = data + self.state = self.doctypeNameState + return True + + def doctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.state = self.afterDoctypeNameState + elif data == ">": + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype-name"}) + self.currentToken["correct"] = False + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] += data + return True + + def afterDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.currentToken["correct"] = False + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + if data in ("p", "P"): + matched = True + for expected in (("u", "U"), ("b", "B"), ("l", "L"), + ("i", "I"), ("c", "C")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypePublicKeywordState + return True + elif data in ("s", "S"): + matched = True + for expected in (("y", "Y"), ("s", "S"), ("t", "T"), + ("e", "E"), ("m", "M")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypeSystemKeywordState + return True + + # All the characters read before the current 'data' will be + # [a-zA-Z], so they're garbage in the bogus doctype and can be + # discarded; only the latest character might be '>' or EOF + # and needs to be ungetted + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-space-or-right-bracket-in-doctype", "datavars": + {"data": data}}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + + return True + + def afterDoctypePublicKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypePublicIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + return True + + def beforeDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypePublicIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def doctypePublicIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def afterDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.betweenDoctypePublicAndSystemIdentifiersState + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def betweenDoctypePublicAndSystemIdentifiersState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def afterDoctypeSystemKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeSystemIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + return True + + def beforeDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypeSystemIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def doctypeSystemIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def afterDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.state = self.bogusDoctypeState + return True + + def bogusDoctypeState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + # XXX EMIT + self.stream.unget(data) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + pass + return True + + def cdataSectionState(self): + data = [] + while True: + data.append(self.stream.charsUntil("]")) + data.append(self.stream.charsUntil(">")) + char = self.stream.char() + if char == EOF: + break + else: + assert char == ">" + if data[-1][-2:] == "]]": + data[-1] = data[-1][:-2] + break + else: + data.append(char) + + data = "".join(data) # pylint:disable=redefined-variable-type + # Deal with null here rather than in the parser + nullCount = data.count("\u0000") + if nullCount > 0: + for _ in range(nullCount): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + data = data.replace("\u0000", "\uFFFD") + if data: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": data}) + self.state = self.dataState + return True diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py new file mode 100644 index 0000000..07bad5d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import, division, unicode_literals + +from .py import Trie + +__all__ = ["Trie"] diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1de7c3fa52e9a1d5ffa42ee9da8c1cf3b72be2e5 GIT binary patch literal 388 zcmYk1Jx&8L5QTTWn;%6)P;iMw+PFXzNC;6-(*VsjBWFD%CjP_rM&uOK9DysjrOFkc z!A=@R@*97nH_|*dpU;X^pPOH%Qvc5Izes|+^!!B)DNJE44jkT$@_-@gOKSo?3-5W;YD3!@*)z7E?eBZvXU7W*J;3LWqkrSSJ%B%{ zG5ZAA_ymjj0V@fTWstLoc_ZWuELFh)lY{~W|L@n8o0e;)E zD|`6uJO&@40D;Y0e_y9+^5D}0Q|maXHx6Q>Hj*qhW@A|DjT%SIZo+3a$27w8Vz#hR zL(Lkv;8*+_&iNBe${6%|?KS*@?SaF?B)<$A{t&3A@9idRWK$}&Z9L0yqgjHIF;zSj))ZwVkvkxg)P~Jj8$sAN?w!3Xqos@aCLmdCY z0+aic_`#7nGlT7{Oi=Kh!HclIZ(C-J&~zJPtxsXr!$r_TzERmw&tt!S11n_S6~ZYd zgr1}Bh38`wWXR&c7kR(}KIy*L^(9ToQ=ij>cd=^V3a;THJX$^H4ZlJuP=){vkNO=4 zk~jqah@Ws_lYZM0xV!Yzsr`{ zDxa)4zPBS^VoC?jiE|f!NE{M`q-l`R8VpGgw%cd=0gm57)yZbg#f%y&w|5k1>ij*N zY~0;~QcT`@1uz#zJ9TX!L=opo2-_1PFXcF+|9K&f$8mNu(h)+I3Hp+ps_#)n0QEaq z*@YY1CMwG?gwE}>TBSc{Vsd{Mi=kVba<FKcCwSq$Hw-C zB$~ajz3?Y+LCvvO{v2O9^~8Z2TzJp3nWti}$^k-Vpig3^P_39M4K=?pI5tv2=y1NXxNDwd=ZY6ld z`8PMEL7;B}smPC=2{mBK&?)<#P;$s{<5RlTWwr&77TUx`B2@s|AY#2wEIoco)FSYI z|I+_XOQrkCFfQV~vJi3qL79wmSr`zOvVRzFz_1~YM^dG=cywZKWC8qO^Cv?a>r zM$>w6mRTQlY;zk@=qsg4Wdkl^6WdTj_DKP3t-zBw8N&6H^)gx5cFm8<6aWGAd@ZwG z>*H1jU`>|5R~yaU#NyIz^q;QMh=p{ChV*pt(p6r_9}KL^AKZ-@vQLg6jH(IjQ>tFY zfvMMFupW4N)p(opq%2Gf(dPUW(9$kJ>FLVM?DN%2`GgQ%G7XtT_8|plrn^k7fl6(k zI{Q~mH#k&c!TIV1tL>Rpx9~p=S|97)Sybd7Rmd=sMf z6wX(s3gJxL8>|sHnZmihRpFDyTy{n&tO@oxnFNQXZby}01+MM*xV!6ZsTJ^C;a$3s zx&edrv^2H_-9#DvQ)-3^`;U4J3A`f2B?=Uq5Zy+dsNqP--SpV9th8*H+Q99#G*y_P zSK{hq7zK^-z|v{4Hf_;W)^R1|vkvWmEQJ4Q(?tL)_7d`l`Z@9fn0XRP9-t1-Q>Gaq=R5q+1d6!wx loOfb)rtsmljeB3-$Gg7F6&r2VSG$9KO{4yG+6IW)-oL!B_eKB! literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py new file mode 100644 index 0000000..6b71975 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py @@ -0,0 +1,40 @@ +from __future__ import absolute_import, division, unicode_literals + +try: + from collections.abc import Mapping +except ImportError: # Python 2.7 + from collections import Mapping + + +class Trie(Mapping): + """Abstract base class for tries""" + + def keys(self, prefix=None): + # pylint:disable=arguments-differ + keys = super(Trie, self).keys() + + if prefix is None: + return set(keys) + + return {x for x in keys if x.startswith(prefix)} + + def has_keys_with_prefix(self, prefix): + for key in self.keys(): + if key.startswith(prefix): + return True + + return False + + def longest_prefix(self, prefix): + if prefix in self: + return prefix + + for i in range(1, len(prefix) + 1): + if prefix[:-i] in self: + return prefix[:-i] + + raise KeyError(prefix) + + def longest_prefix_item(self, prefix): + lprefix = self.longest_prefix(prefix) + return (lprefix, self[lprefix]) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py new file mode 100644 index 0000000..c178b21 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from bisect import bisect_left + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + if not all(isinstance(x, text_type) for x in data.keys()): + raise TypeError("All keys must be strings") + + self._data = data + self._keys = sorted(data.keys()) + self._cachestr = "" + self._cachepoints = (0, len(data)) + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + if prefix is None or prefix == "" or not self._keys: + return set(self._keys) + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + start = i = bisect_left(self._keys, prefix, lo, hi) + else: + start = i = bisect_left(self._keys, prefix) + + keys = set() + if start == len(self._keys): + return keys + + while self._keys[i].startswith(prefix): + keys.add(self._keys[i]) + i += 1 + + self._cachestr = prefix + self._cachepoints = (start, i) + + return keys + + def has_keys_with_prefix(self, prefix): + if prefix in self._data: + return True + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + i = bisect_left(self._keys, prefix, lo, hi) + else: + i = bisect_left(self._keys, prefix) + + if i == len(self._keys): + return False + + return self._keys[i].startswith(prefix) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py new file mode 100644 index 0000000..d7c4926 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py @@ -0,0 +1,159 @@ +from __future__ import absolute_import, division, unicode_literals + +from types import ModuleType + +try: + from collections.abc import Mapping +except ImportError: + from collections import Mapping + +from pip._vendor.six import text_type, PY3 + +if PY3: + import xml.etree.ElementTree as default_etree +else: + try: + import xml.etree.cElementTree as default_etree + except ImportError: + import xml.etree.ElementTree as default_etree + + +__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", + "surrogatePairToCodepoint", "moduleFactoryFactory", + "supports_lone_surrogates"] + + +# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be +# caught by the below test. In general this would be any platform +# using UTF-16 as its encoding of unicode strings, such as +# Jython. This is because UTF-16 itself is based on the use of such +# surrogates, and there is no mechanism to further escape such +# escapes. +try: + _x = eval('"\\uD800"') # pylint:disable=eval-used + if not isinstance(_x, text_type): + # We need this with u"" because of http://bugs.jython.org/issue2039 + _x = eval('u"\\uD800"') # pylint:disable=eval-used + assert isinstance(_x, text_type) +except Exception: + supports_lone_surrogates = False +else: + supports_lone_surrogates = True + + +class MethodDispatcher(dict): + """Dict with 2 special properties: + + On initiation, keys that are lists, sets or tuples are converted to + multiple keys so accessing any one of the items in the original + list-like object returns the matching value + + md = MethodDispatcher({("foo", "bar"):"baz"}) + md["foo"] == "baz" + + A default value which can be set through the default attribute. + """ + + def __init__(self, items=()): + _dictEntries = [] + for name, value in items: + if isinstance(name, (list, tuple, frozenset, set)): + for item in name: + _dictEntries.append((item, value)) + else: + _dictEntries.append((name, value)) + dict.__init__(self, _dictEntries) + assert len(self) == len(_dictEntries) + self.default = None + + def __getitem__(self, key): + return dict.get(self, key, self.default) + + def __get__(self, instance, owner=None): + return BoundMethodDispatcher(instance, self) + + +class BoundMethodDispatcher(Mapping): + """Wraps a MethodDispatcher, binding its return values to `instance`""" + def __init__(self, instance, dispatcher): + self.instance = instance + self.dispatcher = dispatcher + + def __getitem__(self, key): + # see https://docs.python.org/3/reference/datamodel.html#object.__get__ + # on a function, __get__ is used to bind a function to an instance as a bound method + return self.dispatcher[key].__get__(self.instance) + + def get(self, key, default): + if key in self.dispatcher: + return self[key] + else: + return default + + def __iter__(self): + return iter(self.dispatcher) + + def __len__(self): + return len(self.dispatcher) + + def __contains__(self, key): + return key in self.dispatcher + + +# Some utility functions to deal with weirdness around UCS2 vs UCS4 +# python builds + +def isSurrogatePair(data): + return (len(data) == 2 and + ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and + ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF) + + +def surrogatePairToCodepoint(data): + char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 + + (ord(data[1]) - 0xDC00)) + return char_val + +# Module Factory Factory (no, this isn't Java, I know) +# Here to stop this being duplicated all over the place. + + +def moduleFactoryFactory(factory): + moduleCache = {} + + def moduleFactory(baseModule, *args, **kwargs): + if isinstance(ModuleType.__name__, type("")): + name = "_%s_factory" % baseModule.__name__ + else: + name = b"_%s_factory" % baseModule.__name__ + + kwargs_tuple = tuple(kwargs.items()) + + try: + return moduleCache[name][args][kwargs_tuple] + except KeyError: + mod = ModuleType(name) + objs = factory(baseModule, *args, **kwargs) + mod.__dict__.update(objs) + if "name" not in moduleCache: + moduleCache[name] = {} + if "args" not in moduleCache[name]: + moduleCache[name][args] = {} + if "kwargs" not in moduleCache[name][args]: + moduleCache[name][args][kwargs_tuple] = {} + moduleCache[name][args][kwargs_tuple] = mod + return mod + + return moduleFactory + + +def memoize(func): + cache = {} + + def wrapped(*args, **kwargs): + key = (tuple(args), tuple(kwargs.items())) + if key not in cache: + cache[key] = func(*args, **kwargs) + return cache[key] + + return wrapped diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py new file mode 100644 index 0000000..fe3e237 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py @@ -0,0 +1,2946 @@ +from __future__ import absolute_import, division, unicode_literals + +import string + +EOF = None + +E = { + "null-character": + "Null character in input stream, replaced with U+FFFD.", + "invalid-codepoint": + "Invalid codepoint in stream.", + "incorrectly-placed-solidus": + "Solidus (/) incorrectly placed in tag.", + "incorrect-cr-newline-entity": + "Incorrect CR newline entity, replaced with LF.", + "illegal-windows-1252-entity": + "Entity used with illegal number (windows-1252 reference).", + "cant-convert-numeric-entity": + "Numeric entity couldn't be converted to character " + "(codepoint U+%(charAsInt)08x).", + "illegal-codepoint-for-numeric-entity": + "Numeric entity represents an illegal codepoint: " + "U+%(charAsInt)08x.", + "numeric-entity-without-semicolon": + "Numeric entity didn't end with ';'.", + "expected-numeric-entity-but-got-eof": + "Numeric entity expected. Got end of file instead.", + "expected-numeric-entity": + "Numeric entity expected but none found.", + "named-entity-without-semicolon": + "Named entity didn't end with ';'.", + "expected-named-entity": + "Named entity expected. Got none.", + "attributes-in-end-tag": + "End tag contains unexpected attributes.", + 'self-closing-flag-on-end-tag': + "End tag contains unexpected self-closing flag.", + "expected-tag-name-but-got-right-bracket": + "Expected tag name. Got '>' instead.", + "expected-tag-name-but-got-question-mark": + "Expected tag name. Got '?' instead. (HTML doesn't " + "support processing instructions.)", + "expected-tag-name": + "Expected tag name. Got something else instead", + "expected-closing-tag-but-got-right-bracket": + "Expected closing tag. Got '>' instead. Ignoring ''.", + "expected-closing-tag-but-got-eof": + "Expected closing tag. Unexpected end of file.", + "expected-closing-tag-but-got-char": + "Expected closing tag. Unexpected character '%(data)s' found.", + "eof-in-tag-name": + "Unexpected end of file in the tag name.", + "expected-attribute-name-but-got-eof": + "Unexpected end of file. Expected attribute name instead.", + "eof-in-attribute-name": + "Unexpected end of file in attribute name.", + "invalid-character-in-attribute-name": + "Invalid character in attribute name", + "duplicate-attribute": + "Dropped duplicate attribute on tag.", + "expected-end-of-tag-name-but-got-eof": + "Unexpected end of file. Expected = or end of tag.", + "expected-attribute-value-but-got-eof": + "Unexpected end of file. Expected attribute value.", + "expected-attribute-value-but-got-right-bracket": + "Expected attribute value. Got '>' instead.", + 'equals-in-unquoted-attribute-value': + "Unexpected = in unquoted attribute", + 'unexpected-character-in-unquoted-attribute-value': + "Unexpected character in unquoted attribute", + "invalid-character-after-attribute-name": + "Unexpected character after attribute name.", + "unexpected-character-after-attribute-value": + "Unexpected character after attribute value.", + "eof-in-attribute-value-double-quote": + "Unexpected end of file in attribute value (\").", + "eof-in-attribute-value-single-quote": + "Unexpected end of file in attribute value (').", + "eof-in-attribute-value-no-quotes": + "Unexpected end of file in attribute value.", + "unexpected-EOF-after-solidus-in-tag": + "Unexpected end of file in tag. Expected >", + "unexpected-character-after-solidus-in-tag": + "Unexpected character after / in tag. Expected >", + "expected-dashes-or-doctype": + "Expected '--' or 'DOCTYPE'. Not found.", + "unexpected-bang-after-double-dash-in-comment": + "Unexpected ! after -- in comment", + "unexpected-space-after-double-dash-in-comment": + "Unexpected space after -- in comment", + "incorrect-comment": + "Incorrect comment.", + "eof-in-comment": + "Unexpected end of file in comment.", + "eof-in-comment-end-dash": + "Unexpected end of file in comment (-)", + "unexpected-dash-after-double-dash-in-comment": + "Unexpected '-' after '--' found in comment.", + "eof-in-comment-double-dash": + "Unexpected end of file in comment (--).", + "eof-in-comment-end-space-state": + "Unexpected end of file in comment.", + "eof-in-comment-end-bang-state": + "Unexpected end of file in comment.", + "unexpected-char-in-comment": + "Unexpected character in comment found.", + "need-space-after-doctype": + "No space after literal string 'DOCTYPE'.", + "expected-doctype-name-but-got-right-bracket": + "Unexpected > character. Expected DOCTYPE name.", + "expected-doctype-name-but-got-eof": + "Unexpected end of file. Expected DOCTYPE name.", + "eof-in-doctype-name": + "Unexpected end of file in DOCTYPE name.", + "eof-in-doctype": + "Unexpected end of file in DOCTYPE.", + "expected-space-or-right-bracket-in-doctype": + "Expected space or '>'. Got '%(data)s'", + "unexpected-end-of-doctype": + "Unexpected end of DOCTYPE.", + "unexpected-char-in-doctype": + "Unexpected character in DOCTYPE.", + "eof-in-innerhtml": + "XXX innerHTML EOF", + "unexpected-doctype": + "Unexpected DOCTYPE. Ignored.", + "non-html-root": + "html needs to be the first start tag.", + "expected-doctype-but-got-eof": + "Unexpected End of file. Expected DOCTYPE.", + "unknown-doctype": + "Erroneous DOCTYPE.", + "expected-doctype-but-got-chars": + "Unexpected non-space characters. Expected DOCTYPE.", + "expected-doctype-but-got-start-tag": + "Unexpected start tag (%(name)s). Expected DOCTYPE.", + "expected-doctype-but-got-end-tag": + "Unexpected end tag (%(name)s). Expected DOCTYPE.", + "end-tag-after-implied-root": + "Unexpected end tag (%(name)s) after the (implied) root element.", + "expected-named-closing-tag-but-got-eof": + "Unexpected end of file. Expected end tag (%(name)s).", + "two-heads-are-not-better-than-one": + "Unexpected start tag head in existing head. Ignored.", + "unexpected-end-tag": + "Unexpected end tag (%(name)s). Ignored.", + "unexpected-start-tag-out-of-my-head": + "Unexpected start tag (%(name)s) that can be in head. Moved.", + "unexpected-start-tag": + "Unexpected start tag (%(name)s).", + "missing-end-tag": + "Missing end tag (%(name)s).", + "missing-end-tags": + "Missing end tags (%(name)s).", + "unexpected-start-tag-implies-end-tag": + "Unexpected start tag (%(startName)s) " + "implies end tag (%(endName)s).", + "unexpected-start-tag-treated-as": + "Unexpected start tag (%(originalName)s). Treated as %(newName)s.", + "deprecated-tag": + "Unexpected start tag %(name)s. Don't use it!", + "unexpected-start-tag-ignored": + "Unexpected start tag %(name)s. Ignored.", + "expected-one-end-tag-but-got-another": + "Unexpected end tag (%(gotName)s). " + "Missing end tag (%(expectedName)s).", + "end-tag-too-early": + "End tag (%(name)s) seen too early. Expected other end tag.", + "end-tag-too-early-named": + "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).", + "end-tag-too-early-ignored": + "End tag (%(name)s) seen too early. Ignored.", + "adoption-agency-1.1": + "End tag (%(name)s) violates step 1, " + "paragraph 1 of the adoption agency algorithm.", + "adoption-agency-1.2": + "End tag (%(name)s) violates step 1, " + "paragraph 2 of the adoption agency algorithm.", + "adoption-agency-1.3": + "End tag (%(name)s) violates step 1, " + "paragraph 3 of the adoption agency algorithm.", + "adoption-agency-4.4": + "End tag (%(name)s) violates step 4, " + "paragraph 4 of the adoption agency algorithm.", + "unexpected-end-tag-treated-as": + "Unexpected end tag (%(originalName)s). Treated as %(newName)s.", + "no-end-tag": + "This element (%(name)s) has no end tag.", + "unexpected-implied-end-tag-in-table": + "Unexpected implied end tag (%(name)s) in the table phase.", + "unexpected-implied-end-tag-in-table-body": + "Unexpected implied end tag (%(name)s) in the table body phase.", + "unexpected-char-implies-table-voodoo": + "Unexpected non-space characters in " + "table context caused voodoo mode.", + "unexpected-hidden-input-in-table": + "Unexpected input with type hidden in table context.", + "unexpected-form-in-table": + "Unexpected form in table context.", + "unexpected-start-tag-implies-table-voodoo": + "Unexpected start tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-end-tag-implies-table-voodoo": + "Unexpected end tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-cell-in-table-body": + "Unexpected table cell start tag (%(name)s) " + "in the table body phase.", + "unexpected-cell-end-tag": + "Got table cell end tag (%(name)s) " + "while required end tags are missing.", + "unexpected-end-tag-in-table-body": + "Unexpected end tag (%(name)s) in the table body phase. Ignored.", + "unexpected-implied-end-tag-in-table-row": + "Unexpected implied end tag (%(name)s) in the table row phase.", + "unexpected-end-tag-in-table-row": + "Unexpected end tag (%(name)s) in the table row phase. Ignored.", + "unexpected-select-in-select": + "Unexpected select start tag in the select phase " + "treated as select end tag.", + "unexpected-input-in-select": + "Unexpected input start tag in the select phase.", + "unexpected-start-tag-in-select": + "Unexpected start tag token (%(name)s in the select phase. " + "Ignored.", + "unexpected-end-tag-in-select": + "Unexpected end tag (%(name)s) in the select phase. Ignored.", + "unexpected-table-element-start-tag-in-select-in-table": + "Unexpected table element start tag (%(name)s) in the select in table phase.", + "unexpected-table-element-end-tag-in-select-in-table": + "Unexpected table element end tag (%(name)s) in the select in table phase.", + "unexpected-char-after-body": + "Unexpected non-space characters in the after body phase.", + "unexpected-start-tag-after-body": + "Unexpected start tag token (%(name)s)" + " in the after body phase.", + "unexpected-end-tag-after-body": + "Unexpected end tag token (%(name)s)" + " in the after body phase.", + "unexpected-char-in-frameset": + "Unexpected characters in the frameset phase. Characters ignored.", + "unexpected-start-tag-in-frameset": + "Unexpected start tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-frameset-in-frameset-innerhtml": + "Unexpected end tag token (frameset) " + "in the frameset phase (innerHTML).", + "unexpected-end-tag-in-frameset": + "Unexpected end tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-char-after-frameset": + "Unexpected non-space characters in the " + "after frameset phase. Ignored.", + "unexpected-start-tag-after-frameset": + "Unexpected start tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-frameset": + "Unexpected end tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-body-innerhtml": + "Unexpected end tag after body(innerHtml)", + "expected-eof-but-got-char": + "Unexpected non-space characters. Expected end of file.", + "expected-eof-but-got-start-tag": + "Unexpected start tag (%(name)s)" + ". Expected end of file.", + "expected-eof-but-got-end-tag": + "Unexpected end tag (%(name)s)" + ". Expected end of file.", + "eof-in-table": + "Unexpected end of file. Expected table content.", + "eof-in-select": + "Unexpected end of file. Expected select content.", + "eof-in-frameset": + "Unexpected end of file. Expected frameset content.", + "eof-in-script-in-script": + "Unexpected end of file. Expected script content.", + "eof-in-foreign-lands": + "Unexpected end of file. Expected foreign content", + "non-void-element-with-trailing-solidus": + "Trailing solidus not allowed on element %(name)s", + "unexpected-html-element-in-foreign-content": + "Element %(name)s not allowed in a non-html context", + "unexpected-end-tag-before-html": + "Unexpected end tag (%(name)s) before html.", + "unexpected-inhead-noscript-tag": + "Element %(name)s not allowed in a inhead-noscript context", + "eof-in-head-noscript": + "Unexpected end of file. Expected inhead-noscript content", + "char-in-head-noscript": + "Unexpected non-space character. Expected inhead-noscript content", + "XXX-undefined-error": + "Undefined error (this sucks and should be fixed)", +} + +namespaces = { + "html": "http://www.w3.org/1999/xhtml", + "mathml": "http://www.w3.org/1998/Math/MathML", + "svg": "http://www.w3.org/2000/svg", + "xlink": "http://www.w3.org/1999/xlink", + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/" +} + +scopingElements = frozenset([ + (namespaces["html"], "applet"), + (namespaces["html"], "caption"), + (namespaces["html"], "html"), + (namespaces["html"], "marquee"), + (namespaces["html"], "object"), + (namespaces["html"], "table"), + (namespaces["html"], "td"), + (namespaces["html"], "th"), + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext"), + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title"), +]) + +formattingElements = frozenset([ + (namespaces["html"], "a"), + (namespaces["html"], "b"), + (namespaces["html"], "big"), + (namespaces["html"], "code"), + (namespaces["html"], "em"), + (namespaces["html"], "font"), + (namespaces["html"], "i"), + (namespaces["html"], "nobr"), + (namespaces["html"], "s"), + (namespaces["html"], "small"), + (namespaces["html"], "strike"), + (namespaces["html"], "strong"), + (namespaces["html"], "tt"), + (namespaces["html"], "u") +]) + +specialElements = frozenset([ + (namespaces["html"], "address"), + (namespaces["html"], "applet"), + (namespaces["html"], "area"), + (namespaces["html"], "article"), + (namespaces["html"], "aside"), + (namespaces["html"], "base"), + (namespaces["html"], "basefont"), + (namespaces["html"], "bgsound"), + (namespaces["html"], "blockquote"), + (namespaces["html"], "body"), + (namespaces["html"], "br"), + (namespaces["html"], "button"), + (namespaces["html"], "caption"), + (namespaces["html"], "center"), + (namespaces["html"], "col"), + (namespaces["html"], "colgroup"), + (namespaces["html"], "command"), + (namespaces["html"], "dd"), + (namespaces["html"], "details"), + (namespaces["html"], "dir"), + (namespaces["html"], "div"), + (namespaces["html"], "dl"), + (namespaces["html"], "dt"), + (namespaces["html"], "embed"), + (namespaces["html"], "fieldset"), + (namespaces["html"], "figure"), + (namespaces["html"], "footer"), + (namespaces["html"], "form"), + (namespaces["html"], "frame"), + (namespaces["html"], "frameset"), + (namespaces["html"], "h1"), + (namespaces["html"], "h2"), + (namespaces["html"], "h3"), + (namespaces["html"], "h4"), + (namespaces["html"], "h5"), + (namespaces["html"], "h6"), + (namespaces["html"], "head"), + (namespaces["html"], "header"), + (namespaces["html"], "hr"), + (namespaces["html"], "html"), + (namespaces["html"], "iframe"), + # Note that image is commented out in the spec as "this isn't an + # element that can end up on the stack, so it doesn't matter," + (namespaces["html"], "image"), + (namespaces["html"], "img"), + (namespaces["html"], "input"), + (namespaces["html"], "isindex"), + (namespaces["html"], "li"), + (namespaces["html"], "link"), + (namespaces["html"], "listing"), + (namespaces["html"], "marquee"), + (namespaces["html"], "menu"), + (namespaces["html"], "meta"), + (namespaces["html"], "nav"), + (namespaces["html"], "noembed"), + (namespaces["html"], "noframes"), + (namespaces["html"], "noscript"), + (namespaces["html"], "object"), + (namespaces["html"], "ol"), + (namespaces["html"], "p"), + (namespaces["html"], "param"), + (namespaces["html"], "plaintext"), + (namespaces["html"], "pre"), + (namespaces["html"], "script"), + (namespaces["html"], "section"), + (namespaces["html"], "select"), + (namespaces["html"], "style"), + (namespaces["html"], "table"), + (namespaces["html"], "tbody"), + (namespaces["html"], "td"), + (namespaces["html"], "textarea"), + (namespaces["html"], "tfoot"), + (namespaces["html"], "th"), + (namespaces["html"], "thead"), + (namespaces["html"], "title"), + (namespaces["html"], "tr"), + (namespaces["html"], "ul"), + (namespaces["html"], "wbr"), + (namespaces["html"], "xmp"), + (namespaces["svg"], "foreignObject") +]) + +htmlIntegrationPointElements = frozenset([ + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title") +]) + +mathmlTextIntegrationPointElements = frozenset([ + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext") +]) + +adjustSVGAttributes = { + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan" +} + +adjustMathMLAttributes = {"definitionurl": "definitionURL"} + +adjustForeignAttributes = { + "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), + "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), + "xlink:href": ("xlink", "href", namespaces["xlink"]), + "xlink:role": ("xlink", "role", namespaces["xlink"]), + "xlink:show": ("xlink", "show", namespaces["xlink"]), + "xlink:title": ("xlink", "title", namespaces["xlink"]), + "xlink:type": ("xlink", "type", namespaces["xlink"]), + "xml:base": ("xml", "base", namespaces["xml"]), + "xml:lang": ("xml", "lang", namespaces["xml"]), + "xml:space": ("xml", "space", namespaces["xml"]), + "xmlns": (None, "xmlns", namespaces["xmlns"]), + "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) +} + +unadjustForeignAttributes = {(ns, local): qname for qname, (prefix, local, ns) in + adjustForeignAttributes.items()} + +spaceCharacters = frozenset([ + "\t", + "\n", + "\u000C", + " ", + "\r" +]) + +tableInsertModeElements = frozenset([ + "table", + "tbody", + "tfoot", + "thead", + "tr" +]) + +asciiLowercase = frozenset(string.ascii_lowercase) +asciiUppercase = frozenset(string.ascii_uppercase) +asciiLetters = frozenset(string.ascii_letters) +digits = frozenset(string.digits) +hexDigits = frozenset(string.hexdigits) + +asciiUpper2Lower = {ord(c): ord(c.lower()) for c in string.ascii_uppercase} + +# Heading elements need to be ordered +headingElements = ( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6" +) + +voidElements = frozenset([ + "base", + "command", + "event-source", + "link", + "meta", + "hr", + "br", + "img", + "embed", + "param", + "area", + "col", + "input", + "source", + "track" +]) + +cdataElements = frozenset(['title', 'textarea']) + +rcdataElements = frozenset([ + 'style', + 'script', + 'xmp', + 'iframe', + 'noembed', + 'noframes', + 'noscript' +]) + +booleanAttributes = { + "": frozenset(["irrelevant", "itemscope"]), + "style": frozenset(["scoped"]), + "img": frozenset(["ismap"]), + "audio": frozenset(["autoplay", "controls"]), + "video": frozenset(["autoplay", "controls"]), + "script": frozenset(["defer", "async"]), + "details": frozenset(["open"]), + "datagrid": frozenset(["multiple", "disabled"]), + "command": frozenset(["hidden", "disabled", "checked", "default"]), + "hr": frozenset(["noshade"]), + "menu": frozenset(["autosubmit"]), + "fieldset": frozenset(["disabled", "readonly"]), + "option": frozenset(["disabled", "readonly", "selected"]), + "optgroup": frozenset(["disabled", "readonly"]), + "button": frozenset(["disabled", "autofocus"]), + "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]), + "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]), + "output": frozenset(["disabled", "readonly"]), + "iframe": frozenset(["seamless"]), +} + +# entitiesWindows1252 has to be _ordered_ and needs to have an index. It +# therefore can't be a frozenset. +entitiesWindows1252 = ( + 8364, # 0x80 0x20AC EURO SIGN + 65533, # 0x81 UNDEFINED + 8218, # 0x82 0x201A SINGLE LOW-9 QUOTATION MARK + 402, # 0x83 0x0192 LATIN SMALL LETTER F WITH HOOK + 8222, # 0x84 0x201E DOUBLE LOW-9 QUOTATION MARK + 8230, # 0x85 0x2026 HORIZONTAL ELLIPSIS + 8224, # 0x86 0x2020 DAGGER + 8225, # 0x87 0x2021 DOUBLE DAGGER + 710, # 0x88 0x02C6 MODIFIER LETTER CIRCUMFLEX ACCENT + 8240, # 0x89 0x2030 PER MILLE SIGN + 352, # 0x8A 0x0160 LATIN CAPITAL LETTER S WITH CARON + 8249, # 0x8B 0x2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 338, # 0x8C 0x0152 LATIN CAPITAL LIGATURE OE + 65533, # 0x8D UNDEFINED + 381, # 0x8E 0x017D LATIN CAPITAL LETTER Z WITH CARON + 65533, # 0x8F UNDEFINED + 65533, # 0x90 UNDEFINED + 8216, # 0x91 0x2018 LEFT SINGLE QUOTATION MARK + 8217, # 0x92 0x2019 RIGHT SINGLE QUOTATION MARK + 8220, # 0x93 0x201C LEFT DOUBLE QUOTATION MARK + 8221, # 0x94 0x201D RIGHT DOUBLE QUOTATION MARK + 8226, # 0x95 0x2022 BULLET + 8211, # 0x96 0x2013 EN DASH + 8212, # 0x97 0x2014 EM DASH + 732, # 0x98 0x02DC SMALL TILDE + 8482, # 0x99 0x2122 TRADE MARK SIGN + 353, # 0x9A 0x0161 LATIN SMALL LETTER S WITH CARON + 8250, # 0x9B 0x203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE + 65533, # 0x9D UNDEFINED + 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON + 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +) + +xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;']) + +entities = { + "AElig": "\xc6", + "AElig;": "\xc6", + "AMP": "&", + "AMP;": "&", + "Aacute": "\xc1", + "Aacute;": "\xc1", + "Abreve;": "\u0102", + "Acirc": "\xc2", + "Acirc;": "\xc2", + "Acy;": "\u0410", + "Afr;": "\U0001d504", + "Agrave": "\xc0", + "Agrave;": "\xc0", + "Alpha;": "\u0391", + "Amacr;": "\u0100", + "And;": "\u2a53", + "Aogon;": "\u0104", + "Aopf;": "\U0001d538", + "ApplyFunction;": "\u2061", + "Aring": "\xc5", + "Aring;": "\xc5", + "Ascr;": "\U0001d49c", + "Assign;": "\u2254", + "Atilde": "\xc3", + "Atilde;": "\xc3", + "Auml": "\xc4", + "Auml;": "\xc4", + "Backslash;": "\u2216", + "Barv;": "\u2ae7", + "Barwed;": "\u2306", + "Bcy;": "\u0411", + "Because;": "\u2235", + "Bernoullis;": "\u212c", + "Beta;": "\u0392", + "Bfr;": "\U0001d505", + "Bopf;": "\U0001d539", + "Breve;": "\u02d8", + "Bscr;": "\u212c", + "Bumpeq;": "\u224e", + "CHcy;": "\u0427", + "COPY": "\xa9", + "COPY;": "\xa9", + "Cacute;": "\u0106", + "Cap;": "\u22d2", + "CapitalDifferentialD;": "\u2145", + "Cayleys;": "\u212d", + "Ccaron;": "\u010c", + "Ccedil": "\xc7", + "Ccedil;": "\xc7", + "Ccirc;": "\u0108", + "Cconint;": "\u2230", + "Cdot;": "\u010a", + "Cedilla;": "\xb8", + "CenterDot;": "\xb7", + "Cfr;": "\u212d", + "Chi;": "\u03a7", + "CircleDot;": "\u2299", + "CircleMinus;": "\u2296", + "CirclePlus;": "\u2295", + "CircleTimes;": "\u2297", + "ClockwiseContourIntegral;": "\u2232", + "CloseCurlyDoubleQuote;": "\u201d", + "CloseCurlyQuote;": "\u2019", + "Colon;": "\u2237", + "Colone;": "\u2a74", + "Congruent;": "\u2261", + "Conint;": "\u222f", + "ContourIntegral;": "\u222e", + "Copf;": "\u2102", + "Coproduct;": "\u2210", + "CounterClockwiseContourIntegral;": "\u2233", + "Cross;": "\u2a2f", + "Cscr;": "\U0001d49e", + "Cup;": "\u22d3", + "CupCap;": "\u224d", + "DD;": "\u2145", + "DDotrahd;": "\u2911", + "DJcy;": "\u0402", + "DScy;": "\u0405", + "DZcy;": "\u040f", + "Dagger;": "\u2021", + "Darr;": "\u21a1", + "Dashv;": "\u2ae4", + "Dcaron;": "\u010e", + "Dcy;": "\u0414", + "Del;": "\u2207", + "Delta;": "\u0394", + "Dfr;": "\U0001d507", + "DiacriticalAcute;": "\xb4", + "DiacriticalDot;": "\u02d9", + "DiacriticalDoubleAcute;": "\u02dd", + "DiacriticalGrave;": "`", + "DiacriticalTilde;": "\u02dc", + "Diamond;": "\u22c4", + "DifferentialD;": "\u2146", + "Dopf;": "\U0001d53b", + "Dot;": "\xa8", + "DotDot;": "\u20dc", + "DotEqual;": "\u2250", + "DoubleContourIntegral;": "\u222f", + "DoubleDot;": "\xa8", + "DoubleDownArrow;": "\u21d3", + "DoubleLeftArrow;": "\u21d0", + "DoubleLeftRightArrow;": "\u21d4", + "DoubleLeftTee;": "\u2ae4", + "DoubleLongLeftArrow;": "\u27f8", + "DoubleLongLeftRightArrow;": "\u27fa", + "DoubleLongRightArrow;": "\u27f9", + "DoubleRightArrow;": "\u21d2", + "DoubleRightTee;": "\u22a8", + "DoubleUpArrow;": "\u21d1", + "DoubleUpDownArrow;": "\u21d5", + "DoubleVerticalBar;": "\u2225", + "DownArrow;": "\u2193", + "DownArrowBar;": "\u2913", + "DownArrowUpArrow;": "\u21f5", + "DownBreve;": "\u0311", + "DownLeftRightVector;": "\u2950", + "DownLeftTeeVector;": "\u295e", + "DownLeftVector;": "\u21bd", + "DownLeftVectorBar;": "\u2956", + "DownRightTeeVector;": "\u295f", + "DownRightVector;": "\u21c1", + "DownRightVectorBar;": "\u2957", + "DownTee;": "\u22a4", + "DownTeeArrow;": "\u21a7", + "Downarrow;": "\u21d3", + "Dscr;": "\U0001d49f", + "Dstrok;": "\u0110", + "ENG;": "\u014a", + "ETH": "\xd0", + "ETH;": "\xd0", + "Eacute": "\xc9", + "Eacute;": "\xc9", + "Ecaron;": "\u011a", + "Ecirc": "\xca", + "Ecirc;": "\xca", + "Ecy;": "\u042d", + "Edot;": "\u0116", + "Efr;": "\U0001d508", + "Egrave": "\xc8", + "Egrave;": "\xc8", + "Element;": "\u2208", + "Emacr;": "\u0112", + "EmptySmallSquare;": "\u25fb", + "EmptyVerySmallSquare;": "\u25ab", + "Eogon;": "\u0118", + "Eopf;": "\U0001d53c", + "Epsilon;": "\u0395", + "Equal;": "\u2a75", + "EqualTilde;": "\u2242", + "Equilibrium;": "\u21cc", + "Escr;": "\u2130", + "Esim;": "\u2a73", + "Eta;": "\u0397", + "Euml": "\xcb", + "Euml;": "\xcb", + "Exists;": "\u2203", + "ExponentialE;": "\u2147", + "Fcy;": "\u0424", + "Ffr;": "\U0001d509", + "FilledSmallSquare;": "\u25fc", + "FilledVerySmallSquare;": "\u25aa", + "Fopf;": "\U0001d53d", + "ForAll;": "\u2200", + "Fouriertrf;": "\u2131", + "Fscr;": "\u2131", + "GJcy;": "\u0403", + "GT": ">", + "GT;": ">", + "Gamma;": "\u0393", + "Gammad;": "\u03dc", + "Gbreve;": "\u011e", + "Gcedil;": "\u0122", + "Gcirc;": "\u011c", + "Gcy;": "\u0413", + "Gdot;": "\u0120", + "Gfr;": "\U0001d50a", + "Gg;": "\u22d9", + "Gopf;": "\U0001d53e", + "GreaterEqual;": "\u2265", + "GreaterEqualLess;": "\u22db", + "GreaterFullEqual;": "\u2267", + "GreaterGreater;": "\u2aa2", + "GreaterLess;": "\u2277", + "GreaterSlantEqual;": "\u2a7e", + "GreaterTilde;": "\u2273", + "Gscr;": "\U0001d4a2", + "Gt;": "\u226b", + "HARDcy;": "\u042a", + "Hacek;": "\u02c7", + "Hat;": "^", + "Hcirc;": "\u0124", + "Hfr;": "\u210c", + "HilbertSpace;": "\u210b", + "Hopf;": "\u210d", + "HorizontalLine;": "\u2500", + "Hscr;": "\u210b", + "Hstrok;": "\u0126", + "HumpDownHump;": "\u224e", + "HumpEqual;": "\u224f", + "IEcy;": "\u0415", + "IJlig;": "\u0132", + "IOcy;": "\u0401", + "Iacute": "\xcd", + "Iacute;": "\xcd", + "Icirc": "\xce", + "Icirc;": "\xce", + "Icy;": "\u0418", + "Idot;": "\u0130", + "Ifr;": "\u2111", + "Igrave": "\xcc", + "Igrave;": "\xcc", + "Im;": "\u2111", + "Imacr;": "\u012a", + "ImaginaryI;": "\u2148", + "Implies;": "\u21d2", + "Int;": "\u222c", + "Integral;": "\u222b", + "Intersection;": "\u22c2", + "InvisibleComma;": "\u2063", + "InvisibleTimes;": "\u2062", + "Iogon;": "\u012e", + "Iopf;": "\U0001d540", + "Iota;": "\u0399", + "Iscr;": "\u2110", + "Itilde;": "\u0128", + "Iukcy;": "\u0406", + "Iuml": "\xcf", + "Iuml;": "\xcf", + "Jcirc;": "\u0134", + "Jcy;": "\u0419", + "Jfr;": "\U0001d50d", + "Jopf;": "\U0001d541", + "Jscr;": "\U0001d4a5", + "Jsercy;": "\u0408", + "Jukcy;": "\u0404", + "KHcy;": "\u0425", + "KJcy;": "\u040c", + "Kappa;": "\u039a", + "Kcedil;": "\u0136", + "Kcy;": "\u041a", + "Kfr;": "\U0001d50e", + "Kopf;": "\U0001d542", + "Kscr;": "\U0001d4a6", + "LJcy;": "\u0409", + "LT": "<", + "LT;": "<", + "Lacute;": "\u0139", + "Lambda;": "\u039b", + "Lang;": "\u27ea", + "Laplacetrf;": "\u2112", + "Larr;": "\u219e", + "Lcaron;": "\u013d", + "Lcedil;": "\u013b", + "Lcy;": "\u041b", + "LeftAngleBracket;": "\u27e8", + "LeftArrow;": "\u2190", + "LeftArrowBar;": "\u21e4", + "LeftArrowRightArrow;": "\u21c6", + "LeftCeiling;": "\u2308", + "LeftDoubleBracket;": "\u27e6", + "LeftDownTeeVector;": "\u2961", + "LeftDownVector;": "\u21c3", + "LeftDownVectorBar;": "\u2959", + "LeftFloor;": "\u230a", + "LeftRightArrow;": "\u2194", + "LeftRightVector;": "\u294e", + "LeftTee;": "\u22a3", + "LeftTeeArrow;": "\u21a4", + "LeftTeeVector;": "\u295a", + "LeftTriangle;": "\u22b2", + "LeftTriangleBar;": "\u29cf", + "LeftTriangleEqual;": "\u22b4", + "LeftUpDownVector;": "\u2951", + "LeftUpTeeVector;": "\u2960", + "LeftUpVector;": "\u21bf", + "LeftUpVectorBar;": "\u2958", + "LeftVector;": "\u21bc", + "LeftVectorBar;": "\u2952", + "Leftarrow;": "\u21d0", + "Leftrightarrow;": "\u21d4", + "LessEqualGreater;": "\u22da", + "LessFullEqual;": "\u2266", + "LessGreater;": "\u2276", + "LessLess;": "\u2aa1", + "LessSlantEqual;": "\u2a7d", + "LessTilde;": "\u2272", + "Lfr;": "\U0001d50f", + "Ll;": "\u22d8", + "Lleftarrow;": "\u21da", + "Lmidot;": "\u013f", + "LongLeftArrow;": "\u27f5", + "LongLeftRightArrow;": "\u27f7", + "LongRightArrow;": "\u27f6", + "Longleftarrow;": "\u27f8", + "Longleftrightarrow;": "\u27fa", + "Longrightarrow;": "\u27f9", + "Lopf;": "\U0001d543", + "LowerLeftArrow;": "\u2199", + "LowerRightArrow;": "\u2198", + "Lscr;": "\u2112", + "Lsh;": "\u21b0", + "Lstrok;": "\u0141", + "Lt;": "\u226a", + "Map;": "\u2905", + "Mcy;": "\u041c", + "MediumSpace;": "\u205f", + "Mellintrf;": "\u2133", + "Mfr;": "\U0001d510", + "MinusPlus;": "\u2213", + "Mopf;": "\U0001d544", + "Mscr;": "\u2133", + "Mu;": "\u039c", + "NJcy;": "\u040a", + "Nacute;": "\u0143", + "Ncaron;": "\u0147", + "Ncedil;": "\u0145", + "Ncy;": "\u041d", + "NegativeMediumSpace;": "\u200b", + "NegativeThickSpace;": "\u200b", + "NegativeThinSpace;": "\u200b", + "NegativeVeryThinSpace;": "\u200b", + "NestedGreaterGreater;": "\u226b", + "NestedLessLess;": "\u226a", + "NewLine;": "\n", + "Nfr;": "\U0001d511", + "NoBreak;": "\u2060", + "NonBreakingSpace;": "\xa0", + "Nopf;": "\u2115", + "Not;": "\u2aec", + "NotCongruent;": "\u2262", + "NotCupCap;": "\u226d", + "NotDoubleVerticalBar;": "\u2226", + "NotElement;": "\u2209", + "NotEqual;": "\u2260", + "NotEqualTilde;": "\u2242\u0338", + "NotExists;": "\u2204", + "NotGreater;": "\u226f", + "NotGreaterEqual;": "\u2271", + "NotGreaterFullEqual;": "\u2267\u0338", + "NotGreaterGreater;": "\u226b\u0338", + "NotGreaterLess;": "\u2279", + "NotGreaterSlantEqual;": "\u2a7e\u0338", + "NotGreaterTilde;": "\u2275", + "NotHumpDownHump;": "\u224e\u0338", + "NotHumpEqual;": "\u224f\u0338", + "NotLeftTriangle;": "\u22ea", + "NotLeftTriangleBar;": "\u29cf\u0338", + "NotLeftTriangleEqual;": "\u22ec", + "NotLess;": "\u226e", + "NotLessEqual;": "\u2270", + "NotLessGreater;": "\u2278", + "NotLessLess;": "\u226a\u0338", + "NotLessSlantEqual;": "\u2a7d\u0338", + "NotLessTilde;": "\u2274", + "NotNestedGreaterGreater;": "\u2aa2\u0338", + "NotNestedLessLess;": "\u2aa1\u0338", + "NotPrecedes;": "\u2280", + "NotPrecedesEqual;": "\u2aaf\u0338", + "NotPrecedesSlantEqual;": "\u22e0", + "NotReverseElement;": "\u220c", + "NotRightTriangle;": "\u22eb", + "NotRightTriangleBar;": "\u29d0\u0338", + "NotRightTriangleEqual;": "\u22ed", + "NotSquareSubset;": "\u228f\u0338", + "NotSquareSubsetEqual;": "\u22e2", + "NotSquareSuperset;": "\u2290\u0338", + "NotSquareSupersetEqual;": "\u22e3", + "NotSubset;": "\u2282\u20d2", + "NotSubsetEqual;": "\u2288", + "NotSucceeds;": "\u2281", + "NotSucceedsEqual;": "\u2ab0\u0338", + "NotSucceedsSlantEqual;": "\u22e1", + "NotSucceedsTilde;": "\u227f\u0338", + "NotSuperset;": "\u2283\u20d2", + "NotSupersetEqual;": "\u2289", + "NotTilde;": "\u2241", + "NotTildeEqual;": "\u2244", + "NotTildeFullEqual;": "\u2247", + "NotTildeTilde;": "\u2249", + "NotVerticalBar;": "\u2224", + "Nscr;": "\U0001d4a9", + "Ntilde": "\xd1", + "Ntilde;": "\xd1", + "Nu;": "\u039d", + "OElig;": "\u0152", + "Oacute": "\xd3", + "Oacute;": "\xd3", + "Ocirc": "\xd4", + "Ocirc;": "\xd4", + "Ocy;": "\u041e", + "Odblac;": "\u0150", + "Ofr;": "\U0001d512", + "Ograve": "\xd2", + "Ograve;": "\xd2", + "Omacr;": "\u014c", + "Omega;": "\u03a9", + "Omicron;": "\u039f", + "Oopf;": "\U0001d546", + "OpenCurlyDoubleQuote;": "\u201c", + "OpenCurlyQuote;": "\u2018", + "Or;": "\u2a54", + "Oscr;": "\U0001d4aa", + "Oslash": "\xd8", + "Oslash;": "\xd8", + "Otilde": "\xd5", + "Otilde;": "\xd5", + "Otimes;": "\u2a37", + "Ouml": "\xd6", + "Ouml;": "\xd6", + "OverBar;": "\u203e", + "OverBrace;": "\u23de", + "OverBracket;": "\u23b4", + "OverParenthesis;": "\u23dc", + "PartialD;": "\u2202", + "Pcy;": "\u041f", + "Pfr;": "\U0001d513", + "Phi;": "\u03a6", + "Pi;": "\u03a0", + "PlusMinus;": "\xb1", + "Poincareplane;": "\u210c", + "Popf;": "\u2119", + "Pr;": "\u2abb", + "Precedes;": "\u227a", + "PrecedesEqual;": "\u2aaf", + "PrecedesSlantEqual;": "\u227c", + "PrecedesTilde;": "\u227e", + "Prime;": "\u2033", + "Product;": "\u220f", + "Proportion;": "\u2237", + "Proportional;": "\u221d", + "Pscr;": "\U0001d4ab", + "Psi;": "\u03a8", + "QUOT": "\"", + "QUOT;": "\"", + "Qfr;": "\U0001d514", + "Qopf;": "\u211a", + "Qscr;": "\U0001d4ac", + "RBarr;": "\u2910", + "REG": "\xae", + "REG;": "\xae", + "Racute;": "\u0154", + "Rang;": "\u27eb", + "Rarr;": "\u21a0", + "Rarrtl;": "\u2916", + "Rcaron;": "\u0158", + "Rcedil;": "\u0156", + "Rcy;": "\u0420", + "Re;": "\u211c", + "ReverseElement;": "\u220b", + "ReverseEquilibrium;": "\u21cb", + "ReverseUpEquilibrium;": "\u296f", + "Rfr;": "\u211c", + "Rho;": "\u03a1", + "RightAngleBracket;": "\u27e9", + "RightArrow;": "\u2192", + "RightArrowBar;": "\u21e5", + "RightArrowLeftArrow;": "\u21c4", + "RightCeiling;": "\u2309", + "RightDoubleBracket;": "\u27e7", + "RightDownTeeVector;": "\u295d", + "RightDownVector;": "\u21c2", + "RightDownVectorBar;": "\u2955", + "RightFloor;": "\u230b", + "RightTee;": "\u22a2", + "RightTeeArrow;": "\u21a6", + "RightTeeVector;": "\u295b", + "RightTriangle;": "\u22b3", + "RightTriangleBar;": "\u29d0", + "RightTriangleEqual;": "\u22b5", + "RightUpDownVector;": "\u294f", + "RightUpTeeVector;": "\u295c", + "RightUpVector;": "\u21be", + "RightUpVectorBar;": "\u2954", + "RightVector;": "\u21c0", + "RightVectorBar;": "\u2953", + "Rightarrow;": "\u21d2", + "Ropf;": "\u211d", + "RoundImplies;": "\u2970", + "Rrightarrow;": "\u21db", + "Rscr;": "\u211b", + "Rsh;": "\u21b1", + "RuleDelayed;": "\u29f4", + "SHCHcy;": "\u0429", + "SHcy;": "\u0428", + "SOFTcy;": "\u042c", + "Sacute;": "\u015a", + "Sc;": "\u2abc", + "Scaron;": "\u0160", + "Scedil;": "\u015e", + "Scirc;": "\u015c", + "Scy;": "\u0421", + "Sfr;": "\U0001d516", + "ShortDownArrow;": "\u2193", + "ShortLeftArrow;": "\u2190", + "ShortRightArrow;": "\u2192", + "ShortUpArrow;": "\u2191", + "Sigma;": "\u03a3", + "SmallCircle;": "\u2218", + "Sopf;": "\U0001d54a", + "Sqrt;": "\u221a", + "Square;": "\u25a1", + "SquareIntersection;": "\u2293", + "SquareSubset;": "\u228f", + "SquareSubsetEqual;": "\u2291", + "SquareSuperset;": "\u2290", + "SquareSupersetEqual;": "\u2292", + "SquareUnion;": "\u2294", + "Sscr;": "\U0001d4ae", + "Star;": "\u22c6", + "Sub;": "\u22d0", + "Subset;": "\u22d0", + "SubsetEqual;": "\u2286", + "Succeeds;": "\u227b", + "SucceedsEqual;": "\u2ab0", + "SucceedsSlantEqual;": "\u227d", + "SucceedsTilde;": "\u227f", + "SuchThat;": "\u220b", + "Sum;": "\u2211", + "Sup;": "\u22d1", + "Superset;": "\u2283", + "SupersetEqual;": "\u2287", + "Supset;": "\u22d1", + "THORN": "\xde", + "THORN;": "\xde", + "TRADE;": "\u2122", + "TSHcy;": "\u040b", + "TScy;": "\u0426", + "Tab;": "\t", + "Tau;": "\u03a4", + "Tcaron;": "\u0164", + "Tcedil;": "\u0162", + "Tcy;": "\u0422", + "Tfr;": "\U0001d517", + "Therefore;": "\u2234", + "Theta;": "\u0398", + "ThickSpace;": "\u205f\u200a", + "ThinSpace;": "\u2009", + "Tilde;": "\u223c", + "TildeEqual;": "\u2243", + "TildeFullEqual;": "\u2245", + "TildeTilde;": "\u2248", + "Topf;": "\U0001d54b", + "TripleDot;": "\u20db", + "Tscr;": "\U0001d4af", + "Tstrok;": "\u0166", + "Uacute": "\xda", + "Uacute;": "\xda", + "Uarr;": "\u219f", + "Uarrocir;": "\u2949", + "Ubrcy;": "\u040e", + "Ubreve;": "\u016c", + "Ucirc": "\xdb", + "Ucirc;": "\xdb", + "Ucy;": "\u0423", + "Udblac;": "\u0170", + "Ufr;": "\U0001d518", + "Ugrave": "\xd9", + "Ugrave;": "\xd9", + "Umacr;": "\u016a", + "UnderBar;": "_", + "UnderBrace;": "\u23df", + "UnderBracket;": "\u23b5", + "UnderParenthesis;": "\u23dd", + "Union;": "\u22c3", + "UnionPlus;": "\u228e", + "Uogon;": "\u0172", + "Uopf;": "\U0001d54c", + "UpArrow;": "\u2191", + "UpArrowBar;": "\u2912", + "UpArrowDownArrow;": "\u21c5", + "UpDownArrow;": "\u2195", + "UpEquilibrium;": "\u296e", + "UpTee;": "\u22a5", + "UpTeeArrow;": "\u21a5", + "Uparrow;": "\u21d1", + "Updownarrow;": "\u21d5", + "UpperLeftArrow;": "\u2196", + "UpperRightArrow;": "\u2197", + "Upsi;": "\u03d2", + "Upsilon;": "\u03a5", + "Uring;": "\u016e", + "Uscr;": "\U0001d4b0", + "Utilde;": "\u0168", + "Uuml": "\xdc", + "Uuml;": "\xdc", + "VDash;": "\u22ab", + "Vbar;": "\u2aeb", + "Vcy;": "\u0412", + "Vdash;": "\u22a9", + "Vdashl;": "\u2ae6", + "Vee;": "\u22c1", + "Verbar;": "\u2016", + "Vert;": "\u2016", + "VerticalBar;": "\u2223", + "VerticalLine;": "|", + "VerticalSeparator;": "\u2758", + "VerticalTilde;": "\u2240", + "VeryThinSpace;": "\u200a", + "Vfr;": "\U0001d519", + "Vopf;": "\U0001d54d", + "Vscr;": "\U0001d4b1", + "Vvdash;": "\u22aa", + "Wcirc;": "\u0174", + "Wedge;": "\u22c0", + "Wfr;": "\U0001d51a", + "Wopf;": "\U0001d54e", + "Wscr;": "\U0001d4b2", + "Xfr;": "\U0001d51b", + "Xi;": "\u039e", + "Xopf;": "\U0001d54f", + "Xscr;": "\U0001d4b3", + "YAcy;": "\u042f", + "YIcy;": "\u0407", + "YUcy;": "\u042e", + "Yacute": "\xdd", + "Yacute;": "\xdd", + "Ycirc;": "\u0176", + "Ycy;": "\u042b", + "Yfr;": "\U0001d51c", + "Yopf;": "\U0001d550", + "Yscr;": "\U0001d4b4", + "Yuml;": "\u0178", + "ZHcy;": "\u0416", + "Zacute;": "\u0179", + "Zcaron;": "\u017d", + "Zcy;": "\u0417", + "Zdot;": "\u017b", + "ZeroWidthSpace;": "\u200b", + "Zeta;": "\u0396", + "Zfr;": "\u2128", + "Zopf;": "\u2124", + "Zscr;": "\U0001d4b5", + "aacute": "\xe1", + "aacute;": "\xe1", + "abreve;": "\u0103", + "ac;": "\u223e", + "acE;": "\u223e\u0333", + "acd;": "\u223f", + "acirc": "\xe2", + "acirc;": "\xe2", + "acute": "\xb4", + "acute;": "\xb4", + "acy;": "\u0430", + "aelig": "\xe6", + "aelig;": "\xe6", + "af;": "\u2061", + "afr;": "\U0001d51e", + "agrave": "\xe0", + "agrave;": "\xe0", + "alefsym;": "\u2135", + "aleph;": "\u2135", + "alpha;": "\u03b1", + "amacr;": "\u0101", + "amalg;": "\u2a3f", + "amp": "&", + "amp;": "&", + "and;": "\u2227", + "andand;": "\u2a55", + "andd;": "\u2a5c", + "andslope;": "\u2a58", + "andv;": "\u2a5a", + "ang;": "\u2220", + "ange;": "\u29a4", + "angle;": "\u2220", + "angmsd;": "\u2221", + "angmsdaa;": "\u29a8", + "angmsdab;": "\u29a9", + "angmsdac;": "\u29aa", + "angmsdad;": "\u29ab", + "angmsdae;": "\u29ac", + "angmsdaf;": "\u29ad", + "angmsdag;": "\u29ae", + "angmsdah;": "\u29af", + "angrt;": "\u221f", + "angrtvb;": "\u22be", + "angrtvbd;": "\u299d", + "angsph;": "\u2222", + "angst;": "\xc5", + "angzarr;": "\u237c", + "aogon;": "\u0105", + "aopf;": "\U0001d552", + "ap;": "\u2248", + "apE;": "\u2a70", + "apacir;": "\u2a6f", + "ape;": "\u224a", + "apid;": "\u224b", + "apos;": "'", + "approx;": "\u2248", + "approxeq;": "\u224a", + "aring": "\xe5", + "aring;": "\xe5", + "ascr;": "\U0001d4b6", + "ast;": "*", + "asymp;": "\u2248", + "asympeq;": "\u224d", + "atilde": "\xe3", + "atilde;": "\xe3", + "auml": "\xe4", + "auml;": "\xe4", + "awconint;": "\u2233", + "awint;": "\u2a11", + "bNot;": "\u2aed", + "backcong;": "\u224c", + "backepsilon;": "\u03f6", + "backprime;": "\u2035", + "backsim;": "\u223d", + "backsimeq;": "\u22cd", + "barvee;": "\u22bd", + "barwed;": "\u2305", + "barwedge;": "\u2305", + "bbrk;": "\u23b5", + "bbrktbrk;": "\u23b6", + "bcong;": "\u224c", + "bcy;": "\u0431", + "bdquo;": "\u201e", + "becaus;": "\u2235", + "because;": "\u2235", + "bemptyv;": "\u29b0", + "bepsi;": "\u03f6", + "bernou;": "\u212c", + "beta;": "\u03b2", + "beth;": "\u2136", + "between;": "\u226c", + "bfr;": "\U0001d51f", + "bigcap;": "\u22c2", + "bigcirc;": "\u25ef", + "bigcup;": "\u22c3", + "bigodot;": "\u2a00", + "bigoplus;": "\u2a01", + "bigotimes;": "\u2a02", + "bigsqcup;": "\u2a06", + "bigstar;": "\u2605", + "bigtriangledown;": "\u25bd", + "bigtriangleup;": "\u25b3", + "biguplus;": "\u2a04", + "bigvee;": "\u22c1", + "bigwedge;": "\u22c0", + "bkarow;": "\u290d", + "blacklozenge;": "\u29eb", + "blacksquare;": "\u25aa", + "blacktriangle;": "\u25b4", + "blacktriangledown;": "\u25be", + "blacktriangleleft;": "\u25c2", + "blacktriangleright;": "\u25b8", + "blank;": "\u2423", + "blk12;": "\u2592", + "blk14;": "\u2591", + "blk34;": "\u2593", + "block;": "\u2588", + "bne;": "=\u20e5", + "bnequiv;": "\u2261\u20e5", + "bnot;": "\u2310", + "bopf;": "\U0001d553", + "bot;": "\u22a5", + "bottom;": "\u22a5", + "bowtie;": "\u22c8", + "boxDL;": "\u2557", + "boxDR;": "\u2554", + "boxDl;": "\u2556", + "boxDr;": "\u2553", + "boxH;": "\u2550", + "boxHD;": "\u2566", + "boxHU;": "\u2569", + "boxHd;": "\u2564", + "boxHu;": "\u2567", + "boxUL;": "\u255d", + "boxUR;": "\u255a", + "boxUl;": "\u255c", + "boxUr;": "\u2559", + "boxV;": "\u2551", + "boxVH;": "\u256c", + "boxVL;": "\u2563", + "boxVR;": "\u2560", + "boxVh;": "\u256b", + "boxVl;": "\u2562", + "boxVr;": "\u255f", + "boxbox;": "\u29c9", + "boxdL;": "\u2555", + "boxdR;": "\u2552", + "boxdl;": "\u2510", + "boxdr;": "\u250c", + "boxh;": "\u2500", + "boxhD;": "\u2565", + "boxhU;": "\u2568", + "boxhd;": "\u252c", + "boxhu;": "\u2534", + "boxminus;": "\u229f", + "boxplus;": "\u229e", + "boxtimes;": "\u22a0", + "boxuL;": "\u255b", + "boxuR;": "\u2558", + "boxul;": "\u2518", + "boxur;": "\u2514", + "boxv;": "\u2502", + "boxvH;": "\u256a", + "boxvL;": "\u2561", + "boxvR;": "\u255e", + "boxvh;": "\u253c", + "boxvl;": "\u2524", + "boxvr;": "\u251c", + "bprime;": "\u2035", + "breve;": "\u02d8", + "brvbar": "\xa6", + "brvbar;": "\xa6", + "bscr;": "\U0001d4b7", + "bsemi;": "\u204f", + "bsim;": "\u223d", + "bsime;": "\u22cd", + "bsol;": "\\", + "bsolb;": "\u29c5", + "bsolhsub;": "\u27c8", + "bull;": "\u2022", + "bullet;": "\u2022", + "bump;": "\u224e", + "bumpE;": "\u2aae", + "bumpe;": "\u224f", + "bumpeq;": "\u224f", + "cacute;": "\u0107", + "cap;": "\u2229", + "capand;": "\u2a44", + "capbrcup;": "\u2a49", + "capcap;": "\u2a4b", + "capcup;": "\u2a47", + "capdot;": "\u2a40", + "caps;": "\u2229\ufe00", + "caret;": "\u2041", + "caron;": "\u02c7", + "ccaps;": "\u2a4d", + "ccaron;": "\u010d", + "ccedil": "\xe7", + "ccedil;": "\xe7", + "ccirc;": "\u0109", + "ccups;": "\u2a4c", + "ccupssm;": "\u2a50", + "cdot;": "\u010b", + "cedil": "\xb8", + "cedil;": "\xb8", + "cemptyv;": "\u29b2", + "cent": "\xa2", + "cent;": "\xa2", + "centerdot;": "\xb7", + "cfr;": "\U0001d520", + "chcy;": "\u0447", + "check;": "\u2713", + "checkmark;": "\u2713", + "chi;": "\u03c7", + "cir;": "\u25cb", + "cirE;": "\u29c3", + "circ;": "\u02c6", + "circeq;": "\u2257", + "circlearrowleft;": "\u21ba", + "circlearrowright;": "\u21bb", + "circledR;": "\xae", + "circledS;": "\u24c8", + "circledast;": "\u229b", + "circledcirc;": "\u229a", + "circleddash;": "\u229d", + "cire;": "\u2257", + "cirfnint;": "\u2a10", + "cirmid;": "\u2aef", + "cirscir;": "\u29c2", + "clubs;": "\u2663", + "clubsuit;": "\u2663", + "colon;": ":", + "colone;": "\u2254", + "coloneq;": "\u2254", + "comma;": ",", + "commat;": "@", + "comp;": "\u2201", + "compfn;": "\u2218", + "complement;": "\u2201", + "complexes;": "\u2102", + "cong;": "\u2245", + "congdot;": "\u2a6d", + "conint;": "\u222e", + "copf;": "\U0001d554", + "coprod;": "\u2210", + "copy": "\xa9", + "copy;": "\xa9", + "copysr;": "\u2117", + "crarr;": "\u21b5", + "cross;": "\u2717", + "cscr;": "\U0001d4b8", + "csub;": "\u2acf", + "csube;": "\u2ad1", + "csup;": "\u2ad0", + "csupe;": "\u2ad2", + "ctdot;": "\u22ef", + "cudarrl;": "\u2938", + "cudarrr;": "\u2935", + "cuepr;": "\u22de", + "cuesc;": "\u22df", + "cularr;": "\u21b6", + "cularrp;": "\u293d", + "cup;": "\u222a", + "cupbrcap;": "\u2a48", + "cupcap;": "\u2a46", + "cupcup;": "\u2a4a", + "cupdot;": "\u228d", + "cupor;": "\u2a45", + "cups;": "\u222a\ufe00", + "curarr;": "\u21b7", + "curarrm;": "\u293c", + "curlyeqprec;": "\u22de", + "curlyeqsucc;": "\u22df", + "curlyvee;": "\u22ce", + "curlywedge;": "\u22cf", + "curren": "\xa4", + "curren;": "\xa4", + "curvearrowleft;": "\u21b6", + "curvearrowright;": "\u21b7", + "cuvee;": "\u22ce", + "cuwed;": "\u22cf", + "cwconint;": "\u2232", + "cwint;": "\u2231", + "cylcty;": "\u232d", + "dArr;": "\u21d3", + "dHar;": "\u2965", + "dagger;": "\u2020", + "daleth;": "\u2138", + "darr;": "\u2193", + "dash;": "\u2010", + "dashv;": "\u22a3", + "dbkarow;": "\u290f", + "dblac;": "\u02dd", + "dcaron;": "\u010f", + "dcy;": "\u0434", + "dd;": "\u2146", + "ddagger;": "\u2021", + "ddarr;": "\u21ca", + "ddotseq;": "\u2a77", + "deg": "\xb0", + "deg;": "\xb0", + "delta;": "\u03b4", + "demptyv;": "\u29b1", + "dfisht;": "\u297f", + "dfr;": "\U0001d521", + "dharl;": "\u21c3", + "dharr;": "\u21c2", + "diam;": "\u22c4", + "diamond;": "\u22c4", + "diamondsuit;": "\u2666", + "diams;": "\u2666", + "die;": "\xa8", + "digamma;": "\u03dd", + "disin;": "\u22f2", + "div;": "\xf7", + "divide": "\xf7", + "divide;": "\xf7", + "divideontimes;": "\u22c7", + "divonx;": "\u22c7", + "djcy;": "\u0452", + "dlcorn;": "\u231e", + "dlcrop;": "\u230d", + "dollar;": "$", + "dopf;": "\U0001d555", + "dot;": "\u02d9", + "doteq;": "\u2250", + "doteqdot;": "\u2251", + "dotminus;": "\u2238", + "dotplus;": "\u2214", + "dotsquare;": "\u22a1", + "doublebarwedge;": "\u2306", + "downarrow;": "\u2193", + "downdownarrows;": "\u21ca", + "downharpoonleft;": "\u21c3", + "downharpoonright;": "\u21c2", + "drbkarow;": "\u2910", + "drcorn;": "\u231f", + "drcrop;": "\u230c", + "dscr;": "\U0001d4b9", + "dscy;": "\u0455", + "dsol;": "\u29f6", + "dstrok;": "\u0111", + "dtdot;": "\u22f1", + "dtri;": "\u25bf", + "dtrif;": "\u25be", + "duarr;": "\u21f5", + "duhar;": "\u296f", + "dwangle;": "\u29a6", + "dzcy;": "\u045f", + "dzigrarr;": "\u27ff", + "eDDot;": "\u2a77", + "eDot;": "\u2251", + "eacute": "\xe9", + "eacute;": "\xe9", + "easter;": "\u2a6e", + "ecaron;": "\u011b", + "ecir;": "\u2256", + "ecirc": "\xea", + "ecirc;": "\xea", + "ecolon;": "\u2255", + "ecy;": "\u044d", + "edot;": "\u0117", + "ee;": "\u2147", + "efDot;": "\u2252", + "efr;": "\U0001d522", + "eg;": "\u2a9a", + "egrave": "\xe8", + "egrave;": "\xe8", + "egs;": "\u2a96", + "egsdot;": "\u2a98", + "el;": "\u2a99", + "elinters;": "\u23e7", + "ell;": "\u2113", + "els;": "\u2a95", + "elsdot;": "\u2a97", + "emacr;": "\u0113", + "empty;": "\u2205", + "emptyset;": "\u2205", + "emptyv;": "\u2205", + "emsp13;": "\u2004", + "emsp14;": "\u2005", + "emsp;": "\u2003", + "eng;": "\u014b", + "ensp;": "\u2002", + "eogon;": "\u0119", + "eopf;": "\U0001d556", + "epar;": "\u22d5", + "eparsl;": "\u29e3", + "eplus;": "\u2a71", + "epsi;": "\u03b5", + "epsilon;": "\u03b5", + "epsiv;": "\u03f5", + "eqcirc;": "\u2256", + "eqcolon;": "\u2255", + "eqsim;": "\u2242", + "eqslantgtr;": "\u2a96", + "eqslantless;": "\u2a95", + "equals;": "=", + "equest;": "\u225f", + "equiv;": "\u2261", + "equivDD;": "\u2a78", + "eqvparsl;": "\u29e5", + "erDot;": "\u2253", + "erarr;": "\u2971", + "escr;": "\u212f", + "esdot;": "\u2250", + "esim;": "\u2242", + "eta;": "\u03b7", + "eth": "\xf0", + "eth;": "\xf0", + "euml": "\xeb", + "euml;": "\xeb", + "euro;": "\u20ac", + "excl;": "!", + "exist;": "\u2203", + "expectation;": "\u2130", + "exponentiale;": "\u2147", + "fallingdotseq;": "\u2252", + "fcy;": "\u0444", + "female;": "\u2640", + "ffilig;": "\ufb03", + "fflig;": "\ufb00", + "ffllig;": "\ufb04", + "ffr;": "\U0001d523", + "filig;": "\ufb01", + "fjlig;": "fj", + "flat;": "\u266d", + "fllig;": "\ufb02", + "fltns;": "\u25b1", + "fnof;": "\u0192", + "fopf;": "\U0001d557", + "forall;": "\u2200", + "fork;": "\u22d4", + "forkv;": "\u2ad9", + "fpartint;": "\u2a0d", + "frac12": "\xbd", + "frac12;": "\xbd", + "frac13;": "\u2153", + "frac14": "\xbc", + "frac14;": "\xbc", + "frac15;": "\u2155", + "frac16;": "\u2159", + "frac18;": "\u215b", + "frac23;": "\u2154", + "frac25;": "\u2156", + "frac34": "\xbe", + "frac34;": "\xbe", + "frac35;": "\u2157", + "frac38;": "\u215c", + "frac45;": "\u2158", + "frac56;": "\u215a", + "frac58;": "\u215d", + "frac78;": "\u215e", + "frasl;": "\u2044", + "frown;": "\u2322", + "fscr;": "\U0001d4bb", + "gE;": "\u2267", + "gEl;": "\u2a8c", + "gacute;": "\u01f5", + "gamma;": "\u03b3", + "gammad;": "\u03dd", + "gap;": "\u2a86", + "gbreve;": "\u011f", + "gcirc;": "\u011d", + "gcy;": "\u0433", + "gdot;": "\u0121", + "ge;": "\u2265", + "gel;": "\u22db", + "geq;": "\u2265", + "geqq;": "\u2267", + "geqslant;": "\u2a7e", + "ges;": "\u2a7e", + "gescc;": "\u2aa9", + "gesdot;": "\u2a80", + "gesdoto;": "\u2a82", + "gesdotol;": "\u2a84", + "gesl;": "\u22db\ufe00", + "gesles;": "\u2a94", + "gfr;": "\U0001d524", + "gg;": "\u226b", + "ggg;": "\u22d9", + "gimel;": "\u2137", + "gjcy;": "\u0453", + "gl;": "\u2277", + "glE;": "\u2a92", + "gla;": "\u2aa5", + "glj;": "\u2aa4", + "gnE;": "\u2269", + "gnap;": "\u2a8a", + "gnapprox;": "\u2a8a", + "gne;": "\u2a88", + "gneq;": "\u2a88", + "gneqq;": "\u2269", + "gnsim;": "\u22e7", + "gopf;": "\U0001d558", + "grave;": "`", + "gscr;": "\u210a", + "gsim;": "\u2273", + "gsime;": "\u2a8e", + "gsiml;": "\u2a90", + "gt": ">", + "gt;": ">", + "gtcc;": "\u2aa7", + "gtcir;": "\u2a7a", + "gtdot;": "\u22d7", + "gtlPar;": "\u2995", + "gtquest;": "\u2a7c", + "gtrapprox;": "\u2a86", + "gtrarr;": "\u2978", + "gtrdot;": "\u22d7", + "gtreqless;": "\u22db", + "gtreqqless;": "\u2a8c", + "gtrless;": "\u2277", + "gtrsim;": "\u2273", + "gvertneqq;": "\u2269\ufe00", + "gvnE;": "\u2269\ufe00", + "hArr;": "\u21d4", + "hairsp;": "\u200a", + "half;": "\xbd", + "hamilt;": "\u210b", + "hardcy;": "\u044a", + "harr;": "\u2194", + "harrcir;": "\u2948", + "harrw;": "\u21ad", + "hbar;": "\u210f", + "hcirc;": "\u0125", + "hearts;": "\u2665", + "heartsuit;": "\u2665", + "hellip;": "\u2026", + "hercon;": "\u22b9", + "hfr;": "\U0001d525", + "hksearow;": "\u2925", + "hkswarow;": "\u2926", + "hoarr;": "\u21ff", + "homtht;": "\u223b", + "hookleftarrow;": "\u21a9", + "hookrightarrow;": "\u21aa", + "hopf;": "\U0001d559", + "horbar;": "\u2015", + "hscr;": "\U0001d4bd", + "hslash;": "\u210f", + "hstrok;": "\u0127", + "hybull;": "\u2043", + "hyphen;": "\u2010", + "iacute": "\xed", + "iacute;": "\xed", + "ic;": "\u2063", + "icirc": "\xee", + "icirc;": "\xee", + "icy;": "\u0438", + "iecy;": "\u0435", + "iexcl": "\xa1", + "iexcl;": "\xa1", + "iff;": "\u21d4", + "ifr;": "\U0001d526", + "igrave": "\xec", + "igrave;": "\xec", + "ii;": "\u2148", + "iiiint;": "\u2a0c", + "iiint;": "\u222d", + "iinfin;": "\u29dc", + "iiota;": "\u2129", + "ijlig;": "\u0133", + "imacr;": "\u012b", + "image;": "\u2111", + "imagline;": "\u2110", + "imagpart;": "\u2111", + "imath;": "\u0131", + "imof;": "\u22b7", + "imped;": "\u01b5", + "in;": "\u2208", + "incare;": "\u2105", + "infin;": "\u221e", + "infintie;": "\u29dd", + "inodot;": "\u0131", + "int;": "\u222b", + "intcal;": "\u22ba", + "integers;": "\u2124", + "intercal;": "\u22ba", + "intlarhk;": "\u2a17", + "intprod;": "\u2a3c", + "iocy;": "\u0451", + "iogon;": "\u012f", + "iopf;": "\U0001d55a", + "iota;": "\u03b9", + "iprod;": "\u2a3c", + "iquest": "\xbf", + "iquest;": "\xbf", + "iscr;": "\U0001d4be", + "isin;": "\u2208", + "isinE;": "\u22f9", + "isindot;": "\u22f5", + "isins;": "\u22f4", + "isinsv;": "\u22f3", + "isinv;": "\u2208", + "it;": "\u2062", + "itilde;": "\u0129", + "iukcy;": "\u0456", + "iuml": "\xef", + "iuml;": "\xef", + "jcirc;": "\u0135", + "jcy;": "\u0439", + "jfr;": "\U0001d527", + "jmath;": "\u0237", + "jopf;": "\U0001d55b", + "jscr;": "\U0001d4bf", + "jsercy;": "\u0458", + "jukcy;": "\u0454", + "kappa;": "\u03ba", + "kappav;": "\u03f0", + "kcedil;": "\u0137", + "kcy;": "\u043a", + "kfr;": "\U0001d528", + "kgreen;": "\u0138", + "khcy;": "\u0445", + "kjcy;": "\u045c", + "kopf;": "\U0001d55c", + "kscr;": "\U0001d4c0", + "lAarr;": "\u21da", + "lArr;": "\u21d0", + "lAtail;": "\u291b", + "lBarr;": "\u290e", + "lE;": "\u2266", + "lEg;": "\u2a8b", + "lHar;": "\u2962", + "lacute;": "\u013a", + "laemptyv;": "\u29b4", + "lagran;": "\u2112", + "lambda;": "\u03bb", + "lang;": "\u27e8", + "langd;": "\u2991", + "langle;": "\u27e8", + "lap;": "\u2a85", + "laquo": "\xab", + "laquo;": "\xab", + "larr;": "\u2190", + "larrb;": "\u21e4", + "larrbfs;": "\u291f", + "larrfs;": "\u291d", + "larrhk;": "\u21a9", + "larrlp;": "\u21ab", + "larrpl;": "\u2939", + "larrsim;": "\u2973", + "larrtl;": "\u21a2", + "lat;": "\u2aab", + "latail;": "\u2919", + "late;": "\u2aad", + "lates;": "\u2aad\ufe00", + "lbarr;": "\u290c", + "lbbrk;": "\u2772", + "lbrace;": "{", + "lbrack;": "[", + "lbrke;": "\u298b", + "lbrksld;": "\u298f", + "lbrkslu;": "\u298d", + "lcaron;": "\u013e", + "lcedil;": "\u013c", + "lceil;": "\u2308", + "lcub;": "{", + "lcy;": "\u043b", + "ldca;": "\u2936", + "ldquo;": "\u201c", + "ldquor;": "\u201e", + "ldrdhar;": "\u2967", + "ldrushar;": "\u294b", + "ldsh;": "\u21b2", + "le;": "\u2264", + "leftarrow;": "\u2190", + "leftarrowtail;": "\u21a2", + "leftharpoondown;": "\u21bd", + "leftharpoonup;": "\u21bc", + "leftleftarrows;": "\u21c7", + "leftrightarrow;": "\u2194", + "leftrightarrows;": "\u21c6", + "leftrightharpoons;": "\u21cb", + "leftrightsquigarrow;": "\u21ad", + "leftthreetimes;": "\u22cb", + "leg;": "\u22da", + "leq;": "\u2264", + "leqq;": "\u2266", + "leqslant;": "\u2a7d", + "les;": "\u2a7d", + "lescc;": "\u2aa8", + "lesdot;": "\u2a7f", + "lesdoto;": "\u2a81", + "lesdotor;": "\u2a83", + "lesg;": "\u22da\ufe00", + "lesges;": "\u2a93", + "lessapprox;": "\u2a85", + "lessdot;": "\u22d6", + "lesseqgtr;": "\u22da", + "lesseqqgtr;": "\u2a8b", + "lessgtr;": "\u2276", + "lesssim;": "\u2272", + "lfisht;": "\u297c", + "lfloor;": "\u230a", + "lfr;": "\U0001d529", + "lg;": "\u2276", + "lgE;": "\u2a91", + "lhard;": "\u21bd", + "lharu;": "\u21bc", + "lharul;": "\u296a", + "lhblk;": "\u2584", + "ljcy;": "\u0459", + "ll;": "\u226a", + "llarr;": "\u21c7", + "llcorner;": "\u231e", + "llhard;": "\u296b", + "lltri;": "\u25fa", + "lmidot;": "\u0140", + "lmoust;": "\u23b0", + "lmoustache;": "\u23b0", + "lnE;": "\u2268", + "lnap;": "\u2a89", + "lnapprox;": "\u2a89", + "lne;": "\u2a87", + "lneq;": "\u2a87", + "lneqq;": "\u2268", + "lnsim;": "\u22e6", + "loang;": "\u27ec", + "loarr;": "\u21fd", + "lobrk;": "\u27e6", + "longleftarrow;": "\u27f5", + "longleftrightarrow;": "\u27f7", + "longmapsto;": "\u27fc", + "longrightarrow;": "\u27f6", + "looparrowleft;": "\u21ab", + "looparrowright;": "\u21ac", + "lopar;": "\u2985", + "lopf;": "\U0001d55d", + "loplus;": "\u2a2d", + "lotimes;": "\u2a34", + "lowast;": "\u2217", + "lowbar;": "_", + "loz;": "\u25ca", + "lozenge;": "\u25ca", + "lozf;": "\u29eb", + "lpar;": "(", + "lparlt;": "\u2993", + "lrarr;": "\u21c6", + "lrcorner;": "\u231f", + "lrhar;": "\u21cb", + "lrhard;": "\u296d", + "lrm;": "\u200e", + "lrtri;": "\u22bf", + "lsaquo;": "\u2039", + "lscr;": "\U0001d4c1", + "lsh;": "\u21b0", + "lsim;": "\u2272", + "lsime;": "\u2a8d", + "lsimg;": "\u2a8f", + "lsqb;": "[", + "lsquo;": "\u2018", + "lsquor;": "\u201a", + "lstrok;": "\u0142", + "lt": "<", + "lt;": "<", + "ltcc;": "\u2aa6", + "ltcir;": "\u2a79", + "ltdot;": "\u22d6", + "lthree;": "\u22cb", + "ltimes;": "\u22c9", + "ltlarr;": "\u2976", + "ltquest;": "\u2a7b", + "ltrPar;": "\u2996", + "ltri;": "\u25c3", + "ltrie;": "\u22b4", + "ltrif;": "\u25c2", + "lurdshar;": "\u294a", + "luruhar;": "\u2966", + "lvertneqq;": "\u2268\ufe00", + "lvnE;": "\u2268\ufe00", + "mDDot;": "\u223a", + "macr": "\xaf", + "macr;": "\xaf", + "male;": "\u2642", + "malt;": "\u2720", + "maltese;": "\u2720", + "map;": "\u21a6", + "mapsto;": "\u21a6", + "mapstodown;": "\u21a7", + "mapstoleft;": "\u21a4", + "mapstoup;": "\u21a5", + "marker;": "\u25ae", + "mcomma;": "\u2a29", + "mcy;": "\u043c", + "mdash;": "\u2014", + "measuredangle;": "\u2221", + "mfr;": "\U0001d52a", + "mho;": "\u2127", + "micro": "\xb5", + "micro;": "\xb5", + "mid;": "\u2223", + "midast;": "*", + "midcir;": "\u2af0", + "middot": "\xb7", + "middot;": "\xb7", + "minus;": "\u2212", + "minusb;": "\u229f", + "minusd;": "\u2238", + "minusdu;": "\u2a2a", + "mlcp;": "\u2adb", + "mldr;": "\u2026", + "mnplus;": "\u2213", + "models;": "\u22a7", + "mopf;": "\U0001d55e", + "mp;": "\u2213", + "mscr;": "\U0001d4c2", + "mstpos;": "\u223e", + "mu;": "\u03bc", + "multimap;": "\u22b8", + "mumap;": "\u22b8", + "nGg;": "\u22d9\u0338", + "nGt;": "\u226b\u20d2", + "nGtv;": "\u226b\u0338", + "nLeftarrow;": "\u21cd", + "nLeftrightarrow;": "\u21ce", + "nLl;": "\u22d8\u0338", + "nLt;": "\u226a\u20d2", + "nLtv;": "\u226a\u0338", + "nRightarrow;": "\u21cf", + "nVDash;": "\u22af", + "nVdash;": "\u22ae", + "nabla;": "\u2207", + "nacute;": "\u0144", + "nang;": "\u2220\u20d2", + "nap;": "\u2249", + "napE;": "\u2a70\u0338", + "napid;": "\u224b\u0338", + "napos;": "\u0149", + "napprox;": "\u2249", + "natur;": "\u266e", + "natural;": "\u266e", + "naturals;": "\u2115", + "nbsp": "\xa0", + "nbsp;": "\xa0", + "nbump;": "\u224e\u0338", + "nbumpe;": "\u224f\u0338", + "ncap;": "\u2a43", + "ncaron;": "\u0148", + "ncedil;": "\u0146", + "ncong;": "\u2247", + "ncongdot;": "\u2a6d\u0338", + "ncup;": "\u2a42", + "ncy;": "\u043d", + "ndash;": "\u2013", + "ne;": "\u2260", + "neArr;": "\u21d7", + "nearhk;": "\u2924", + "nearr;": "\u2197", + "nearrow;": "\u2197", + "nedot;": "\u2250\u0338", + "nequiv;": "\u2262", + "nesear;": "\u2928", + "nesim;": "\u2242\u0338", + "nexist;": "\u2204", + "nexists;": "\u2204", + "nfr;": "\U0001d52b", + "ngE;": "\u2267\u0338", + "nge;": "\u2271", + "ngeq;": "\u2271", + "ngeqq;": "\u2267\u0338", + "ngeqslant;": "\u2a7e\u0338", + "nges;": "\u2a7e\u0338", + "ngsim;": "\u2275", + "ngt;": "\u226f", + "ngtr;": "\u226f", + "nhArr;": "\u21ce", + "nharr;": "\u21ae", + "nhpar;": "\u2af2", + "ni;": "\u220b", + "nis;": "\u22fc", + "nisd;": "\u22fa", + "niv;": "\u220b", + "njcy;": "\u045a", + "nlArr;": "\u21cd", + "nlE;": "\u2266\u0338", + "nlarr;": "\u219a", + "nldr;": "\u2025", + "nle;": "\u2270", + "nleftarrow;": "\u219a", + "nleftrightarrow;": "\u21ae", + "nleq;": "\u2270", + "nleqq;": "\u2266\u0338", + "nleqslant;": "\u2a7d\u0338", + "nles;": "\u2a7d\u0338", + "nless;": "\u226e", + "nlsim;": "\u2274", + "nlt;": "\u226e", + "nltri;": "\u22ea", + "nltrie;": "\u22ec", + "nmid;": "\u2224", + "nopf;": "\U0001d55f", + "not": "\xac", + "not;": "\xac", + "notin;": "\u2209", + "notinE;": "\u22f9\u0338", + "notindot;": "\u22f5\u0338", + "notinva;": "\u2209", + "notinvb;": "\u22f7", + "notinvc;": "\u22f6", + "notni;": "\u220c", + "notniva;": "\u220c", + "notnivb;": "\u22fe", + "notnivc;": "\u22fd", + "npar;": "\u2226", + "nparallel;": "\u2226", + "nparsl;": "\u2afd\u20e5", + "npart;": "\u2202\u0338", + "npolint;": "\u2a14", + "npr;": "\u2280", + "nprcue;": "\u22e0", + "npre;": "\u2aaf\u0338", + "nprec;": "\u2280", + "npreceq;": "\u2aaf\u0338", + "nrArr;": "\u21cf", + "nrarr;": "\u219b", + "nrarrc;": "\u2933\u0338", + "nrarrw;": "\u219d\u0338", + "nrightarrow;": "\u219b", + "nrtri;": "\u22eb", + "nrtrie;": "\u22ed", + "nsc;": "\u2281", + "nsccue;": "\u22e1", + "nsce;": "\u2ab0\u0338", + "nscr;": "\U0001d4c3", + "nshortmid;": "\u2224", + "nshortparallel;": "\u2226", + "nsim;": "\u2241", + "nsime;": "\u2244", + "nsimeq;": "\u2244", + "nsmid;": "\u2224", + "nspar;": "\u2226", + "nsqsube;": "\u22e2", + "nsqsupe;": "\u22e3", + "nsub;": "\u2284", + "nsubE;": "\u2ac5\u0338", + "nsube;": "\u2288", + "nsubset;": "\u2282\u20d2", + "nsubseteq;": "\u2288", + "nsubseteqq;": "\u2ac5\u0338", + "nsucc;": "\u2281", + "nsucceq;": "\u2ab0\u0338", + "nsup;": "\u2285", + "nsupE;": "\u2ac6\u0338", + "nsupe;": "\u2289", + "nsupset;": "\u2283\u20d2", + "nsupseteq;": "\u2289", + "nsupseteqq;": "\u2ac6\u0338", + "ntgl;": "\u2279", + "ntilde": "\xf1", + "ntilde;": "\xf1", + "ntlg;": "\u2278", + "ntriangleleft;": "\u22ea", + "ntrianglelefteq;": "\u22ec", + "ntriangleright;": "\u22eb", + "ntrianglerighteq;": "\u22ed", + "nu;": "\u03bd", + "num;": "#", + "numero;": "\u2116", + "numsp;": "\u2007", + "nvDash;": "\u22ad", + "nvHarr;": "\u2904", + "nvap;": "\u224d\u20d2", + "nvdash;": "\u22ac", + "nvge;": "\u2265\u20d2", + "nvgt;": ">\u20d2", + "nvinfin;": "\u29de", + "nvlArr;": "\u2902", + "nvle;": "\u2264\u20d2", + "nvlt;": "<\u20d2", + "nvltrie;": "\u22b4\u20d2", + "nvrArr;": "\u2903", + "nvrtrie;": "\u22b5\u20d2", + "nvsim;": "\u223c\u20d2", + "nwArr;": "\u21d6", + "nwarhk;": "\u2923", + "nwarr;": "\u2196", + "nwarrow;": "\u2196", + "nwnear;": "\u2927", + "oS;": "\u24c8", + "oacute": "\xf3", + "oacute;": "\xf3", + "oast;": "\u229b", + "ocir;": "\u229a", + "ocirc": "\xf4", + "ocirc;": "\xf4", + "ocy;": "\u043e", + "odash;": "\u229d", + "odblac;": "\u0151", + "odiv;": "\u2a38", + "odot;": "\u2299", + "odsold;": "\u29bc", + "oelig;": "\u0153", + "ofcir;": "\u29bf", + "ofr;": "\U0001d52c", + "ogon;": "\u02db", + "ograve": "\xf2", + "ograve;": "\xf2", + "ogt;": "\u29c1", + "ohbar;": "\u29b5", + "ohm;": "\u03a9", + "oint;": "\u222e", + "olarr;": "\u21ba", + "olcir;": "\u29be", + "olcross;": "\u29bb", + "oline;": "\u203e", + "olt;": "\u29c0", + "omacr;": "\u014d", + "omega;": "\u03c9", + "omicron;": "\u03bf", + "omid;": "\u29b6", + "ominus;": "\u2296", + "oopf;": "\U0001d560", + "opar;": "\u29b7", + "operp;": "\u29b9", + "oplus;": "\u2295", + "or;": "\u2228", + "orarr;": "\u21bb", + "ord;": "\u2a5d", + "order;": "\u2134", + "orderof;": "\u2134", + "ordf": "\xaa", + "ordf;": "\xaa", + "ordm": "\xba", + "ordm;": "\xba", + "origof;": "\u22b6", + "oror;": "\u2a56", + "orslope;": "\u2a57", + "orv;": "\u2a5b", + "oscr;": "\u2134", + "oslash": "\xf8", + "oslash;": "\xf8", + "osol;": "\u2298", + "otilde": "\xf5", + "otilde;": "\xf5", + "otimes;": "\u2297", + "otimesas;": "\u2a36", + "ouml": "\xf6", + "ouml;": "\xf6", + "ovbar;": "\u233d", + "par;": "\u2225", + "para": "\xb6", + "para;": "\xb6", + "parallel;": "\u2225", + "parsim;": "\u2af3", + "parsl;": "\u2afd", + "part;": "\u2202", + "pcy;": "\u043f", + "percnt;": "%", + "period;": ".", + "permil;": "\u2030", + "perp;": "\u22a5", + "pertenk;": "\u2031", + "pfr;": "\U0001d52d", + "phi;": "\u03c6", + "phiv;": "\u03d5", + "phmmat;": "\u2133", + "phone;": "\u260e", + "pi;": "\u03c0", + "pitchfork;": "\u22d4", + "piv;": "\u03d6", + "planck;": "\u210f", + "planckh;": "\u210e", + "plankv;": "\u210f", + "plus;": "+", + "plusacir;": "\u2a23", + "plusb;": "\u229e", + "pluscir;": "\u2a22", + "plusdo;": "\u2214", + "plusdu;": "\u2a25", + "pluse;": "\u2a72", + "plusmn": "\xb1", + "plusmn;": "\xb1", + "plussim;": "\u2a26", + "plustwo;": "\u2a27", + "pm;": "\xb1", + "pointint;": "\u2a15", + "popf;": "\U0001d561", + "pound": "\xa3", + "pound;": "\xa3", + "pr;": "\u227a", + "prE;": "\u2ab3", + "prap;": "\u2ab7", + "prcue;": "\u227c", + "pre;": "\u2aaf", + "prec;": "\u227a", + "precapprox;": "\u2ab7", + "preccurlyeq;": "\u227c", + "preceq;": "\u2aaf", + "precnapprox;": "\u2ab9", + "precneqq;": "\u2ab5", + "precnsim;": "\u22e8", + "precsim;": "\u227e", + "prime;": "\u2032", + "primes;": "\u2119", + "prnE;": "\u2ab5", + "prnap;": "\u2ab9", + "prnsim;": "\u22e8", + "prod;": "\u220f", + "profalar;": "\u232e", + "profline;": "\u2312", + "profsurf;": "\u2313", + "prop;": "\u221d", + "propto;": "\u221d", + "prsim;": "\u227e", + "prurel;": "\u22b0", + "pscr;": "\U0001d4c5", + "psi;": "\u03c8", + "puncsp;": "\u2008", + "qfr;": "\U0001d52e", + "qint;": "\u2a0c", + "qopf;": "\U0001d562", + "qprime;": "\u2057", + "qscr;": "\U0001d4c6", + "quaternions;": "\u210d", + "quatint;": "\u2a16", + "quest;": "?", + "questeq;": "\u225f", + "quot": "\"", + "quot;": "\"", + "rAarr;": "\u21db", + "rArr;": "\u21d2", + "rAtail;": "\u291c", + "rBarr;": "\u290f", + "rHar;": "\u2964", + "race;": "\u223d\u0331", + "racute;": "\u0155", + "radic;": "\u221a", + "raemptyv;": "\u29b3", + "rang;": "\u27e9", + "rangd;": "\u2992", + "range;": "\u29a5", + "rangle;": "\u27e9", + "raquo": "\xbb", + "raquo;": "\xbb", + "rarr;": "\u2192", + "rarrap;": "\u2975", + "rarrb;": "\u21e5", + "rarrbfs;": "\u2920", + "rarrc;": "\u2933", + "rarrfs;": "\u291e", + "rarrhk;": "\u21aa", + "rarrlp;": "\u21ac", + "rarrpl;": "\u2945", + "rarrsim;": "\u2974", + "rarrtl;": "\u21a3", + "rarrw;": "\u219d", + "ratail;": "\u291a", + "ratio;": "\u2236", + "rationals;": "\u211a", + "rbarr;": "\u290d", + "rbbrk;": "\u2773", + "rbrace;": "}", + "rbrack;": "]", + "rbrke;": "\u298c", + "rbrksld;": "\u298e", + "rbrkslu;": "\u2990", + "rcaron;": "\u0159", + "rcedil;": "\u0157", + "rceil;": "\u2309", + "rcub;": "}", + "rcy;": "\u0440", + "rdca;": "\u2937", + "rdldhar;": "\u2969", + "rdquo;": "\u201d", + "rdquor;": "\u201d", + "rdsh;": "\u21b3", + "real;": "\u211c", + "realine;": "\u211b", + "realpart;": "\u211c", + "reals;": "\u211d", + "rect;": "\u25ad", + "reg": "\xae", + "reg;": "\xae", + "rfisht;": "\u297d", + "rfloor;": "\u230b", + "rfr;": "\U0001d52f", + "rhard;": "\u21c1", + "rharu;": "\u21c0", + "rharul;": "\u296c", + "rho;": "\u03c1", + "rhov;": "\u03f1", + "rightarrow;": "\u2192", + "rightarrowtail;": "\u21a3", + "rightharpoondown;": "\u21c1", + "rightharpoonup;": "\u21c0", + "rightleftarrows;": "\u21c4", + "rightleftharpoons;": "\u21cc", + "rightrightarrows;": "\u21c9", + "rightsquigarrow;": "\u219d", + "rightthreetimes;": "\u22cc", + "ring;": "\u02da", + "risingdotseq;": "\u2253", + "rlarr;": "\u21c4", + "rlhar;": "\u21cc", + "rlm;": "\u200f", + "rmoust;": "\u23b1", + "rmoustache;": "\u23b1", + "rnmid;": "\u2aee", + "roang;": "\u27ed", + "roarr;": "\u21fe", + "robrk;": "\u27e7", + "ropar;": "\u2986", + "ropf;": "\U0001d563", + "roplus;": "\u2a2e", + "rotimes;": "\u2a35", + "rpar;": ")", + "rpargt;": "\u2994", + "rppolint;": "\u2a12", + "rrarr;": "\u21c9", + "rsaquo;": "\u203a", + "rscr;": "\U0001d4c7", + "rsh;": "\u21b1", + "rsqb;": "]", + "rsquo;": "\u2019", + "rsquor;": "\u2019", + "rthree;": "\u22cc", + "rtimes;": "\u22ca", + "rtri;": "\u25b9", + "rtrie;": "\u22b5", + "rtrif;": "\u25b8", + "rtriltri;": "\u29ce", + "ruluhar;": "\u2968", + "rx;": "\u211e", + "sacute;": "\u015b", + "sbquo;": "\u201a", + "sc;": "\u227b", + "scE;": "\u2ab4", + "scap;": "\u2ab8", + "scaron;": "\u0161", + "sccue;": "\u227d", + "sce;": "\u2ab0", + "scedil;": "\u015f", + "scirc;": "\u015d", + "scnE;": "\u2ab6", + "scnap;": "\u2aba", + "scnsim;": "\u22e9", + "scpolint;": "\u2a13", + "scsim;": "\u227f", + "scy;": "\u0441", + "sdot;": "\u22c5", + "sdotb;": "\u22a1", + "sdote;": "\u2a66", + "seArr;": "\u21d8", + "searhk;": "\u2925", + "searr;": "\u2198", + "searrow;": "\u2198", + "sect": "\xa7", + "sect;": "\xa7", + "semi;": ";", + "seswar;": "\u2929", + "setminus;": "\u2216", + "setmn;": "\u2216", + "sext;": "\u2736", + "sfr;": "\U0001d530", + "sfrown;": "\u2322", + "sharp;": "\u266f", + "shchcy;": "\u0449", + "shcy;": "\u0448", + "shortmid;": "\u2223", + "shortparallel;": "\u2225", + "shy": "\xad", + "shy;": "\xad", + "sigma;": "\u03c3", + "sigmaf;": "\u03c2", + "sigmav;": "\u03c2", + "sim;": "\u223c", + "simdot;": "\u2a6a", + "sime;": "\u2243", + "simeq;": "\u2243", + "simg;": "\u2a9e", + "simgE;": "\u2aa0", + "siml;": "\u2a9d", + "simlE;": "\u2a9f", + "simne;": "\u2246", + "simplus;": "\u2a24", + "simrarr;": "\u2972", + "slarr;": "\u2190", + "smallsetminus;": "\u2216", + "smashp;": "\u2a33", + "smeparsl;": "\u29e4", + "smid;": "\u2223", + "smile;": "\u2323", + "smt;": "\u2aaa", + "smte;": "\u2aac", + "smtes;": "\u2aac\ufe00", + "softcy;": "\u044c", + "sol;": "/", + "solb;": "\u29c4", + "solbar;": "\u233f", + "sopf;": "\U0001d564", + "spades;": "\u2660", + "spadesuit;": "\u2660", + "spar;": "\u2225", + "sqcap;": "\u2293", + "sqcaps;": "\u2293\ufe00", + "sqcup;": "\u2294", + "sqcups;": "\u2294\ufe00", + "sqsub;": "\u228f", + "sqsube;": "\u2291", + "sqsubset;": "\u228f", + "sqsubseteq;": "\u2291", + "sqsup;": "\u2290", + "sqsupe;": "\u2292", + "sqsupset;": "\u2290", + "sqsupseteq;": "\u2292", + "squ;": "\u25a1", + "square;": "\u25a1", + "squarf;": "\u25aa", + "squf;": "\u25aa", + "srarr;": "\u2192", + "sscr;": "\U0001d4c8", + "ssetmn;": "\u2216", + "ssmile;": "\u2323", + "sstarf;": "\u22c6", + "star;": "\u2606", + "starf;": "\u2605", + "straightepsilon;": "\u03f5", + "straightphi;": "\u03d5", + "strns;": "\xaf", + "sub;": "\u2282", + "subE;": "\u2ac5", + "subdot;": "\u2abd", + "sube;": "\u2286", + "subedot;": "\u2ac3", + "submult;": "\u2ac1", + "subnE;": "\u2acb", + "subne;": "\u228a", + "subplus;": "\u2abf", + "subrarr;": "\u2979", + "subset;": "\u2282", + "subseteq;": "\u2286", + "subseteqq;": "\u2ac5", + "subsetneq;": "\u228a", + "subsetneqq;": "\u2acb", + "subsim;": "\u2ac7", + "subsub;": "\u2ad5", + "subsup;": "\u2ad3", + "succ;": "\u227b", + "succapprox;": "\u2ab8", + "succcurlyeq;": "\u227d", + "succeq;": "\u2ab0", + "succnapprox;": "\u2aba", + "succneqq;": "\u2ab6", + "succnsim;": "\u22e9", + "succsim;": "\u227f", + "sum;": "\u2211", + "sung;": "\u266a", + "sup1": "\xb9", + "sup1;": "\xb9", + "sup2": "\xb2", + "sup2;": "\xb2", + "sup3": "\xb3", + "sup3;": "\xb3", + "sup;": "\u2283", + "supE;": "\u2ac6", + "supdot;": "\u2abe", + "supdsub;": "\u2ad8", + "supe;": "\u2287", + "supedot;": "\u2ac4", + "suphsol;": "\u27c9", + "suphsub;": "\u2ad7", + "suplarr;": "\u297b", + "supmult;": "\u2ac2", + "supnE;": "\u2acc", + "supne;": "\u228b", + "supplus;": "\u2ac0", + "supset;": "\u2283", + "supseteq;": "\u2287", + "supseteqq;": "\u2ac6", + "supsetneq;": "\u228b", + "supsetneqq;": "\u2acc", + "supsim;": "\u2ac8", + "supsub;": "\u2ad4", + "supsup;": "\u2ad6", + "swArr;": "\u21d9", + "swarhk;": "\u2926", + "swarr;": "\u2199", + "swarrow;": "\u2199", + "swnwar;": "\u292a", + "szlig": "\xdf", + "szlig;": "\xdf", + "target;": "\u2316", + "tau;": "\u03c4", + "tbrk;": "\u23b4", + "tcaron;": "\u0165", + "tcedil;": "\u0163", + "tcy;": "\u0442", + "tdot;": "\u20db", + "telrec;": "\u2315", + "tfr;": "\U0001d531", + "there4;": "\u2234", + "therefore;": "\u2234", + "theta;": "\u03b8", + "thetasym;": "\u03d1", + "thetav;": "\u03d1", + "thickapprox;": "\u2248", + "thicksim;": "\u223c", + "thinsp;": "\u2009", + "thkap;": "\u2248", + "thksim;": "\u223c", + "thorn": "\xfe", + "thorn;": "\xfe", + "tilde;": "\u02dc", + "times": "\xd7", + "times;": "\xd7", + "timesb;": "\u22a0", + "timesbar;": "\u2a31", + "timesd;": "\u2a30", + "tint;": "\u222d", + "toea;": "\u2928", + "top;": "\u22a4", + "topbot;": "\u2336", + "topcir;": "\u2af1", + "topf;": "\U0001d565", + "topfork;": "\u2ada", + "tosa;": "\u2929", + "tprime;": "\u2034", + "trade;": "\u2122", + "triangle;": "\u25b5", + "triangledown;": "\u25bf", + "triangleleft;": "\u25c3", + "trianglelefteq;": "\u22b4", + "triangleq;": "\u225c", + "triangleright;": "\u25b9", + "trianglerighteq;": "\u22b5", + "tridot;": "\u25ec", + "trie;": "\u225c", + "triminus;": "\u2a3a", + "triplus;": "\u2a39", + "trisb;": "\u29cd", + "tritime;": "\u2a3b", + "trpezium;": "\u23e2", + "tscr;": "\U0001d4c9", + "tscy;": "\u0446", + "tshcy;": "\u045b", + "tstrok;": "\u0167", + "twixt;": "\u226c", + "twoheadleftarrow;": "\u219e", + "twoheadrightarrow;": "\u21a0", + "uArr;": "\u21d1", + "uHar;": "\u2963", + "uacute": "\xfa", + "uacute;": "\xfa", + "uarr;": "\u2191", + "ubrcy;": "\u045e", + "ubreve;": "\u016d", + "ucirc": "\xfb", + "ucirc;": "\xfb", + "ucy;": "\u0443", + "udarr;": "\u21c5", + "udblac;": "\u0171", + "udhar;": "\u296e", + "ufisht;": "\u297e", + "ufr;": "\U0001d532", + "ugrave": "\xf9", + "ugrave;": "\xf9", + "uharl;": "\u21bf", + "uharr;": "\u21be", + "uhblk;": "\u2580", + "ulcorn;": "\u231c", + "ulcorner;": "\u231c", + "ulcrop;": "\u230f", + "ultri;": "\u25f8", + "umacr;": "\u016b", + "uml": "\xa8", + "uml;": "\xa8", + "uogon;": "\u0173", + "uopf;": "\U0001d566", + "uparrow;": "\u2191", + "updownarrow;": "\u2195", + "upharpoonleft;": "\u21bf", + "upharpoonright;": "\u21be", + "uplus;": "\u228e", + "upsi;": "\u03c5", + "upsih;": "\u03d2", + "upsilon;": "\u03c5", + "upuparrows;": "\u21c8", + "urcorn;": "\u231d", + "urcorner;": "\u231d", + "urcrop;": "\u230e", + "uring;": "\u016f", + "urtri;": "\u25f9", + "uscr;": "\U0001d4ca", + "utdot;": "\u22f0", + "utilde;": "\u0169", + "utri;": "\u25b5", + "utrif;": "\u25b4", + "uuarr;": "\u21c8", + "uuml": "\xfc", + "uuml;": "\xfc", + "uwangle;": "\u29a7", + "vArr;": "\u21d5", + "vBar;": "\u2ae8", + "vBarv;": "\u2ae9", + "vDash;": "\u22a8", + "vangrt;": "\u299c", + "varepsilon;": "\u03f5", + "varkappa;": "\u03f0", + "varnothing;": "\u2205", + "varphi;": "\u03d5", + "varpi;": "\u03d6", + "varpropto;": "\u221d", + "varr;": "\u2195", + "varrho;": "\u03f1", + "varsigma;": "\u03c2", + "varsubsetneq;": "\u228a\ufe00", + "varsubsetneqq;": "\u2acb\ufe00", + "varsupsetneq;": "\u228b\ufe00", + "varsupsetneqq;": "\u2acc\ufe00", + "vartheta;": "\u03d1", + "vartriangleleft;": "\u22b2", + "vartriangleright;": "\u22b3", + "vcy;": "\u0432", + "vdash;": "\u22a2", + "vee;": "\u2228", + "veebar;": "\u22bb", + "veeeq;": "\u225a", + "vellip;": "\u22ee", + "verbar;": "|", + "vert;": "|", + "vfr;": "\U0001d533", + "vltri;": "\u22b2", + "vnsub;": "\u2282\u20d2", + "vnsup;": "\u2283\u20d2", + "vopf;": "\U0001d567", + "vprop;": "\u221d", + "vrtri;": "\u22b3", + "vscr;": "\U0001d4cb", + "vsubnE;": "\u2acb\ufe00", + "vsubne;": "\u228a\ufe00", + "vsupnE;": "\u2acc\ufe00", + "vsupne;": "\u228b\ufe00", + "vzigzag;": "\u299a", + "wcirc;": "\u0175", + "wedbar;": "\u2a5f", + "wedge;": "\u2227", + "wedgeq;": "\u2259", + "weierp;": "\u2118", + "wfr;": "\U0001d534", + "wopf;": "\U0001d568", + "wp;": "\u2118", + "wr;": "\u2240", + "wreath;": "\u2240", + "wscr;": "\U0001d4cc", + "xcap;": "\u22c2", + "xcirc;": "\u25ef", + "xcup;": "\u22c3", + "xdtri;": "\u25bd", + "xfr;": "\U0001d535", + "xhArr;": "\u27fa", + "xharr;": "\u27f7", + "xi;": "\u03be", + "xlArr;": "\u27f8", + "xlarr;": "\u27f5", + "xmap;": "\u27fc", + "xnis;": "\u22fb", + "xodot;": "\u2a00", + "xopf;": "\U0001d569", + "xoplus;": "\u2a01", + "xotime;": "\u2a02", + "xrArr;": "\u27f9", + "xrarr;": "\u27f6", + "xscr;": "\U0001d4cd", + "xsqcup;": "\u2a06", + "xuplus;": "\u2a04", + "xutri;": "\u25b3", + "xvee;": "\u22c1", + "xwedge;": "\u22c0", + "yacute": "\xfd", + "yacute;": "\xfd", + "yacy;": "\u044f", + "ycirc;": "\u0177", + "ycy;": "\u044b", + "yen": "\xa5", + "yen;": "\xa5", + "yfr;": "\U0001d536", + "yicy;": "\u0457", + "yopf;": "\U0001d56a", + "yscr;": "\U0001d4ce", + "yucy;": "\u044e", + "yuml": "\xff", + "yuml;": "\xff", + "zacute;": "\u017a", + "zcaron;": "\u017e", + "zcy;": "\u0437", + "zdot;": "\u017c", + "zeetrf;": "\u2128", + "zeta;": "\u03b6", + "zfr;": "\U0001d537", + "zhcy;": "\u0436", + "zigrarr;": "\u21dd", + "zopf;": "\U0001d56b", + "zscr;": "\U0001d4cf", + "zwj;": "\u200d", + "zwnj;": "\u200c", +} + +replacementCharacters = { + 0x0: "\uFFFD", + 0x0d: "\u000D", + 0x80: "\u20AC", + 0x81: "\u0081", + 0x82: "\u201A", + 0x83: "\u0192", + 0x84: "\u201E", + 0x85: "\u2026", + 0x86: "\u2020", + 0x87: "\u2021", + 0x88: "\u02C6", + 0x89: "\u2030", + 0x8A: "\u0160", + 0x8B: "\u2039", + 0x8C: "\u0152", + 0x8D: "\u008D", + 0x8E: "\u017D", + 0x8F: "\u008F", + 0x90: "\u0090", + 0x91: "\u2018", + 0x92: "\u2019", + 0x93: "\u201C", + 0x94: "\u201D", + 0x95: "\u2022", + 0x96: "\u2013", + 0x97: "\u2014", + 0x98: "\u02DC", + 0x99: "\u2122", + 0x9A: "\u0161", + 0x9B: "\u203A", + 0x9C: "\u0153", + 0x9D: "\u009D", + 0x9E: "\u017E", + 0x9F: "\u0178", +} + +tokenTypes = { + "Doctype": 0, + "Characters": 1, + "SpaceCharacters": 2, + "StartTag": 3, + "EndTag": 4, + "EmptyTag": 5, + "Comment": 6, + "ParseError": 7 +} + +tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], + tokenTypes["EmptyTag"]]) + + +prefixes = {v: k for k, v in namespaces.items()} +prefixes["http://www.w3.org/1998/Math/MathML"] = "math" + + +class DataLossWarning(UserWarning): + """Raised when the current tree is unable to represent the input data""" + pass + + +class _ReparseException(Exception): + pass diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__init__.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c238e3562af667716d8dd1a6473300753748d601 GIT binary patch literal 236 zcmYjLu?oU46imTEgno!a`UY_jL>!!5#5u(D*~Yd>OwwXM!_iOhmt6e>H(gBVpa*xy z9S`pDG@X!uTlL}5pHcpigs={DqaZoik!iBM6C3NFysxl#XdQ@>D_&@obJ%FnHmH0k zHNu(GU=%L~tsH98dvu-|t4}CA43VPhpq7UiORZ+v0-W?n4Hp$J(1DQ#ScfXt7E0gL e^LTuewU6_RNhLjFndu+nWux=9##Mav6-d50*+nw| literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4002d3de082ea6fd1683aee963acce8b26e0519e GIT binary patch literal 1358 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 literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a846ecc738e546d743373ba0819579e6b19fd6a GIT binary patch literal 2654 zcmZuzOK%)S5bmDG?(EBU9LI@6h(-}8SrmIE5GWXgK(T`oAzz$CXq0F?o^J1scOL8R zUdOi97aS2TNWOsJW=jYu5!2PQ@sjY&d1ni!sun4U>-&xozW_H3X{X2qqX?3IDG zSt)jsidRXhURCpD;A>tDXy>TTYsb{9eN0$|RUZ>p<>k%Bf$23^ZIv|ZFTfgUn)zg~ zF0;6=xF03GOsL#p(RL)GEX}9-X%uFR`*EbW2x8f!c}4LZ<*VHue?d_J&8_u7@)rg& zMqW*Wgv(wKa+%k+vxqInJmIOrtCM>FW+xCq2>N7BLc}osVJz^IZ@|QfM;Y-9Mm>|2 zpO6iT-aoE*7JB}ec=i)w5w8T^IC<$-6ob;e_9qcob5)1CD(mvpl}hj+aW^vIav?Gy zt5p}i+Z%4A+>Iz@(hbtxq3k+rUX5c{1d-&h@2;(VBqbLL!m*6E*VYz_`*%ZABP}MR zM?+o&MiE5^?I7u`o44nYtu_?>176u85K?&zS42Tzr)G_Bm0{T@WA z0UqRz??-8*d_O<;QWKCnmgMn9UMet(DY*3%GQ{`8IFQo!pOQbnY~6$K$yV42(x9EC zENCrdVPB_2E6jLnE0~96p6`I&sMzl9LTr@Z6WJyYRjXj zIS;AU4cc6`dQq?CZ}XI8qSaAJ{5Brm&`BZTVyYH;yJ8X`PQ#Eo(VQu|H(9*E!pPYW zvwHMZfUseNFysBiq4UJb6_%Kn;cSc3wBJ{v0$}e zwFed>eIi$sIk1#tY`FJ%aZh{=dP=N3 zN4kdi0dAJJ39Ke_x`zA_&!lzWDkH9-#V-T0>A*~nDg$d^Zx}^tqV}WeF&F_n|@CHAfMhLiHix?rrYM|xsJA~C)V_-*g zmK;!s*A50blXV7;>Tm9`MqioEmLqmQy${eJ% zE`v?aLDSIEXJgW5L+RO}6udf-4n0iogQXdb!~mJj-f`cw|L3&)ik4$uj`^O>LUgq* zHp}S197T6v+$WT5O)&xm@cc?{VFQ77;jF4asJlTscb1c$+C{2z(n4|zNl}MiUv9BL z1-XU4Y%vEL;sWlLmQx0IZtX4HhhF3IqS-*D!%oJT8%Vb}Q54Wc_w{Q~^qMj^Vadxk zvjj^H_N}qDG`WiX>vTh0K~rU@TZlS$dj0h{3U9OAk-Jjy1Sr6vBDL#Hu0j2pAWLa0YF5M{H?um~wkN zi2EF`!)u63xPR_snnmg=Os~R_F-(M7becM}3d5nZv`!~rOw$W=!a#ZgsEaU9!I&|M zQzQN|$n-6}Ur6y>mCov&i}W&efkJN9oHfP^v<^>`-ffn#Ibm*Kw0u9wSU*O(?)zK) zARbD}zR$AI_jT3R9^{Q-Zwh%FgW%N3v*4A`p5kL*#>o;)4z=uwntq^_<^=&L?7 zTVLQG9)UeyU@!3|q5mul`_F=mcJ#~X)YWgCSHeucsgh$4i4T=qn^}}<6JvNZGQ^WR W?-%ZUfQ?BavKgZeVV;6fHU9zd7`;FM literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5212a22ab3865ad515681ad8e71235444e06af04 GIT binary patch literal 2799 zcmZ`*&yUdc5@gZCLq)bSe~xx8E4$K zr^-FbjCwA+IU+O%K!VfemPnj);=G6a0jpJMwU?Dv0`V6Fq}}h8?Mbf`+^TZ<)%RY# zdiA|m9WN|&7@lu;eu;1Q8T&VV%s)OpuArHB&`BnF$|iisCt@fj-q7Pr3hAZ(q%~}D z_5qW=Y~5qBrCN7_zj(ty1{#Y@7bkz)8y&2R8cZ1a$VX`CVPoV^2{zzvt*P@ z6{Sh3bex(WIq7igZ^lNA$iVy~@NosryopY+A(w0@Bp-U@&OJ8tD@TSc8OS!i0~yK_ zc(-K-4xO~YjU>f72d{smCi$K+y}T&lMx6G_c+2$0I-m5)ZPhFD9hLP=sZ~4~kw?u# zaiK<@nLnbF_&nr?V#e;UYwWY$p*Mq5Q?6g0i5Z`HWA78j*^Ch0A>UoBXRpz$WYw%> zaE`BI*MZ5WdZhZk^^Ho$!xM#8dr3Yu7oH9;+Lm+gA=4+}z>}{B9~-63V6+`)@m8M6 zcyKKrO(!ZVA?F;TPs$dH0vninBt{gysK(6BjCO$i8d>TanQF?5GnbzKLH zB1(xUTEUk&gO2eP#EpK-{}sZ$c_Nqy|5%IHmD3X*kSJBMB(9JyEop}_yVo_53 zxD3@_rdHUzy=C43JAB?n zJ2+c&W1ftTpCb>Jn}-N7OAZ-~LhKm*(*QnZqvw4@wy$O>bRCE1o$e9l5kai@WwRpL&c?8C#vvXb zTX_DeF5zv+9kBN z(B4LS#|D&;K#6T2Q4prq%VL;2G%QADLGifku}Cb^uZH10;x?ley8nRXP(d=y3QF4Ql;Sy#I>ZD8(JqdzN}t~T99 zN7uWdE6`Q3PPh=h=j7}CHZ3)tmQ#(TG}R?d#Y$6^vP_erLq5y24KG(tzE6MJ45^$J GUGIO#a;Y}} literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a200ff85c8e32378b9fce70a65cbf29e058af447 GIT binary patch literal 16922 zcmeHOd7Kg$MBVuPy{dlG6B2{x z$3L8z->dI?_0He>z2EQmUcIqKM~e!6-Z1me)@nyl{(vIGKXw?um++AGm* z677>{zeKl7bU>mzBswV3oe~|A=q`!wmgpXd?v>~T61`BO!xG&m(GiJWB+*fcUM$fu ziKZmFU!n&jdQhT=Bzjn)mq=tvbX=loiB3p#QlgqfrzEl@s!P<6h)HBi)Rd?tQClKM zqSF%1NaRY?k;s$C7pRNXeMTZKQ6Nz$QBR^-iRL7lmuNwvNTQcY)R*Y2L@$%*5s4m^ z=;acdlIRB{db317DAA%sOA`H%L_aLiTO|4siGEa~ zACu^<5pT(T_{?c8PwXsWq1#Q_YR`2KEm2lkBJ1JK4L~PqUw4Kg-_DevbV- z`vvwM_KWPj?0xK)*!$TpvtMB!U>{^3V!z5h%s#??jr}_NDEk=uIQshe6cO=Zk*6MD$bWVWcH7p^Ie<`WW~G z=p?8HIt8*obx;GuKsKleYJu7y2XvZ!4B-s>1VR_o0eK)F)CHXZaZmsv4utH>2z#Je z&>UzUv;c}gF9r2MXF)FmJpy_Z^m5QEK#zeQ2fb2o>{URoW?w`2Eue1&eH-Z8K~J#1 zNBA1hcYwYV^jgsCK(7aVmypv85Xt3}pzj8K59oVA-v@dF==(u$1icCL1E4p9eh{<> zS_1tL=!ZdX0sRQ*M?pUZdMoH0=xv}M2fZEi6QFm1eiHOkpm&1a1tOXG8PLyy-VORW z(9eT@0Yvio9?&m>-V2%ly$|$Dp!b7*8T2cl4}d-h`Vi<>K_3Qv1Vr=yYoK2TeH29V z^zk8i_yp+~q$Tq38!KWfW#yAA-VI3(6B}&m0XfB^p+*f+9yWDPAq0K5y?sqEfu;V`0b?TKHCvKc57N=~xa;j}} zyRx?(hTUNIOC3LQT-Tbci!7+L=mZsX-0yU3kD>EcP_dBE zb*+Ytq;R5g6j=ki;@V-Q7gT0Ef3D(qm9TABEObR=LbE6;I$Ly_m9%}--1CB-6Iy7E zI$v4vd%WV=HVeR#r)bYMo{3jk^}u&~po;kBlfI8!`p64`tX4hONw7KO5 zN5-|-u!Dxxwc~uxOVGF)mwMar#N2|j*Pyoz<|U94KM zZq@7D%vlZYdkdX7XEDa@AV4~|Ekt?fG+aB*T7kpp(PNGutMxcjcUp0=?)r_HGd(}F zW3A3(quvWc-!qMd<;_|_oC8-$tYV{qdAE72H^6m-n1DFb@ZC6%pvC=O7kMEt7!>Bp z3au7*m?@C!1YxW(7H634#yMt(mg5G9HBl{dEZ1)#;&2LPk=3boCyb~L3+hR!5+BbrBfo=lb40tu@_plXJ%&VEY7-C-F8jGwUNYP&2?h66K6VBH`cKjdS=fnE^xa^gXaP=wY%0Oi)%V zIy~w=TZpv~GZmtfLQeG1=|DO{;v!zP0f~8lU|$%4pK4JdlqZ{wx@m>vSQrHl(G~ZChUG zcg&Fvn385kHjYl2Iv#VVT?`h9!5JP>G+c0Pyu>I`6GDtsM7ww?28`1k3^m)Ku@dHZ z0rsKgV)CS3#JUH41wPHTux305L$Tvh3J&_N&*RZFcAqciWGs!|&n+(y;+Ai?PWPzb zLBru#bf(ViCRQTDSj?OaZPX3ys6Y~0Z9?lrWeKaKilWiwc)%O6+A^~(ccI%hv+Wt1 zdvV!yJlo=TbBj4x8L>|B7!$$qI?h87En0igXa{=N@mkfiG&qbY)hRS{UEf`3VU6br zhziP7pXCm!HFYTe5WK)r(}U3OLYC&7uz?zghY$@X#Ne`_khe@Pu*bC{d`#%nBe7vM z8g@5CQdCZ0wb3ROOi5;el7avW3^B?}yrdUd%XL};Wh|Gng8RMDhfaZx45@{@6Zp}Lcpu?=j3@s~;^ZXj;3 zB@UF7;GNz^7SU=OJ_A*jCjhBSYHkY^Md>>1fheLm$(V$Z>A-kW@vH-N-lcpwtcP?+ ze1Fn&bHufqw8930qo?)Lq7THp z&j^O+U@TnU68$o;#ydj$m_^cRu&VG)sPR19A?ACoP&^rwKw?ycS8H2gH5vGFG#Okj z^?B3cXL=47vKgcJEE3HUCt(?$B-p5Atr1AqT9)@*p>klj8yL5!F0trIby;vIN!HV0 z@?)v|X40w2%6WrEw!7yrASA)`QDwQH2hWR(k>z3k$asogKopH<@S;>!dYuiuD}B*_t0Lg&fvk zisP&ge@&Q>Jo+bDLlMcBVj`2NLNN_4yD^gZS}y!;nhmp%On73NW5lqBvcjZCdH9t! zoPmWnJCCYo_?t0H@Dtit+aE|KEE**%k3}n$XTf(oliUDO() z1+5|K#aSYyF`-K}CWH%yZ!iio!+IQTz-VFHjjZQO1gbvi`jB#;FNg$E8x$K!y~2(o zlo#Hj<$*$sN=Yo46v{5-uR0J%R8F~5 z@FazoifBCAN?p(gurE#&9$a^@mlSd|()PI%RV_AKr5dAh5=SC(h$M4_wh*YKi-kcI zs^nwE1=zu(r0%K8xQN(PcObrT9?|>ZM+E1nKZ(y<#f%I`JC2B!(zjA+rU~~9R%MKJ zMLzSO`-AvHsTML(01FC%10kW#81ThX9~LHSFjJ<1L8Zb;FcGk5HEk)V<5gHI$hmL= zqBSygSRF)pmBqr`)>Jh|+Bz|X4&i&PNo>of%jl= zKTOtgtnox_g#Ceua}yH>1S^wx5QNBJMn&3AR0rBEDv9VaJEINB<~3}?YPZ@exVLfT zE6KuhG&!`_ouS1NjU}+kEErYJg=&vh4<*WZJYP;zX`-5Ds>cBC$uZ zfDN};dTYdH^&6yV?Q*i@1(nLB=VQRpXo{<-J!v1t91FIJZBW&wMGw17=2}d84nj*3 zAZnR)?72IH;xvnN@>X|n-X;`-A;u?4sA75N=Z6%%HlL`C`CZsYH^Lr-gbtx_I)y_h zokCt9gOME=pID_@V{IWB^8$@IE=IoJIpDFQ*sYB4&koE=qK4t(k#avry>Ik*;B)>t zB+uk%CAA->4(&`E1s<5PyAy@gx>?tKpZc}*(N;gkl#pozeK^8DfoQ~zCrB}4*w(_R?SGNLokYTaQ@RK{S0p{NSSD=N+UPQ%6#4_R#fVdBQfTpQ{` zFvI2_LCW}bIHARP2F@|yWt!PK4%cU5Ja8Us>UFG1gucrtq*Jv-4v-H{oX4+14JIAE z3NgU$7}^deXa>V^1fIEOuh9-1%hX|F7jTHgZQF~A*CdDOIAb#nZ1r8Ifz2dj#l)7f z12(`W`gHh98@^tL&rl=S)6Fi#*wEnvgUjDWNm<{8=Z4)H*(4mJ8QmVo4$#D|2XKg5wN9mdC5aU_q!TpXfdRL}`NHmCL+j&@sq7mhMw z9`YHX?ZP=XjXApxJ8$aqoi5G|0Y!->zDn>9qBS#PIUNiwSnwJcg+-^T0gl50(Im$X z5ILFwe;x9U8&Ltrhb?=u>$Rd{0#A4C)*!j%GzZCOwmV3!ce;rHPGUpic-CQjq$pOd z@N@EtIkr~rv#@UP0VnlWE3}QO?6tb^tnh6LqP=f&FQx6fVqM$!AKjbQQWGVPSFLJv zplaSwy>;Tm&dHV0@hjFR`{pfF=!gsU0$*6*Wr=Si*j@Q_igv|nw(sAKzdh+8*=@i> z9XYXcoL+)xUvSIKWxUKs(MWXf6b{Yl zJ6cc);VF3)QoL5D;x{V~-*f+chj&6&+jvYpc()J%q&ptj4P?YwM@4S`f(EW}${)fy zC<6+n5rj&p_Lax9MXj$cK}m#}MLo=h23^DKYkj4b>t_(=`#QoxKZ~%~HxQ2Wa|lcQ zJi^g_0b#jcM7XLyf^dwf7>j02p|OMLc!+UWmAiz?QIr#~8^}suXE{l>1YWMF zcF>he_kdc(^f=wiH`@q;+VYvYZ2I8TRAo6?Hb30VMg2$z{Y^HBec&9ON3Upbkoe!* z96kqKYJ?=8|LGCp^EkYp%S|P!rLP|D^5{}gq6U*U;eMMZzdv=VLUrOJ{J`cUf{YWF8ukb^C#dzfBY8e7Uu`R_w5?g2DySs;i7HMUXkQ^K zqe?@W(W(Uajsa!p`C*05v+1gUS2=ZRux{jF(JibPlzTFWaKCD|&`PxQBt`yo-4%TA;ODgJT9R(6RQ4>IaKxW#tu-dqopeGN@K@ zssL%f-wa_*HYBVIkoEhjEsKS)|3kGR?~n6N~m z;*FP?#dzJNm_J^E+hw~={$l8PzK6WPy%gL=!9EK1Q*b*42Pn9Mf`b&${{Wm*<3g<_ zzL(W%=aloWpL_s^DZwO6yJz96D6=N-#1}@PvnLzAJ$c5$uF0<2^OzPm$x!8!xQ7Gt zO?u%pc1dXzCsDJLxNbsG+|s#e;?_x={NMlxZasY&o9sH>$r|o6FrUjSJ(J0HHJFt5 zOD4Jt{1)`{Gw}X`O&FrGb4)8~hI;XDv?{SX69XY>pp^VUX<-q`=m$Fxgvx0Z)=*he z?^I4~It%N#m?7H;r1DZ^x>-4^^);r= zD*Pnk8N|_XrsJu@d}o>%)M;f_3GV6ZOzA27Sg5k>W@Y&eKZd~yM_@`B40}rP%Barx?j zCxmi@5=RG1K(HttqwkxkW00^gD_F=66Cd&Y1$-2awP=DLl^hkk6>NPW(yk%4(hg9` z^&K^kUpt&P8B!1@sW(xSSg zJf?!}@R?3)n-n2IU>Z$MU%w5khAipiXPwzD^60lyo_C+k!n4A}1=BB3UZIBBMT7ju zFb59|6ZDw6lv&I#DQAtou~>jdnOV}#8c{hcF2WasWab`HysLC299ej~3hBhZ)hK60 z+1r$tD4uqM($De3{oLtN5~6RTvnu~6)s8+PovE1K!(n-G6=GwkG2hPzUrpYhIo!`L zu4dUKtc25RQ0}lyS5B`*?{^G(4{j7i-{I>H_nq?Z1+UH&!HGOLp`2bPc=u?yo)vK6 zy$I37=u!Srl;6Nc`r7iF&B~HSZ-&!uQ&=f2@g@lBMhGbWT?jqIb1(U=;5eS-gN_~% zd(9qs)yGdM)ygB)4=hdxCw9h4kCxD1z5ji2X;|+@n{gX%I@mG29l`OFTaowp$?+3A z_fLU+C$0HTfU4;qKzP4((dSad?d53w*;(w7FE| zTr4mnz;y>)OeskzhZP$y46UjxnVY~Y3aca5EDl?JcnRcB;0Ok9%~AL&wZY9n+*5;} z!w=A4F4AeoutaX6#rH5?D$rRd^$0osoVai62W;Ge3ekh_ZVpw*~tWsTF4~8ov+e{6+LKU)dJiwq;we zCCY9Kc5e$JWAC>ejZ zI4ObE=CsM=WTIl7z6`_}`j(zsmNb4p^?)IWHC$B@MnavvScInV-X;;c8Z{}H?z~Eq zP6Dz?y+OT7BSX1K%fsSF8&g5J*lMhh1zK-JLKgl7AXG4OuViRnO|UtM#e zKRs8Sp6g$Cd^(t(JF)Y+{+1MN=}&JzKFzKt{W?89zT?_Nk}9Mmw;x9tBnLZoA3wQ! z&xsw|_W<{=rEJm0^er{LTVxhl#v^Ta<-EG*yjtbxj{=wBaXk1d5zuiZ$5%wU3nT83 zMYWULw;smv~(Bf?8w*A*nvmFTci^uf%{{A$3=?&NKIy*gqB!RX! zI(-{|^L@wbcRKxUqu&kt-TD5u{`Gx~W&ilK<0WpA(25&j_^gts@lztHiQ{glA$}aO z#KWGUbey^4W6+Sd?ILyDNY=vpXcXcenui;QxN*lnL@9K|iFWN5F9>p57id9{m#S!M zpg&1->YA>u10r3zN!O_p1Wrqu(=g)EbZ0?V08#z2^|*_&2>*=?_NU3W z-vW?V^^ArgUiAC*^6vcV{F?l({JQaN{4CXs!8CjHAr{q=%gn(sh0~drm^}VG$~1;M zGZTR|Pt*%>$6BbX#3HZ9_G2R5eoO?;`~;c5fRh#B)6_dsj>(s~0t_*}^J1qXY^+dx zF?a?+KL`&ByhI58Ai7a3KfAsi?mm3y-aeq zwy{1r=s7@71$uCg{S*Bmy!Pb3kRH+*ZCtydKuYs{GvvG|-tTt_jNgv`;eWY={Efoh z0T_7#Q&+GEB4|NMn$VIZtaK8GBE||gafKt?!Ylp62h0! zrF*p|0vX;TAoZRT5sL0BBD&I_^)H>IFV;lw6-m|wn~+ie8qOpm#}4>`u8X;mXxk!Jy}xyIbiN4 zS(2HvAczR>3vxlLebDhi=f#rZ6yBGJehn4|m<})oEQ3v=j0o}TF{vH^`x@9Tu&<4~ z;xFlnU9uO1l9Qnzkd?JPh0a^ooHY>91Y$Pd^CRn4yp+mA${$w(p~2+6BDq+@H#D-{ z+schR<%5x0L(3pdu?^DHe%R)CUr8<1Lf+Ordsm6H<@Lz5u9n524Isa*s`X5LEGxAR z`~6AO19-jpHU0rO9cNQs@uRvDJbqSZ^R|`Ztd{W!--g$=JZ+@PS8#I%bv9|E>Y2<; zyb`x%wTO%S0L5utJ=*yy*5L3q#Cgn*q>h`siPMFwM6KefDT~ig`LOLG9pCoOPIIR2 zfoDUQI)a5zmkwzF4|~gK?=53p)}vjv(Y}Z5e6VW8otp&hjY7;8CLOZXP1CX#^8&wn zX?il}#XF{xrlQUO4i8m*E-NjKb&FgZ6~L+LJ}&4$B1D~dN4IsC;%5XSd0Cy zeX$#`eRok2wGFwVfaxiT osXjq%jIZ7OjtAH<3Zu8d<5f<-!3Z_>(*_HmDnqEty7R&2zxA$bzyJUM literal 0 HcmV?d00001 diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py new file mode 100644 index 0000000..5ba926e --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + +from collections import OrderedDict + + +def _attr_key(attr): + """Return an appropriate key for an attribute for sorting + + Attributes have a namespace that can be either ``None`` or a string. We + can't compare the two because they're different types, so we convert + ``None`` to an empty string first. + + """ + return (attr[0][0] or ''), attr[0][1] + + +class Filter(base.Filter): + """Alphabetizes attributes for elements""" + def __iter__(self): + for token in base.Filter.__iter__(self): + if token["type"] in ("StartTag", "EmptyTag"): + attrs = OrderedDict() + for name, value in sorted(token["data"].items(), + key=_attr_key): + attrs[name] = value + token["data"] = attrs + yield token diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py new file mode 100644 index 0000000..c7dbaed --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + + +class Filter(object): + def __init__(self, source): + self.source = source + + def __iter__(self): + return iter(self.source) + + def __getattr__(self, name): + return getattr(self.source, name) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py new file mode 100644 index 0000000..aefb5c8 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Injects ```` tag into head of document""" + def __init__(self, source, encoding): + """Creates a Filter + + :arg source: the source token stream + + :arg encoding: the encoding to set + + """ + base.Filter.__init__(self, source) + self.encoding = encoding + + def __iter__(self): + state = "pre_head" + meta_found = (self.encoding is None) + pending = [] + + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag": + if token["name"].lower() == "head": + state = "in_head" + + elif type == "EmptyTag": + if token["name"].lower() == "meta": + # replace charset with actual encoding + has_http_equiv_content_type = False + for (namespace, name), value in token["data"].items(): + if namespace is not None: + continue + elif name.lower() == 'charset': + token["data"][(namespace, name)] = self.encoding + meta_found = True + break + elif name == 'http-equiv' and value.lower() == 'content-type': + has_http_equiv_content_type = True + else: + if has_http_equiv_content_type and (None, "content") in token["data"]: + token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + meta_found = True + + elif token["name"].lower() == "head" and not meta_found: + # insert meta into empty head + yield {"type": "StartTag", "name": "head", + "data": token["data"]} + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + yield {"type": "EndTag", "name": "head"} + meta_found = True + continue + + elif type == "EndTag": + if token["name"].lower() == "head" and pending: + # insert meta into head (if necessary) and flush pending queue + yield pending.pop(0) + if not meta_found: + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + while pending: + yield pending.pop(0) + meta_found = True + state = "post_head" + + if state == "in_head": + pending.append(token) + else: + yield token diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py new file mode 100644 index 0000000..fcc07ee --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type + +from . import base +from ..constants import namespaces, voidElements + +from ..constants import spaceCharacters +spaceCharacters = "".join(spaceCharacters) + + +class Filter(base.Filter): + """Lints the token stream for errors + + If it finds any errors, it'll raise an ``AssertionError``. + + """ + def __init__(self, source, require_matching_tags=True): + """Creates a Filter + + :arg source: the source token stream + + :arg require_matching_tags: whether or not to require matching tags + + """ + super(Filter, self).__init__(source) + self.require_matching_tags = require_matching_tags + + def __iter__(self): + open_elements = [] + for token in base.Filter.__iter__(self): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(token["data"], dict) + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert type == "EmptyTag" + else: + assert type == "StartTag" + if type == "StartTag" and self.require_matching_tags: + open_elements.append((namespace, name)) + for (namespace, name), value in token["data"].items(): + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(value, text_type) + + elif type == "EndTag": + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert False, "Void element reported as EndTag token: %(tag)s" % {"tag": name} + elif self.require_matching_tags: + start = open_elements.pop() + assert start == (namespace, name) + + elif type == "Comment": + data = token["data"] + assert isinstance(data, text_type) + + elif type in ("Characters", "SpaceCharacters"): + data = token["data"] + assert isinstance(data, text_type) + assert data != "" + if type == "SpaceCharacters": + assert data.strip(spaceCharacters) == "" + + elif type == "Doctype": + name = token["name"] + assert name is None or isinstance(name, text_type) + assert token["publicId"] is None or isinstance(name, text_type) + assert token["systemId"] is None or isinstance(name, text_type) + + elif type == "Entity": + assert isinstance(token["name"], text_type) + + elif type == "SerializerError": + assert isinstance(token["data"], text_type) + + else: + assert False, "Unknown token type: %(type)s" % {"type": type} + + yield token diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py new file mode 100644 index 0000000..4a86501 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py @@ -0,0 +1,207 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Removes optional tags from the token stream""" + def slider(self): + previous1 = previous2 = None + for token in self.source: + if previous1 is not None: + yield previous2, previous1, token + previous2 = previous1 + previous1 = token + if previous1 is not None: + yield previous2, previous1, None + + def __iter__(self): + for previous, token, next in self.slider(): + type = token["type"] + if type == "StartTag": + if (token["data"] or + not self.is_optional_start(token["name"], previous, next)): + yield token + elif type == "EndTag": + if not self.is_optional_end(token["name"], next): + yield token + else: + yield token + + def is_optional_start(self, tagname, previous, next): + type = next and next["type"] or None + if tagname in 'html': + # An html element's start tag may be omitted if the first thing + # inside the html element is not a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname == 'head': + # A head element's start tag may be omitted if the first thing + # inside the head element is an element. + # XXX: we also omit the start tag if the head element is empty + if type in ("StartTag", "EmptyTag"): + return True + elif type == "EndTag": + return next["name"] == "head" + elif tagname == 'body': + # A body element's start tag may be omitted if the first thing + # inside the body element is not a space character or a comment, + # except if the first thing inside the body element is a script + # or style element and the node immediately preceding the body + # element is a head element whose end tag has been omitted. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we do not look at the preceding event, so we never omit + # the body element's start tag if it's followed by a script or + # a style element. + return next["name"] not in ('script', 'style') + else: + return True + elif tagname == 'colgroup': + # A colgroup element's start tag may be omitted if the first thing + # inside the colgroup element is a col element, and if the element + # is not immediately preceded by another colgroup element whose + # end tag has been omitted. + if type in ("StartTag", "EmptyTag"): + # XXX: we do not look at the preceding event, so instead we never + # omit the colgroup element's end tag when it is immediately + # followed by another colgroup element. See is_optional_end. + return next["name"] == "col" + else: + return False + elif tagname == 'tbody': + # A tbody element's start tag may be omitted if the first thing + # inside the tbody element is a tr element, and if the element is + # not immediately preceded by a tbody, thead, or tfoot element + # whose end tag has been omitted. + if type == "StartTag": + # omit the thead and tfoot elements' end tag when they are + # immediately followed by a tbody element. See is_optional_end. + if previous and previous['type'] == 'EndTag' and \ + previous['name'] in ('tbody', 'thead', 'tfoot'): + return False + return next["name"] == 'tr' + else: + return False + return False + + def is_optional_end(self, tagname, next): + type = next and next["type"] or None + if tagname in ('html', 'head', 'body'): + # An html element's end tag may be omitted if the html element + # is not immediately followed by a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname in ('li', 'optgroup', 'tr'): + # A li element's end tag may be omitted if the li element is + # immediately followed by another li element or if there is + # no more content in the parent element. + # An optgroup element's end tag may be omitted if the optgroup + # element is immediately followed by another optgroup element, + # or if there is no more content in the parent element. + # A tr element's end tag may be omitted if the tr element is + # immediately followed by another tr element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] == tagname + else: + return type == "EndTag" or type is None + elif tagname in ('dt', 'dd'): + # A dt element's end tag may be omitted if the dt element is + # immediately followed by another dt element or a dd element. + # A dd element's end tag may be omitted if the dd element is + # immediately followed by another dd element or a dt element, + # or if there is no more content in the parent element. + if type == "StartTag": + return next["name"] in ('dt', 'dd') + elif tagname == 'dd': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'p': + # A p element's end tag may be omitted if the p element is + # immediately followed by an address, article, aside, + # blockquote, datagrid, dialog, dir, div, dl, fieldset, + # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, + # nav, ol, p, pre, section, table, or ul, element, or if + # there is no more content in the parent element. + if type in ("StartTag", "EmptyTag"): + return next["name"] in ('address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul') + else: + return type == "EndTag" or type is None + elif tagname == 'option': + # An option element's end tag may be omitted if the option + # element is immediately followed by another option element, + # or if it is immediately followed by an optgroup + # element, or if there is no more content in the parent + # element. + if type == "StartTag": + return next["name"] in ('option', 'optgroup') + else: + return type == "EndTag" or type is None + elif tagname in ('rt', 'rp'): + # An rt element's end tag may be omitted if the rt element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + # An rp element's end tag may be omitted if the rp element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('rt', 'rp') + else: + return type == "EndTag" or type is None + elif tagname == 'colgroup': + # A colgroup element's end tag may be omitted if the colgroup + # element is not immediately followed by a space character or + # a comment. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we also look for an immediately following colgroup + # element. See is_optional_start. + return next["name"] != 'colgroup' + else: + return True + elif tagname in ('thead', 'tbody'): + # A thead element's end tag may be omitted if the thead element + # is immediately followed by a tbody or tfoot element. + # A tbody element's end tag may be omitted if the tbody element + # is immediately followed by a tbody or tfoot element, or if + # there is no more content in the parent element. + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] in ['tbody', 'tfoot'] + elif tagname == 'tbody': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'tfoot': + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] == 'tbody' + else: + return type == "EndTag" or type is None + elif tagname in ('td', 'th'): + # A td element's end tag may be omitted if the td element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + # A th element's end tag may be omitted if the th element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('td', 'th') + else: + return type == "EndTag" or type is None + return False diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py new file mode 100644 index 0000000..aa7431d --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -0,0 +1,916 @@ +"""Deprecated from html5lib 1.1. + +See `here `_ for +information about its deprecation; `Bleach `_ +is recommended as a replacement. Please let us know in the aforementioned issue +if Bleach is unsuitable for your needs. + +""" +from __future__ import absolute_import, division, unicode_literals + +import re +import warnings +from xml.sax.saxutils import escape, unescape + +from pip._vendor.six.moves import urllib_parse as urlparse + +from . import base +from ..constants import namespaces, prefixes + +__all__ = ["Filter"] + + +_deprecation_msg = ( + "html5lib's sanitizer is deprecated; see " + + "https://github.com/html5lib/html5lib-python/issues/443 and please let " + + "us know if Bleach is unsuitable for your needs" +) + +warnings.warn(_deprecation_msg, DeprecationWarning) + +allowed_elements = frozenset(( + (namespaces['html'], 'a'), + (namespaces['html'], 'abbr'), + (namespaces['html'], 'acronym'), + (namespaces['html'], 'address'), + (namespaces['html'], 'area'), + (namespaces['html'], 'article'), + (namespaces['html'], 'aside'), + (namespaces['html'], 'audio'), + (namespaces['html'], 'b'), + (namespaces['html'], 'big'), + (namespaces['html'], 'blockquote'), + (namespaces['html'], 'br'), + (namespaces['html'], 'button'), + (namespaces['html'], 'canvas'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'center'), + (namespaces['html'], 'cite'), + (namespaces['html'], 'code'), + (namespaces['html'], 'col'), + (namespaces['html'], 'colgroup'), + (namespaces['html'], 'command'), + (namespaces['html'], 'datagrid'), + (namespaces['html'], 'datalist'), + (namespaces['html'], 'dd'), + (namespaces['html'], 'del'), + (namespaces['html'], 'details'), + (namespaces['html'], 'dfn'), + (namespaces['html'], 'dialog'), + (namespaces['html'], 'dir'), + (namespaces['html'], 'div'), + (namespaces['html'], 'dl'), + (namespaces['html'], 'dt'), + (namespaces['html'], 'em'), + (namespaces['html'], 'event-source'), + (namespaces['html'], 'fieldset'), + (namespaces['html'], 'figcaption'), + (namespaces['html'], 'figure'), + (namespaces['html'], 'footer'), + (namespaces['html'], 'font'), + (namespaces['html'], 'form'), + (namespaces['html'], 'header'), + (namespaces['html'], 'h1'), + (namespaces['html'], 'h2'), + (namespaces['html'], 'h3'), + (namespaces['html'], 'h4'), + (namespaces['html'], 'h5'), + (namespaces['html'], 'h6'), + (namespaces['html'], 'hr'), + (namespaces['html'], 'i'), + (namespaces['html'], 'img'), + (namespaces['html'], 'input'), + (namespaces['html'], 'ins'), + (namespaces['html'], 'keygen'), + (namespaces['html'], 'kbd'), + (namespaces['html'], 'label'), + (namespaces['html'], 'legend'), + (namespaces['html'], 'li'), + (namespaces['html'], 'm'), + (namespaces['html'], 'map'), + (namespaces['html'], 'menu'), + (namespaces['html'], 'meter'), + (namespaces['html'], 'multicol'), + (namespaces['html'], 'nav'), + (namespaces['html'], 'nextid'), + (namespaces['html'], 'ol'), + (namespaces['html'], 'output'), + (namespaces['html'], 'optgroup'), + (namespaces['html'], 'option'), + (namespaces['html'], 'p'), + (namespaces['html'], 'pre'), + (namespaces['html'], 'progress'), + (namespaces['html'], 'q'), + (namespaces['html'], 's'), + (namespaces['html'], 'samp'), + (namespaces['html'], 'section'), + (namespaces['html'], 'select'), + (namespaces['html'], 'small'), + (namespaces['html'], 'sound'), + (namespaces['html'], 'source'), + (namespaces['html'], 'spacer'), + (namespaces['html'], 'span'), + (namespaces['html'], 'strike'), + (namespaces['html'], 'strong'), + (namespaces['html'], 'sub'), + (namespaces['html'], 'sup'), + (namespaces['html'], 'table'), + (namespaces['html'], 'tbody'), + (namespaces['html'], 'td'), + (namespaces['html'], 'textarea'), + (namespaces['html'], 'time'), + (namespaces['html'], 'tfoot'), + (namespaces['html'], 'th'), + (namespaces['html'], 'thead'), + (namespaces['html'], 'tr'), + (namespaces['html'], 'tt'), + (namespaces['html'], 'u'), + (namespaces['html'], 'ul'), + (namespaces['html'], 'var'), + (namespaces['html'], 'video'), + (namespaces['mathml'], 'maction'), + (namespaces['mathml'], 'math'), + (namespaces['mathml'], 'merror'), + (namespaces['mathml'], 'mfrac'), + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mmultiscripts'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mover'), + (namespaces['mathml'], 'mpadded'), + (namespaces['mathml'], 'mphantom'), + (namespaces['mathml'], 'mprescripts'), + (namespaces['mathml'], 'mroot'), + (namespaces['mathml'], 'mrow'), + (namespaces['mathml'], 'mspace'), + (namespaces['mathml'], 'msqrt'), + (namespaces['mathml'], 'mstyle'), + (namespaces['mathml'], 'msub'), + (namespaces['mathml'], 'msubsup'), + (namespaces['mathml'], 'msup'), + (namespaces['mathml'], 'mtable'), + (namespaces['mathml'], 'mtd'), + (namespaces['mathml'], 'mtext'), + (namespaces['mathml'], 'mtr'), + (namespaces['mathml'], 'munder'), + (namespaces['mathml'], 'munderover'), + (namespaces['mathml'], 'none'), + (namespaces['svg'], 'a'), + (namespaces['svg'], 'animate'), + (namespaces['svg'], 'animateColor'), + (namespaces['svg'], 'animateMotion'), + (namespaces['svg'], 'animateTransform'), + (namespaces['svg'], 'clipPath'), + (namespaces['svg'], 'circle'), + (namespaces['svg'], 'defs'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'ellipse'), + (namespaces['svg'], 'font-face'), + (namespaces['svg'], 'font-face-name'), + (namespaces['svg'], 'font-face-src'), + (namespaces['svg'], 'g'), + (namespaces['svg'], 'glyph'), + (namespaces['svg'], 'hkern'), + (namespaces['svg'], 'linearGradient'), + (namespaces['svg'], 'line'), + (namespaces['svg'], 'marker'), + (namespaces['svg'], 'metadata'), + (namespaces['svg'], 'missing-glyph'), + (namespaces['svg'], 'mpath'), + (namespaces['svg'], 'path'), + (namespaces['svg'], 'polygon'), + (namespaces['svg'], 'polyline'), + (namespaces['svg'], 'radialGradient'), + (namespaces['svg'], 'rect'), + (namespaces['svg'], 'set'), + (namespaces['svg'], 'stop'), + (namespaces['svg'], 'svg'), + (namespaces['svg'], 'switch'), + (namespaces['svg'], 'text'), + (namespaces['svg'], 'title'), + (namespaces['svg'], 'tspan'), + (namespaces['svg'], 'use'), +)) + +allowed_attributes = frozenset(( + # HTML attributes + (None, 'abbr'), + (None, 'accept'), + (None, 'accept-charset'), + (None, 'accesskey'), + (None, 'action'), + (None, 'align'), + (None, 'alt'), + (None, 'autocomplete'), + (None, 'autofocus'), + (None, 'axis'), + (None, 'background'), + (None, 'balance'), + (None, 'bgcolor'), + (None, 'bgproperties'), + (None, 'border'), + (None, 'bordercolor'), + (None, 'bordercolordark'), + (None, 'bordercolorlight'), + (None, 'bottompadding'), + (None, 'cellpadding'), + (None, 'cellspacing'), + (None, 'ch'), + (None, 'challenge'), + (None, 'char'), + (None, 'charoff'), + (None, 'choff'), + (None, 'charset'), + (None, 'checked'), + (None, 'cite'), + (None, 'class'), + (None, 'clear'), + (None, 'color'), + (None, 'cols'), + (None, 'colspan'), + (None, 'compact'), + (None, 'contenteditable'), + (None, 'controls'), + (None, 'coords'), + (None, 'data'), + (None, 'datafld'), + (None, 'datapagesize'), + (None, 'datasrc'), + (None, 'datetime'), + (None, 'default'), + (None, 'delay'), + (None, 'dir'), + (None, 'disabled'), + (None, 'draggable'), + (None, 'dynsrc'), + (None, 'enctype'), + (None, 'end'), + (None, 'face'), + (None, 'for'), + (None, 'form'), + (None, 'frame'), + (None, 'galleryimg'), + (None, 'gutter'), + (None, 'headers'), + (None, 'height'), + (None, 'hidefocus'), + (None, 'hidden'), + (None, 'high'), + (None, 'href'), + (None, 'hreflang'), + (None, 'hspace'), + (None, 'icon'), + (None, 'id'), + (None, 'inputmode'), + (None, 'ismap'), + (None, 'keytype'), + (None, 'label'), + (None, 'leftspacing'), + (None, 'lang'), + (None, 'list'), + (None, 'longdesc'), + (None, 'loop'), + (None, 'loopcount'), + (None, 'loopend'), + (None, 'loopstart'), + (None, 'low'), + (None, 'lowsrc'), + (None, 'max'), + (None, 'maxlength'), + (None, 'media'), + (None, 'method'), + (None, 'min'), + (None, 'multiple'), + (None, 'name'), + (None, 'nohref'), + (None, 'noshade'), + (None, 'nowrap'), + (None, 'open'), + (None, 'optimum'), + (None, 'pattern'), + (None, 'ping'), + (None, 'point-size'), + (None, 'poster'), + (None, 'pqg'), + (None, 'preload'), + (None, 'prompt'), + (None, 'radiogroup'), + (None, 'readonly'), + (None, 'rel'), + (None, 'repeat-max'), + (None, 'repeat-min'), + (None, 'replace'), + (None, 'required'), + (None, 'rev'), + (None, 'rightspacing'), + (None, 'rows'), + (None, 'rowspan'), + (None, 'rules'), + (None, 'scope'), + (None, 'selected'), + (None, 'shape'), + (None, 'size'), + (None, 'span'), + (None, 'src'), + (None, 'start'), + (None, 'step'), + (None, 'style'), + (None, 'summary'), + (None, 'suppress'), + (None, 'tabindex'), + (None, 'target'), + (None, 'template'), + (None, 'title'), + (None, 'toppadding'), + (None, 'type'), + (None, 'unselectable'), + (None, 'usemap'), + (None, 'urn'), + (None, 'valign'), + (None, 'value'), + (None, 'variable'), + (None, 'volume'), + (None, 'vspace'), + (None, 'vrml'), + (None, 'width'), + (None, 'wrap'), + (namespaces['xml'], 'lang'), + # MathML attributes + (None, 'actiontype'), + (None, 'align'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnlines'), + (None, 'columnspacing'), + (None, 'columnspan'), + (None, 'depth'), + (None, 'display'), + (None, 'displaystyle'), + (None, 'equalcolumns'), + (None, 'equalrows'), + (None, 'fence'), + (None, 'fontstyle'), + (None, 'fontweight'), + (None, 'frame'), + (None, 'height'), + (None, 'linethickness'), + (None, 'lspace'), + (None, 'mathbackground'), + (None, 'mathcolor'), + (None, 'mathvariant'), + (None, 'mathvariant'), + (None, 'maxsize'), + (None, 'minsize'), + (None, 'other'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowlines'), + (None, 'rowspacing'), + (None, 'rowspan'), + (None, 'rspace'), + (None, 'scriptlevel'), + (None, 'selection'), + (None, 'separator'), + (None, 'stretchy'), + (None, 'width'), + (None, 'width'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'type'), + # SVG attributes + (None, 'accent-height'), + (None, 'accumulate'), + (None, 'additive'), + (None, 'alphabetic'), + (None, 'arabic-form'), + (None, 'ascent'), + (None, 'attributeName'), + (None, 'attributeType'), + (None, 'baseProfile'), + (None, 'bbox'), + (None, 'begin'), + (None, 'by'), + (None, 'calcMode'), + (None, 'cap-height'), + (None, 'class'), + (None, 'clip-path'), + (None, 'color'), + (None, 'color-rendering'), + (None, 'content'), + (None, 'cx'), + (None, 'cy'), + (None, 'd'), + (None, 'dx'), + (None, 'dy'), + (None, 'descent'), + (None, 'display'), + (None, 'dur'), + (None, 'end'), + (None, 'fill'), + (None, 'fill-opacity'), + (None, 'fill-rule'), + (None, 'font-family'), + (None, 'font-size'), + (None, 'font-stretch'), + (None, 'font-style'), + (None, 'font-variant'), + (None, 'font-weight'), + (None, 'from'), + (None, 'fx'), + (None, 'fy'), + (None, 'g1'), + (None, 'g2'), + (None, 'glyph-name'), + (None, 'gradientUnits'), + (None, 'hanging'), + (None, 'height'), + (None, 'horiz-adv-x'), + (None, 'horiz-origin-x'), + (None, 'id'), + (None, 'ideographic'), + (None, 'k'), + (None, 'keyPoints'), + (None, 'keySplines'), + (None, 'keyTimes'), + (None, 'lang'), + (None, 'marker-end'), + (None, 'marker-mid'), + (None, 'marker-start'), + (None, 'markerHeight'), + (None, 'markerUnits'), + (None, 'markerWidth'), + (None, 'mathematical'), + (None, 'max'), + (None, 'min'), + (None, 'name'), + (None, 'offset'), + (None, 'opacity'), + (None, 'orient'), + (None, 'origin'), + (None, 'overline-position'), + (None, 'overline-thickness'), + (None, 'panose-1'), + (None, 'path'), + (None, 'pathLength'), + (None, 'points'), + (None, 'preserveAspectRatio'), + (None, 'r'), + (None, 'refX'), + (None, 'refY'), + (None, 'repeatCount'), + (None, 'repeatDur'), + (None, 'requiredExtensions'), + (None, 'requiredFeatures'), + (None, 'restart'), + (None, 'rotate'), + (None, 'rx'), + (None, 'ry'), + (None, 'slope'), + (None, 'stemh'), + (None, 'stemv'), + (None, 'stop-color'), + (None, 'stop-opacity'), + (None, 'strikethrough-position'), + (None, 'strikethrough-thickness'), + (None, 'stroke'), + (None, 'stroke-dasharray'), + (None, 'stroke-dashoffset'), + (None, 'stroke-linecap'), + (None, 'stroke-linejoin'), + (None, 'stroke-miterlimit'), + (None, 'stroke-opacity'), + (None, 'stroke-width'), + (None, 'systemLanguage'), + (None, 'target'), + (None, 'text-anchor'), + (None, 'to'), + (None, 'transform'), + (None, 'type'), + (None, 'u1'), + (None, 'u2'), + (None, 'underline-position'), + (None, 'underline-thickness'), + (None, 'unicode'), + (None, 'unicode-range'), + (None, 'units-per-em'), + (None, 'values'), + (None, 'version'), + (None, 'viewBox'), + (None, 'visibility'), + (None, 'width'), + (None, 'widths'), + (None, 'x'), + (None, 'x-height'), + (None, 'x1'), + (None, 'x2'), + (namespaces['xlink'], 'actuate'), + (namespaces['xlink'], 'arcrole'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'role'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'title'), + (namespaces['xlink'], 'type'), + (namespaces['xml'], 'base'), + (namespaces['xml'], 'lang'), + (namespaces['xml'], 'space'), + (None, 'y'), + (None, 'y1'), + (None, 'y2'), + (None, 'zoomAndPan'), +)) + +attr_val_is_uri = frozenset(( + (None, 'href'), + (None, 'src'), + (None, 'cite'), + (None, 'action'), + (None, 'longdesc'), + (None, 'poster'), + (None, 'background'), + (None, 'datasrc'), + (None, 'dynsrc'), + (None, 'lowsrc'), + (None, 'ping'), + (namespaces['xlink'], 'href'), + (namespaces['xml'], 'base'), +)) + +svg_attr_val_allows_ref = frozenset(( + (None, 'clip-path'), + (None, 'color-profile'), + (None, 'cursor'), + (None, 'fill'), + (None, 'filter'), + (None, 'marker'), + (None, 'marker-start'), + (None, 'marker-mid'), + (None, 'marker-end'), + (None, 'mask'), + (None, 'stroke'), +)) + +svg_allow_local_href = frozenset(( + (None, 'altGlyph'), + (None, 'animate'), + (None, 'animateColor'), + (None, 'animateMotion'), + (None, 'animateTransform'), + (None, 'cursor'), + (None, 'feImage'), + (None, 'filter'), + (None, 'linearGradient'), + (None, 'pattern'), + (None, 'radialGradient'), + (None, 'textpath'), + (None, 'tref'), + (None, 'set'), + (None, 'use') +)) + +allowed_css_properties = frozenset(( + 'azimuth', + 'background-color', + 'border-bottom-color', + 'border-collapse', + 'border-color', + 'border-left-color', + 'border-right-color', + 'border-top-color', + 'clear', + 'color', + 'cursor', + 'direction', + 'display', + 'elevation', + 'float', + 'font', + 'font-family', + 'font-size', + 'font-style', + 'font-variant', + 'font-weight', + 'height', + 'letter-spacing', + 'line-height', + 'overflow', + 'pause', + 'pause-after', + 'pause-before', + 'pitch', + 'pitch-range', + 'richness', + 'speak', + 'speak-header', + 'speak-numeral', + 'speak-punctuation', + 'speech-rate', + 'stress', + 'text-align', + 'text-decoration', + 'text-indent', + 'unicode-bidi', + 'vertical-align', + 'voice-family', + 'volume', + 'white-space', + 'width', +)) + +allowed_css_keywords = frozenset(( + 'auto', + 'aqua', + 'black', + 'block', + 'blue', + 'bold', + 'both', + 'bottom', + 'brown', + 'center', + 'collapse', + 'dashed', + 'dotted', + 'fuchsia', + 'gray', + 'green', + '!important', + 'italic', + 'left', + 'lime', + 'maroon', + 'medium', + 'none', + 'navy', + 'normal', + 'nowrap', + 'olive', + 'pointer', + 'purple', + 'red', + 'right', + 'solid', + 'silver', + 'teal', + 'top', + 'transparent', + 'underline', + 'white', + 'yellow', +)) + +allowed_svg_properties = frozenset(( + 'fill', + 'fill-opacity', + 'fill-rule', + 'stroke', + 'stroke-width', + 'stroke-linecap', + 'stroke-linejoin', + 'stroke-opacity', +)) + +allowed_protocols = frozenset(( + 'ed2k', + 'ftp', + 'http', + 'https', + 'irc', + 'mailto', + 'news', + 'gopher', + 'nntp', + 'telnet', + 'webcal', + 'xmpp', + 'callto', + 'feed', + 'urn', + 'aim', + 'rsync', + 'tag', + 'ssh', + 'sftp', + 'rtsp', + 'afs', + 'data', +)) + +allowed_content_types = frozenset(( + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp', + 'image/bmp', + 'text/plain', +)) + + +data_content_type = re.compile(r''' + ^ + # Match a content type / + (?P[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+) + # Match any character set and encoding + (?:(?:;charset=(?:[-a-zA-Z0-9]+)(?:;(?:base64))?) + |(?:;(?:base64))?(?:;charset=(?:[-a-zA-Z0-9]+))?) + # Assume the rest is data + ,.* + $ + ''', + re.VERBOSE) + + +class Filter(base.Filter): + """Sanitizes token stream of XHTML+MathML+SVG and of inline style attributes""" + def __init__(self, + source, + allowed_elements=allowed_elements, + allowed_attributes=allowed_attributes, + allowed_css_properties=allowed_css_properties, + allowed_css_keywords=allowed_css_keywords, + allowed_svg_properties=allowed_svg_properties, + allowed_protocols=allowed_protocols, + allowed_content_types=allowed_content_types, + attr_val_is_uri=attr_val_is_uri, + svg_attr_val_allows_ref=svg_attr_val_allows_ref, + svg_allow_local_href=svg_allow_local_href): + """Creates a Filter + + :arg allowed_elements: set of elements to allow--everything else will + be escaped + + :arg allowed_attributes: set of attributes to allow in + elements--everything else will be stripped + + :arg allowed_css_properties: set of CSS properties to allow--everything + else will be stripped + + :arg allowed_css_keywords: set of CSS keywords to allow--everything + else will be stripped + + :arg allowed_svg_properties: set of SVG properties to allow--everything + else will be removed + + :arg allowed_protocols: set of allowed protocols for URIs + + :arg allowed_content_types: set of allowed content types for ``data`` URIs. + + :arg attr_val_is_uri: set of attributes that have URI values--values + that have a scheme not listed in ``allowed_protocols`` are removed + + :arg svg_attr_val_allows_ref: set of SVG attributes that can have + references + + :arg svg_allow_local_href: set of SVG elements that can have local + hrefs--these are removed + + """ + super(Filter, self).__init__(source) + + warnings.warn(_deprecation_msg, DeprecationWarning) + + self.allowed_elements = allowed_elements + self.allowed_attributes = allowed_attributes + self.allowed_css_properties = allowed_css_properties + self.allowed_css_keywords = allowed_css_keywords + self.allowed_svg_properties = allowed_svg_properties + self.allowed_protocols = allowed_protocols + self.allowed_content_types = allowed_content_types + self.attr_val_is_uri = attr_val_is_uri + self.svg_attr_val_allows_ref = svg_attr_val_allows_ref + self.svg_allow_local_href = svg_allow_local_href + + def __iter__(self): + for token in base.Filter.__iter__(self): + token = self.sanitize_token(token) + if token: + yield token + + # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and + # stripping out all attributes not in ALLOWED_ATTRIBUTES. Style attributes + # are parsed, and a restricted set, specified by ALLOWED_CSS_PROPERTIES and + # ALLOWED_CSS_KEYWORDS, are allowed through. attributes in ATTR_VAL_IS_URI + # are scanned, and only URI schemes specified in ALLOWED_PROTOCOLS are + # allowed. + # + # sanitize_html('') + # => <script> do_nasty_stuff() </script> + # sanitize_html('Click here for $100') + # => Click here for $100 + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile(r'url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..0d12584 --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + """Collapses whitespace except in pre, textarea, and script elements""" + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..d06784f --- /dev/null +++ b/pytest_project/pytest-env/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2795 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys + +import types + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + _ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML document as a string or file-like object into a tree + + :arg doc: the document to parse as a string or file-like object + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import parse + >>> parse('

      This is a doc

      ') + + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, **kwargs) + + +def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML fragment as a string or file-like object into a tree + + :arg doc: the fragment to parse as a string or file-like object + + :arg container: the container context to parse the fragment in + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import parseFragment + >>> parseFragment('this is a fragment') + + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, **kwargs) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser + + Generates a tree structure from a stream of (possibly malformed) HTML. + + """ + + def __init__(self, tree=None, strict=False, namespaceHTMLElements=True, debug=False): + """ + :arg tree: a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + + :arg strict: raise an exception when a parse error is encountered + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :arg debug: whether or not to enable debug mode which logs things + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() # generates parser with etree builder + >>> parser = HTMLParser('lxml', strict=True) # generates parser with lxml builder which is strict + + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.errors = [] + + self.phases = {name: cls(self, self.tree) for name, cls in + getPhases(debug).items()} + + def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.scripting = scripting + self.tokenizer = _tokenizer.HTMLTokenizer(stream, parser=self, **kwargs) + self.reset() + + try: + self.mainLoop() + except _ReparseException: + self.reset() + self.mainLoop() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False # pylint:disable=redefined-variable-type + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + @property + def documentEncoding(self): + """Name of the character encoding that was used to decode the input stream, or + :obj:`None` if that is not determined yet + + """ + if not hasattr(self, 'tokenizer'): + return None + return self.tokenizer.stream.charEncoding[0].name + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.tokenizer: + prev_token = None + new_token = token + while new_token is not None: + prev_token = new_token + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + type == StartTagToken and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and prev_token["selfClosing"] and + not prev_token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": prev_token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def parse(self, stream, *args, **kwargs): + """Parse a HTML document into a well-formed tree + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element). + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parse('

      This is a doc

      ') + + + """ + self._parse(stream, False, None, *args, **kwargs) + return self.tree.getDocument() + + def parseFragment(self, stream, *args, **kwargs): + """Parse a HTML fragment into a well-formed tree fragment + + :arg container: name of the element we're setting the innerHTML + property if set to None, default to 'div' + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parseFragment('this is a fragment') + + + """ + self._parse(stream, True, *args, **kwargs) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars=None): + # XXX The idea is to make errorcode mandatory. + if datavars is None: + datavars = {} + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError(E[errorcode] % datavars) + + def adjustMathMLAttributes(self, token): + adjust_attributes(token, adjustMathMLAttributes) + + def adjustSVGAttributes(self, token): + adjust_attributes(token, adjustSVGAttributes) + + def adjustForeignAttributes(self, token): + adjust_attributes(token, adjustForeignAttributesMap) + + def reparseTokenNormal(self, token): + # pylint:disable=unused-argument + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + # Generic RCDATA/RAWTEXT Parsing algorithm + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +@_utils.memoize +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = {value: key for key, value in tokenTypes.items()} + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + info = {"type": type_names[token['type']]} + if token['type'] in tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + # pylint:disable=unused-argument + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + __slots__ = ("parser", "tree", "__startTagCache", "__endTagCache") + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + self.__startTagCache = {} + self.__endTagCache = {} + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__startTagCache: + func = self.__startTagCache[name] + else: + func = self.__startTagCache[name] = self.startTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__startTagCache) > len(self.startTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__startTagCache.pop(next(iter(self.__startTagCache))) + return func(token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__endTagCache: + func = self.__endTagCache[name] + else: + func = self.__endTagCache[name] = self.endTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__endTagCache) > len(self.endTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__endTagCache.pop(next(iter(self.__endTagCache))) + return func(token) + + class InitialPhase(Phase): + __slots__ = tuple() + + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" or + publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) or + publicId in ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None or + systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + __slots__ = tuple() + + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), endTagImplyHead) + ]) + endTagHandler.default = endTagOther + + class InHeadPhase(Phase): + __slots__ = tuple() + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = _inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagNoscript(self, token): + if self.parser.scripting: + self.parser.parseRCDataRawtext(token, "RAWTEXT") + else: + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inHeadNoscript"] + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("title", startTagTitle), + (("noframes", "style"), startTagNoFramesStyle), + ("noscript", startTagNoscript), + ("script", startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + startTagBaseLinkCommand), + ("meta", startTagMeta), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("head", endTagHead), + (("br", "html", "body"), endTagHtmlBodyBr) + ]) + endTagHandler.default = endTagOther + + class InHeadNoscriptPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.parser.parseError("eof-in-head-noscript") + self.anythingElse() + return True + + def processComment(self, token): + return self.parser.phases["inHead"].processComment(token) + + def processCharacters(self, token): + self.parser.parseError("char-in-head-noscript") + self.anythingElse() + return token + + def processSpaceCharacters(self, token): + return self.parser.phases["inHead"].processSpaceCharacters(token) + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBaseLinkCommand(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagHeadNoscript(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagNoscript(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "noscript", "Expected noscript got %s" % node.name + self.parser.phase = self.parser.phases["inHead"] + + def endTagBr(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + # Caller must raise parse error first! + self.endTagNoscript(impliedTagToken("noscript")) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), startTagBaseLinkCommand), + (("head", "noscript"), startTagHeadNoscript), + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("noscript", endTagNoscript), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + + class AfterHeadPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + startTagFromHead), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + endTagHtmlBodyBr)]) + endTagHandler.default = endTagOther + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + __slots__ = ("processSpaceCharacters",) + + def __init__(self, *args, **kwargs): + super(InBodyPhase, self).__init__(*args, **kwargs) + # Set this to the default handler + self.processSpaceCharacters = self.processSpaceCharactersNonPre + + def isMatchingFormattingElement(self, node1, node2): + return (node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes) + + # helper + def addFormattingElement(self, token): + self.tree.insertElement(token) + element = self.tree.openElements[-1] + + matchingElements = [] + for node in self.tree.activeFormattingElements[::-1]: + if node is Marker: + break + elif self.isMatchingFormattingElement(node, element): + matchingElements.append(node) + + assert len(matchingElements) <= 3 + if len(matchingElements) == 3: + self.tree.activeFormattingElements.remove(matchingElements[-1]) + self.tree.activeFormattingElements.append(element) + + # the real deal + def processEOF(self): + allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", + "tfoot", "th", "thead", "tr", "body", + "html")) + for node in self.tree.openElements[::-1]: + if node.name not in allowed_elements: + self.parser.parseError("expected-closing-tag-but-got-eof") + break + # Stop parsing + + def processSpaceCharactersDropNewline(self, token): + # Sometimes (start of
      , , and