[
  {
    "path": ".cirrus.yml",
    "content": "tests_task:\n  # We only use Cirrus CI for FreeBSD at present; the rest of the task will assume FreeBSD.\n  freebsd_instance:\n    image_family: freebsd-14-3\n    # Cirrus has a concurrency limit of 8 vCPUs for FreeBSD. Allow executing 4 tasks in parallel.\n    cpu: 2\n    memory: 2G\n\n  env:\n    matrix:\n      - PYTHON: python3.11\n        PYTHON_VERSION: 3.11\n        PYTHON_PACKAGE: python311\n        SQLITE_PACKAGE: py311-sqlite3\n      # FIXME: Python 3.12 is not available in Ports.\n      # - PYTHON: python3.12\n      #   PYTHON_VERSION: 3.12\n      #   PYTHON_PACKAGE: python312\n      #   SQLITE_PACKAGE: py312-sqlite3\n    # FIXME: use pipx for install. pipx is currently broken in Ports.\n    POETRY_HOME: /opt/poetry\n    # SHELL is not set by default, and we have tests that depend on it.\n    SHELL: sh\n\n  bootstrap_poetry_script:\n    - pkg install -y git $PYTHON_PACKAGE $SQLITE_PACKAGE\n    - $PYTHON -m venv $POETRY_HOME\n    - $POETRY_HOME/bin/pip install poetry\n    - echo \"PATH=${POETRY_HOME}/bin:${PATH}\" >> $CIRRUS_ENV\n\n  setup_environment_script:\n    # TODO: caching\n    - poetry install\n    - poetry env info\n    - poetry show\n\n  matrix:\n    - alias: pytest\n      name: \"Tests / FreeBSD (Python ${PYTHON_VERSION}) / pytest\"\n      skip: \"!changesInclude('.cirrus.yml', 'poetry.lock', 'pyproject.toml', 'src/**.py', 'tests/**')\"\n      pytest_script: poetry run pytest --integration -v --junitxml=junit.xml\n      on_failure:\n        annotate_failure_artifacts:\n          path: junit.xml\n          format: junit\n          type: text/xml\n\nstatus_task:\n  name: \"Tests / FreeBSD Status\"\n\n  depends_on:\n    - pytest\n\n  container:\n    image: alpine:latest\n    cpu: 0.5\n    memory: 512M\n\n  # No-op the clone.\n  clone_script: true\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Do not mess with line endings in metadata files or the hash will be wrong.\n*.metadata binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/---bug-report.yml",
    "content": "name: \"\\U0001F41E Bug Report\"\nlabels: [\"kind/bug\", \"status/triage\"]\ndescription: \"Poetry not working the way it is documented?\"\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for taking the time to file a complete bug report.\n\n        Before submitting your issue, please review the [Before submitting a bug report](https://python-poetry.org/docs/contributing/#before-submitting-a-bug-report) section of our documentation.\n\n  - type: textarea\n    attributes:\n      label: Description\n      description: |\n        Please describe what happened, with as much pertinent information as you can. Feel free to use markdown syntax.\n\n        Also, ensure that the issue is not already fixed in the [latest](https://github.com/python-poetry/poetry/releases/latest) Poetry release.\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Workarounds\n      description: |\n        Is there a mitigation or workaround that allows users to avoid the issue today?\n    validations:\n      required: true\n\n  - type: dropdown\n    attributes:\n      label: Poetry Installation Method\n      description: |\n        How did you install Poetry?\n      options:\n        - \"pipx\"\n        - \"install.python-poetry.org\"\n        - \"system package manager (eg: dnf, apt etc.)\"\n        - \"pip\"\n        - \"other\"\n    validations:\n      required: true\n\n  - type: input\n    attributes:\n      label: Operating System\n      description: |\n        What Operating System are you using?\n      placeholder: \"Fedora 39\"\n    validations:\n      required: true\n\n  - type: input\n    attributes:\n      label: Poetry Version\n      description: |\n        Please attach output from `poetry --version`\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Poetry Configuration\n      description: |\n        Please attach output from `poetry config --list`\n      render: 'bash session'\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Python Sysconfig\n      description: |\n        Please attach output from `python -m sysconfig`\n        Note:_ You can paste the output into the placeholder below. If it is too long, you can attach it as a file.\n      value: |\n        <details>\n          <summary>sysconfig.log</summary>\n          <!-- Please leave one blank line below for enabling the code block rendering. -->\n\n          ```\n          Paste the output of 'python -m sysconfig', over this line.\n          ```\n        </details>\n    validations:\n      required: false\n\n  - type: textarea\n    attributes:\n      label: Example pyproject.toml\n      description: |\n        Please provide an example `pyproject.toml` demonstrating the issue.\n      render: 'TOML'\n    validations:\n      required: false\n\n  - type: textarea\n    attributes:\n      label: Poetry Runtime Logs\n      description: |\n        Please attach logs from the failing command using `poetry -vvv <command>`\n        Note:_ You can paste the output into the placeholder below. If it is too long, you can attach it as a file.\n      value: |\n        <details>\n          <summary>poetry-runtime.log</summary>\n          <!-- Please leave one blank line below for enabling the code block rendering. -->\n\n          ```\n          Paste the output of 'poetry -vvv <command>', over this line.\n          ```\n        </details>\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/---documentation.yml",
    "content": "name: \"\\U0001F4DA Documentation Issue\"\nlabels: [\"area/docs\", \"status/triage\"]\ndescription: \"Did you find errors, omissions, or anything unintelligible in the documentation?\"\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for taking the time to file a complete bug report.\n\n        Before submitting your issue, please review the [Suggesting enhancements](https://python-poetry.org/docs/contributing/#suggesting-enhancements) section of our documentation.\n\n        Please also confirm the following:\n        - You have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repository and believe that this is not a duplicate.\n        - You have searched the [FAQ](https://python-poetry.org/docs/faq/) and general [documentation](https://python-poetry.org/docs/) and believe that your question is not already covered.\n\n  - type: dropdown\n    attributes:\n      label: Issue Kind\n      description: |\n        What best describes the issue?\n      options:\n        - \"Improving documentation\"\n        - \"Missing documentation\"\n        - \"Error in existing documentation\"\n        - \"Unclear documentation\"\n        - \"Other concerns with documentation\"\n    validations:\n      required: true\n\n  - type: input\n    attributes:\n      label: Existing Link\n      description: |\n        If the documentation in question exists, please provide a link to it.\n      placeholder: \"https://python-poetry.org/docs/dependency-specification/#version-constraints\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Description\n      description: |\n        Please describe the feature, with as much pertinent information as you can. Feel free to use markdown syntax.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/---feature-request.yml",
    "content": "name: \"\\U0001F381 Feature Request\"\nlabels: [\"kind/feature\", \"status/triage\"]\ndescription: \"Want something new?\"\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for taking the time to file a complete bug report.\n\n        Before submitting your issue, please search [issues](https://github.com/python-poetry/poetry/issues) to ensure this is not a duplicate.\n\n        If the issue is trivial, why not submit a pull request instead?\n\n  - type: dropdown\n    attributes:\n      label: Issue Kind\n      description: |\n        What best describes this issue?\n      options:\n        - \"Brand new capability\"\n        - \"Change in current behaviour\"\n        - \"Other\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Description\n      description: |\n        Please describe the issue, with as much pertinent information as you can. Feel free to use markdown syntax.\n\n        Also, ensure that the issue is not already fixed in the [development documentation](https://python-poetry.org/docs/main/).\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Impact\n      description: |\n        Please describe the motivation for this issue. Describe, as best you can, how this improves or impacts the users of Poetry and why this is important.\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Workarounds\n      description: |\n        Is there a mitigation, workaround, or addon that allows users to achieve the same functionality today?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser\nblank_issues_enabled: false\ncontact_links:\n- name: '💬 Discussions'\n  url: https://github.com/python-poetry/poetry/discussions\n  about: |\n    Ask questions about using Poetry, Poetry's features and roadmap, or get support and feedback for your usage of Poetry.\n- name: '💬 Discord Server'\n  url: https://discordapp.com/invite/awxPgve\n  about: |\n    Chat with the community and Poetry maintainers about both the usage of and development of the project.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# Pull Request Check List\n\nResolves: #issue-number-here\n\n<!-- This is just a reminder about the most common mistakes. Please make sure that you tick all *appropriate* boxes.  But please read our [contribution guide](https://python-poetry.org/docs/contributing/) at least once, it will save you unnecessary review cycles! -->\n\n- [ ] Added **tests** for changed code.\n- [ ] Updated **documentation** for changed code.\n\n<!-- If you have *any* questions to *any* of the points above, just **submit and ask**!  This checklist is here to *help* you, not to deter you from contributing! -->\n"
  },
  {
    "path": ".github/actions/bootstrap-poetry/action.yaml",
    "content": "name: Bootstrap Poetry\ndescription: Configure the environment with the specified Python and Poetry version.\n\ninputs:\n  python-version:\n    description: Desired node-semver compatible Python version expression (or 'default')\n    default: 'default'\n  python-latest:\n    description: Use an uncached Python if a newer match is available\n    default: 'false'\n  poetry-spec:\n    description: pip-compatible installation specification to use for Poetry\n    default: 'poetry'\n\noutputs:\n  python-path:\n    description: Path to the installed Python interpreter\n    value: ${{ steps.setup-python.outputs.python-path }}\n  python-version:\n    description: Version of the installed Python interpreter\n    value: ${{ steps.setup-python.outputs.python-version }}\n\nruns:\n  using: composite\n  steps:\n    - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0\n      id: setup-python\n      if: inputs.python-version != 'default'\n      with:\n        python-version: ${{ inputs.python-version }}\n        check-latest: ${{ inputs.python-latest == 'true' }}\n        allow-prereleases: true\n        update-environment: false\n\n    - run: pipx install ${PYTHON_PATH:+--python \"$PYTHON_PATH\"} \"${POETRY_SPEC}\"\n      shell: bash\n      env:\n        PYTHON_PATH: ${{ inputs.python-version != 'default' && steps.setup-python.outputs.python-path || '' }}\n        POETRY_SPEC: ${{ inputs.poetry-spec }}\n\n    # Enable handling long path names (+260 char) on the Windows platform\n    # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation\n    - run: git config --system core.longpaths true\n      if: runner.os == 'Windows'\n      shell: pwsh\n\n    # Use Poetry Python for virtual environments\n    # (Otherwise, the system Python will be used per default instead of the Python version we just installed)\n    - run: poetry config virtualenvs.use-poetry-python true\n      shell: bash\n"
  },
  {
    "path": ".github/actions/poetry-install/action.yaml",
    "content": "name: Poetry Install\ndescription: Run `poetry install` with optional artifact and metadata caching\n\ninputs:\n  args:\n    description: Arguments for `poetry install`\n  cache:\n    description: Enable transparent Poetry artifact and metadata caching\n    default: 'true'\n\noutputs:\n  cache-hit:\n    description: Whether an exact cache hit occured\n    value: ${{ steps.cache.outputs.cache-hit }}\n\nruns:\n  using: composite\n  steps:\n    - run: printf 'cache-dir=%s\\n' \"$(poetry config cache-dir)\" >> $GITHUB_OUTPUT\n      id: poetry-config\n      shell: bash\n\n    # Bust the cache every 24 hours to prevent it from expanding over time.\n    - run: printf 'date=%s\\n' \"$(date -I)\" >> $GITHUB_OUTPUT\n      id: get-date\n      if: inputs.cache == 'true'\n      shell: bash\n\n    - uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2\n      id: cache\n      if: inputs.cache == 'true'\n      with:\n        path: |\n          ${{ steps.poetry-config.outputs.cache-dir }}/artifacts\n          ${{ steps.poetry-config.outputs.cache-dir }}/cache\n        key: poetry-${{ steps.get-date.outputs.date }}-${{ runner.os }}-${{ hashFiles('pyproject.toml', 'poetry.lock') }}\n        # The cache is cross-platform, and other platforms are used to seed cache misses.\n        restore-keys: |\n          poetry-${{ steps.get-date.outputs.date }}-${{ runner.os }}-\n          poetry-${{ steps.get-date.outputs.date }}-\n        enableCrossOsArchive: true\n\n    - run: poetry install ${ARGS}\n      shell: bash\n      env:\n        ARGS: ${{ inputs.args }}\n\n    - run: poetry env info\n      shell: bash\n\n    - run: poetry show\n      shell: bash\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    cooldown:\n      default-days: 7\n    labels:\n      - \"area/ci\"\n\n  # Grouped updates to reduce PR noise.\n  #\n  # See:\n  #   - https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/optimizing-pr-creation-version-updates#prioritizing-meaningful-updates\n  #   - https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference#groups--\n  - package-ecosystem: \"pip\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    cooldown:\n      default-days: 7\n    labels:\n      - \"area/project/deps\"\n    groups:\n      production-dependencies:\n        dependency-type: \"production\"\n      development-dependencies:\n        dependency-type: \"development\"\n    open-pull-requests-limit: 2\n"
  },
  {
    "path": ".github/scripts/backport.sh",
    "content": "#!/usr/bin/env bash\n#\n# Copyright 2024 Bjorn Neergaard\n#\n# This work is licensed under the terms of the MIT license.\n# For a copy, see <https://opensource.org/licenses/MIT>.\n#\n# This script is used to automatically create backport pull requests. It is\n# smart enough to require no arguments if run against a PR branch, assuming the\n# upstream repo conforms to the default prefixes. Target branches will be\n# determined by labels present on the original pull request.\n#\n# It is capable of handling PRs merged with a commit, by rebase and by\n# squash-and-rebase. The backport pull request will have its title, body and\n# labels derived from the original. The cherry-picked comments can be signed off,\n# and a comment will be created if requested.\n#\n# In particular, this script assumes the 'origin' remote points to the target\n# repository for backports. We also assume we can freely clobber local and remote\n# branches using our backport branch naming scheme and that you don't mind if we\n# prune your worktrees for you.\n#\n# This script can push the backport branches to a fork if a corresponding\n# --remote is passed.\n\nbasename=${0##*/}\n\n# Check for Homebrew-installed getopt on macOS.\nfor g in /{usr/local,opt/homebrew}/opt/gnu-getopt/bin/getopt; do\n    test -x \"$g\" && : GETOPT=\"${GETOPT:=$g}\" && break\ndone || : GETOPT=\"${GETOPT:=getopt}\"\n\n\"${GETOPT}\" --test >/dev/null\nif [ $? -ne 4 ]; then\n    printf >&2 '%s: GNU getopt is required, please ensure it is available in the PATH\\n' \"$basename\"\n    exit 1\nfi\n\nif ! command -v gh >/dev/null; then\n    printf >&2 \"%s: the GitHub CLI (\\`gh') is required, please ensure it is available in the PATH\\n\" \"$basename\"\nfi\n\nusage() {\n    printf >&2 '%s -h|--help\\n' \"$basename\"\n    printf >&2 '%s [-s|--signoff] [-c|--comment] --pr [pr] --remote [remote] --branch-prefix [prefix] --label-prefix [prefix]\\n' \"$basename\"\n}\n\nargs=\"$(\"${GETOPT}\" -o h,s,c -l help,signoff,comment,pr:,remote:,branch-prefix:,label-prefix: -n \"$basename\" -- \"$@\")\" || exit $?\neval set -- \"$args\"\nunset args\n\nwhile [ \"$#\" -gt 0 ]; do\n    case \"$1\" in\n    --pr | --remote | --branch-prefix | --label-prefix)\n        flag=\"${1:2}\"\n        printf -v \"${flag//-/_}\" '%s' \"$2\"\n        shift 2\n        ;;\n    -s | --signoff)\n        signoff=1\n        shift\n        ;;\n    -c | --comment)\n        comment=1\n        shift\n        ;;\n    -h | --help)\n        usage\n        exit\n        ;;\n    -- | *)\n        shift\n        break\n        ;;\n    esac\ndone\n\nset -eux -o pipefail\n\n# Determine the number of the target pull request, if not already supplied.\n: pr=\"${pr:=$(gh pr view --json number --jq '.number')}\"\n\n# Use the 'origin' remote by default; if a fork is desired, a corresponding remote should\n# be specified, e.g. `gh repo fork --remote-name fork` and `--remote fork`.\n: remote=\"${remote:=origin}\"\n\n# Use 'backport/' as a default prefix for both the resulting branch and the triggering label.\n: branch_prefix=\"${branch_prefix:=backport/}\"\n: label_prefix=\"${label_prefix:=backport/}\"\n\n# Determine the owner of the target remote (necessary to open a pull request) based on the URL.\nremote_owner=$(basename \"$(dirname \"$(git remote get-url --push \"$remote\")\")\")\n\n# Get the state, base branch and merge commit (if it exists) of the pull request.\npr_meta=$(gh pr view --json state,baseRefName,mergeCommit --jq '[.state,.baseRefName,.mergeCommit.oid][]' \"$pr\")\npr_state=$(sed '1q;d' <<<\"$pr_meta\")\npr_base=$(sed '2q;d' <<<\"$pr_meta\")\npr_mergecommit=$(sed '3q;d' <<<\"$pr_meta\")\n\n# Get the list of commits present in the pull request.\npr_commits=$(gh pr view --json commits --jq '.commits[].oid' \"$pr\")\n\n# Get the title and body of the pull request.\npr_title_body=$(gh pr view --json title,body --jq '[.title,.body][]' \"$pr\")\npr_title=$(head -n 1 <<<\"$pr_title_body\")\npr_body=$(tail -n +2 <<<\"$pr_title_body\")\n# Gather the list of labels on the pull request.\npr_labels=$(gh pr view --json labels --jq '.labels[].name' \"$pr\")\n\n# Fetch origin, to ensure we have the latest commits on all upstream branches.\ngit fetch origin\n# Fetch the latest pull request head, to ensure we have all commits available locally.\n# It will be available as FETCH_HEAD for the remainder of this script.\ngit fetch origin \"refs/pull/${pr}/head\"\n\n# Determine which commits should be cherry-picked. This can be surprisingly complex,\n# but the typical cases present on GitHub are handled here.\nif [ \"$pr_state\" = OPEN ] || [ \"$(git rev-list --no-walk --count --merges \"$pr_mergecommit\")\" -eq 1 ]; then\n    # Unmerged, or merge commit: the list of commits is equivalent to the pull request.\n    backport_commits=$pr_commits\nelse\n    # The cherry commits represent those commits that were cherry-picked from the pull request to the base.\n    pr_cherry_commits=$(git cherry refs/remotes/origin/main FETCH_HEAD | sed -n '/^- / s/- //p')\n    # The rebased commits represent those commits present in the base that correspond to the pull request.\n    pr_rebased_commits=$(git cherry FETCH_HEAD refs/remotes/origin/main | sed -n '/^- / s/- //p')\n\n    # Look for cherry-picks (which is what a conflict-free and non-interactive rebase merge\n    # effectively does). Note that a squash confuses the list of rebased commits;\n    # to make our heuristics as effective as possible, we have two checks:\n    # * Git must successfully identify the list of commits cherry-picked from the PR.\n    # * The number of commits in the pull request and identified for backport must match.\n    if [ \"$pr_cherry_commits\" = \"$pr_commits\" ] \\\n        && [ \"$(wc -l <<<\"$pr_rebased_commits\")\" -eq \"$(wc -l <<<\"$pr_commits\")\" ]; then\n        # Rebase: the list of commits is those rebased into the base branch.\n        backport_commits=$pr_rebased_commits\n    else\n        # Squash-and-rebase: the list of commits is the singular merged commit.\n        backport_commits=$pr_mergecommit\n    fi\nfi\n\n# Create a temporary directory in which to hold worktrees for each backport attempt.\nworkdir=\"$(mktemp -d)\"\ntrap 'rm -rf \"${workdir}\"; git worktree prune -v' EXIT\n\n# Create some arrays to track success and failure.\nbackport_urls=()\nfailed_backports=()\n\n# Iterate over all labels matching the prefix to determine what branches must be backported.\nwhile IFS= read -r backport_label; do\n    target_branch=\"${backport_label/#${label_prefix}}\"\n    backport_branch=\"${branch_prefix}${pr}-${target_branch}\"\n\n    # Check that the target branch and base branch are not the same. This heads off some\n    # potential errors.\n    if [ \"$target_branch\" = \"$pr_base\" ]; then\n        continue\n    fi\n\n    # Create a new backport branch, in a new worktree, based on the target branch.\n    backport_worktree=\"${workdir}/${backport_branch}\"\n    git worktree add -B \"$backport_branch\" \"$backport_worktree\" \"refs/remotes/origin/${target_branch}\"\n\n    # Cherry-pick the commits from the target branch in order.\n    for commit in $backport_commits; do\n        if ! git -C \"$backport_worktree\" cherry-pick -x ${signoff:+-s} \"$commit\"; then\n            # If a cherry-pick fails, record the branch and move on.\n            failed_backports+=(\"$target_branch\")\n            continue 2\n        fi\n    done\n\n    # Push the resulting backport branch to the configured remote.\n    git push -f \"$remote\" \"$backport_branch\"\n\n    # Create a derived title and label for the PR.\n    backport_title=\"[${target_branch} backport] ${pr_title}\"\n    backport_body=\"Backport #${pr} to ${target_branch}.\"\n    if [ -n \"$pr_body\" ]; then\n        backport_body+=$'\\n\\n'\"---\"$'\\n\\n'\"$pr_body\"\n    fi\n    # Determine which labels should be brought over to the new pull request, formatted as the CLI expects.\n    backport_labels=$(grep -v \"^${label_prefix}\" <<<\"$pr_labels\" | head -c -1 | tr '\\n' ',')\n\n    # Check for any open backports; note this is a heuristic as we just grab the first pull request\n    # that matches our generated branch name. This is unlikely to fail as we filter by author, however.\n    backport_url=$(gh pr list --author '@me' --head \"$backport_branch\" --json url --jq 'first(.[].url)')\n    if [ -n \"$backport_url\" ]; then\n        # Update the pull request title and body.\n        # TODO: update labels?\n        gh pr edit \"$backport_url\" --title \"$backport_title\" --body \"$backport_body\"\n        found_backport=1\n    else\n        # Create a new pull request from the backport branch, against the target branch.\n        backport_url=$(gh pr create --base \"$target_branch\" --head \"${remote_owner}:${backport_branch}\" \\\n            --title \"$backport_title\" --body-file - \\\n            --label \"$backport_labels\" \\\n            <<<\"$backport_body\" | tail -n 1)\n    fi\n\n    # Track this successful backport.\n    backport_urls+=(\"$backport_url\")\ndone < <(grep \"^${label_prefix}\" <<<\"$pr_labels\")\n\nif [ -n \"${comment:-}\" ]; then\n    # Generate a comment on the original PR, recording what backports we opened (or failed to open).\n    if [ \"${#backport_urls[@]}\" -gt 0 ]; then\n        comment_body+=\"Automated backport PRs opened:\"$'\\n'\n        for backport_url in \"${backport_urls[@]}\"; do\n            comment_body+=\"* ${backport_url}\"$'\\n'\n        done\n        comment_body+=$'\\n'\n    fi\n\n    if [ \"${#failed_backports[@]}\" -gt 0 ]; then\n        comment_body+=\"Backports failed on the following branches:\"\n        for failed_backport in \"${failed_backports[@]}\"; do\n            comment_body+=\"* ${failed_backport}\"$'\\n'\n        done\n        comment_body+=$'\\n'\n\n        # If we're running in GitHub actions, link to the run log to diagnose why a backport failed.\n        if [ -n \"${GITHUB_ACTIONS:-}\" ]; then\n            comment_body+=\"Inspect the run at ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\"\n        fi\n    fi\n\n    if [ -n \"$comment_body\" ]; then\n        # If we had any matches for an existing PR, we'll go ahead and assume we already commented.\n        # Edit the existing comment instead.\n        gh pr comment \"$pr\" ${found_backport:+--edit-last} --body-file - <<<\"$comment_body\"\n    fi\nfi\n"
  },
  {
    "path": ".github/workflows/.tests-matrix.yaml",
    "content": "# Reusable workflow consumed by tests.yaml; used to share a single matrix across jobs.\non:\n  workflow_call:\n    inputs:\n      runner:\n        required: true\n        type: string\n      python-version:\n        required: true\n        type: string\n      run-mypy:\n        required: true\n        type: boolean\n      run-pytest:\n        required: true\n        type: boolean\n      run-pytest-export:\n        required: true\n        type: boolean\n\ndefaults:\n  run:\n    shell: bash\n\nenv:\n  PYTHONWARNDEFAULTENCODING: 'true'\n\njobs:\n  mypy:\n    name: mypy\n    runs-on: ${{ inputs.runner }}\n    if: inputs.run-mypy\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: ./.github/actions/bootstrap-poetry\n        id: bootstrap-poetry\n        with:\n          python-version: ${{ inputs.python-version }}\n\n      - uses: ./.github/actions/poetry-install\n\n      - uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2\n        with:\n          path: .mypy_cache\n          key: mypy-${{ runner.os }}-py${{ steps.bootstrap-poetry.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'poetry.lock') }}\n          restore-keys: |\n            mypy-${{ runner.os }}-py${{ steps.bootstrap-poetry.outputs.python-version }}-\n            mypy-${{ runner.os }}-\n\n      - run: poetry run mypy\n\n  pytest:\n    name: pytest\n    runs-on: ${{ inputs.runner }}\n    if: inputs.run-pytest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: ./.github/actions/bootstrap-poetry\n        with:\n          python-version: ${{ inputs.python-version }}\n\n      - uses: ./.github/actions/poetry-install\n        with:\n          args: --with github-actions\n\n      - run: poetry run pytest --integration -v\n        env:\n          POETRY_TEST_INTEGRATION_GIT_USERNAME: ${{ github.actor }}\n          POETRY_TEST_INTEGRATION_GIT_PASSWORD: ${{ github.token }}\n\n      - run: git diff --exit-code --stat HEAD\n\n  pytest-export:\n    name: pytest (poetry-plugin-export)\n    runs-on: ${{ inputs.runner }}\n    if: inputs.run-pytest-export\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          path: poetry\n\n      - uses: ./poetry/.github/actions/bootstrap-poetry\n        with:\n          python-version: ${{ inputs.python-version }}\n\n      - name: Get poetry-plugin-export version\n        run: |\n          PLUGIN_VERSION=$(curl -s https://pypi.org/pypi/poetry-plugin-export/json | jq -r \".info.version\")\n          echo \"Found version ${PLUGIN_VERSION}\"\n          echo version=${PLUGIN_VERSION} >> $GITHUB_OUTPUT\n        id: poetry-plugin-export-version\n\n      - name: Check out poetry-plugin-export\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          path: poetry-plugin-export\n          repository: python-poetry/poetry-plugin-export\n          # use main for now because of poetry-core#826\n          # ref: refs/tags/${{ steps.poetry-plugin-export-version.outputs.version }}\n\n      - name: Use local poetry\n        working-directory: poetry-plugin-export\n        # Replace the python version to avoid conflicts\n        # if the plugin still supports a wider range than Poetry itself.\n        run: |\n          perl -pi -e 's/^requires-python =.*$/requires-python = \"~='\"${PYTHON_VERSION}\"'\"/' pyproject.toml\n          poetry remove --lock poetry-core  # use whatever poetry uses\n          poetry add --lock --group dev ../poetry\n        env:\n          PYTHON_VERSION: ${{ inputs.python-version }}\n\n      # This step can be removed after having released a poetry-plugin-export version\n      # that has cffi>=1.17.0 in its lock file.\n      - name: Force more recent cffi (workaround for Python 3.13)\n        working-directory: poetry-plugin-export\n        run: poetry update --lock cffi\n\n      - name: Install\n        working-directory: poetry-plugin-export\n        run: poetry install\n\n      - name: Run tests\n        working-directory: poetry-plugin-export\n        run: poetry run pytest -v\n\n      - name: Check for clean working tree\n        working-directory: poetry-plugin-export\n        run: |\n          git checkout -- pyproject.toml poetry.lock\n          git diff --exit-code --stat HEAD\n"
  },
  {
    "path": ".github/workflows/backport.yaml",
    "content": "name: Backport\n\non:\n  pull_request_target:  # zizmor: ignore[dangerous-triggers]\n    types:\n      - closed\n      - labeled\n\n# we create the token we need later on\npermissions: {}\n\njobs:\n  backport:\n    name: Create backport\n    runs-on: ubuntu-latest\n    # This workflow only applies to merged PRs; and triggers on a PR being closed, or the backport label being applied.\n    # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.\n    if: >\n      github.event.pull_request.merged\n      && (\n        github.event.action == 'closed'\n        ||\n          (github.event.action == 'labeled' && contains(github.event.label.name, 'backport/')\n        )\n      )\n    steps:\n      - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1\n        id: app-token\n        with:\n          app-id: ${{ secrets.POETRY_TOKEN_APP_ID }}\n          private-key: ${{ secrets.POETRY_TOKEN_APP_KEY }}\n      - uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2.0.4\n        with:\n          github_token: ${{ steps.app-token.outputs.token }}\n          title_template: \"[<%= base %>] <%= title %>\"\n          label_pattern: \"^backport/(?<base>([^ ]+))$\"\n"
  },
  {
    "path": ".github/workflows/docs.yaml",
    "content": "name: Documentation Preview\n\non:\n  pull_request:\n    # allow repository maintainers to modify and test workflow\n    paths:\n      - \".github/workflows/docs.yaml\"\n  pull_request_target:  # zizmor: ignore[dangerous-triggers]\n    # enable runs for this workflow when labeled as documentation only\n    # prevent execution when the workflow itself is modified from a fork\n    types:\n      - labeled\n      - synchronize\n    paths:\n      - \"docs/**\"\n\njobs:\n  deploy:\n    name: Build & Deploy\n    runs-on: ubuntu-latest\n    if: >\n      (github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'impact/docs'))\n      || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)\n    permissions:\n      contents: read\n      pull-requests: write\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          repository: python-poetry/website\n\n      # use .github from pull request target instead of pull_request.head\n      # for pull_request_target trigger to avoid arbitrary code execution\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          path: poetry-github\n          sparse-checkout: .github\n\n      # only checkout docs from pull_request.head to not use something else by accident\n      # for pull_request_target trigger (security)\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          path: poetry-docs\n          ref: ${{ github.event.pull_request.head.sha }}\n          sparse-checkout: docs\n\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: \"18\"\n\n      - uses: ./poetry-github/.github/actions/bootstrap-poetry\n\n      - uses: ./poetry-github/.github/actions/poetry-install\n        with:\n          args: --no-root --only main\n\n      - name: website-build\n        run: |\n          # Rebuild the docs files from the PR checkout.\n          poetry run python bin/website build --local ./poetry-docs\n          # Build website assets (CSS/JS).\n          npm ci && npm run prod\n          # Build the static website.\n          npx hugo --minify --logLevel info\n\n      - uses: amondnet/vercel-action@888da851026e0573da056b061931bcb765a915c4 # v41.1.4\n        with:\n          vercel-version: 41.1.4\n          vercel-token: ${{ secrets.VERCEL_TOKEN }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}\n          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}\n          scope: python-poetry\n          github-comment: true\n          working-directory: public\n"
  },
  {
    "path": ".github/workflows/lock-threads.yaml",
    "content": "name: Lock Threads\n\non:\n  schedule:\n    - cron: '0 0 * * *' # every day at midnight\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.workflow }}\n\njobs:\n  lock-issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0\n        with:\n          process-only: issues\n          issue-inactive-days: 30\n          issue-comment: >\n            This issue has been automatically locked since there\n            has not been any recent activity after it was closed.\n            Please open a new issue for related bugs.\n\n  lock-prs:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0\n        with:\n          process-only: prs\n          pr-inactive-days: 30\n          pr-comment: >\n            This pull request has been automatically locked since there\n            has not been any recent activity after it was closed.\n            Please open a new issue for related bugs.\n"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: Release\n\non:\n  release:\n    types: [published]\n\npermissions: {}\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - run: pipx run build\n\n      - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: distfiles\n          path: dist/\n          if-no-files-found: error\n\n  upload-github:\n    name: Upload (GitHub)\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    needs: build\n    steps:\n      # We need to be in a git repo for gh to work.\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0\n        with:\n          name: distfiles\n          path: dist/\n\n      - run: gh release upload \"${TAG_NAME}\" dist/*.{tar.gz,whl}\n        env:\n          GH_TOKEN: ${{ github.token }}\n          TAG_NAME: ${{ github.event.release.tag_name }}\n\n  upload-pypi:\n    name: Upload (PyPI)\n    runs-on: ubuntu-latest\n    environment:\n      name: pypi\n      url: https://pypi.org/project/poetry/\n    permissions:\n      id-token: write\n    needs: build\n    steps:\n      - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0\n        with:\n          name: distfiles\n          path: dist/\n\n      - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0\n        with:\n          print-hash: true\n"
  },
  {
    "path": ".github/workflows/tests.yaml",
    "content": "name: Tests\n\non:\n  push:\n  pull_request:\n  merge_group:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}\n  cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\ndefaults:\n  run:\n    shell: bash\n\npermissions: {}\n\njobs:\n  changes:\n    name: Detect changed files\n    runs-on: ubuntu-latest\n    outputs:\n      project: ${{ steps.changes.outputs.project }}\n      fixtures-pypi: ${{ steps.changes.outputs.fixtures-pypi }}\n      src: ${{ steps.changes.outputs.src }}\n      tests: ${{ steps.changes.outputs.tests }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2\n        id: changes\n        with:\n          filters: |\n            workflow: &workflow\n              - '.github/actions/**'\n              - '.github/workflows/tests.yaml'\n              - '.github/workflows/.tests-matrix.yaml'\n            project: &project\n              - *workflow\n              - 'poetry.lock'\n              - 'pyproject.toml'\n            fixtures-pypi:\n              - *workflow\n              - 'tests/repositories/fixtures/pypi.org/**'\n            src:\n              - *project\n              - 'src/**/*.py'\n            tests:\n              - *project\n              - 'src/**/*.py'\n              - 'tests/**'\n\n  lockfile:\n    name: Check poetry.lock\n    runs-on: ubuntu-latest\n    if: needs.changes.outputs.project == 'true'\n    needs: changes\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: ./.github/actions/bootstrap-poetry\n\n      - run: poetry check --lock\n\n  smoke:\n    name: Smoke-test build and install\n    runs-on: ubuntu-latest\n    if: needs.changes.outputs.project == 'true'\n    needs: lockfile\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - run: pipx run build\n\n      - run: pipx run twine check --strict dist/*\n\n      - run: pipx install --suffix=@build dist/*.whl\n\n      - uses: ./.github/actions/bootstrap-poetry\n\n      # Smoke test: confirm the version of the installed wheel matches the project.\n      - run: poetry@build --version | grep $(poetry version --short)\n\n  fixtures-pypi:\n    name: Check fixtures (PyPI)\n    runs-on: ubuntu-latest\n    if: needs.changes.outputs.fixtures-pypi == 'true'\n    needs: changes\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - uses: ./.github/actions/bootstrap-poetry\n\n      - uses: ./.github/actions/poetry-install\n        with:\n          args: --only main,test\n\n      - run: poetry run env PYTHONPATH=\"$GITHUB_WORKSPACE\" python tests/repositories/fixtures/pypi.org/generate.py\n\n      - run: git diff --exit-code --stat HEAD tests/repositories/fixtures/pypi.org\n\n  tests-matrix:\n    # Use this matrix with multiple jobs defined in a reusable workflow:\n    uses: ./.github/workflows/.tests-matrix.yaml\n    name: \"${{ matrix.os.name }} (Python ${{ matrix.python-version }})\"\n    if: '!failure()'\n    needs:\n      - lockfile\n      - changes\n    with:\n      runner: ${{ matrix.os.image }}\n      python-version: ${{ matrix.python-version }}\n      run-mypy: ${{ needs.changes.outputs.tests == 'true' }}\n      run-pytest: ${{ needs.changes.outputs.tests == 'true' }}\n      run-pytest-export: ${{ needs.changes.outputs.src == 'true' }}\n    secrets: inherit  # zizmor: ignore[secrets-inherit]\n    strategy:\n      matrix:\n        os:\n          - name: Ubuntu\n            image: ubuntu-22.04\n          - name: Windows\n            image: windows-2022\n          - name: macOS aarch64\n            image: macos-14\n        python-version:\n          - \"3.10\"\n          - \"3.11\"\n          - \"3.12\"\n          - \"3.13\"\n          - \"3.14\"\n      fail-fast: false\n\n  status:\n    name: Status\n    runs-on: ubuntu-latest\n    if: always()\n    needs:\n      - lockfile\n      - smoke\n      - fixtures-pypi\n      - tests-matrix\n    steps:\n      - run: ${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 'false' || 'true' }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.pyc\n\n# Packages\n/dist/*\n\n# Unit test / coverage reports\n.coverage\n.pytest_cache\n\n.DS_Store\n.idea/*\n.python-version\n.vscode/*\n\n/docs/site/*\n.mypy_cache\n\n.venv\n/poetry.toml\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "ci:\n  autofix_prs: false\n\nrepos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: trailing-whitespace\n        exclude: tests/repositories/fixtures/pypi.org/metadata/.*\\.metadata\n      - id: end-of-file-fixer\n        exclude: ^.*\\.egg-info/|tests/repositories/fixtures/pypi.org/metadata/.*\\.metadata\n      - id: check-merge-conflict\n        exclude: tests/repositories/fixtures/installed/vendor/py3.7/attrs-19.3.0.dist-info/METADATA\n      - id: check-case-conflict\n      - id: check-json\n      - id: check-toml\n        exclude: tests/fixtures/invalid_lock/poetry\\.lock\n      - id: check-yaml\n      - id: pretty-format-json\n        args: [--autofix, --no-ensure-ascii, --no-sort-keys]\n      - id: check-ast\n      - id: debug-statements\n      - id: check-docstring-first\n\n  - repo: https://github.com/pre-commit/pre-commit\n    rev: v4.5.1\n    hooks:\n      - id: validate_manifest\n\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    rev: v0.15.4\n    hooks:\n      - id: ruff-check\n      - id: ruff-format\n\n  - repo: https://github.com/woodruffw/zizmor-pre-commit\n    rev: v1.22.0\n    hooks:\n      - id: zizmor\n"
  },
  {
    "path": ".pre-commit-hooks.yaml",
    "content": "- id: poetry-check\n  name: poetry-check\n  description: run poetry check to validate config\n  entry: poetry check\n  language: python\n  pass_filenames: false\n  files: ^(.*/)?(poetry\\.lock|pyproject\\.toml)$\n\n- id: poetry-lock\n  name: poetry-lock\n  description: run poetry lock to update lock file\n  entry: poetry lock\n  language: python\n  pass_filenames: false\n  files: ^(.*/)?(poetry\\.lock|pyproject\\.toml)$\n\n- id: poetry-install\n  name: poetry-install\n  description: run poetry install to install dependencies from the lock file\n  entry: poetry install\n  language: python\n  pass_filenames: false\n  stages: [post-checkout, post-merge]\n  always_run: true\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## [2.3.2] - 2026-02-01\n\n### Changed\n\n- Allow `dulwich>=1.0` ([#10701](https://github.com/python-poetry/poetry/pull/10701)).\n\n### poetry-core ([`2.3.1`](https://github.com/python-poetry/poetry-core/releases/tag/2.3.1))\n\n- Fix an issue where `platform_release` could not be parsed on Windows Server ([#911](https://github.com/python-poetry/poetry-core/pull/911)).\n\n\n## [2.3.1] - 2026-01-20\n\n### Fixed\n\n- Fix an issue where cached information about each package was always considered outdated ([#10699](https://github.com/python-poetry/poetry/pull/10699)).\n\n### Docs\n\n- Document SHELL_VERBOSITY environment variable ([#10678](https://github.com/python-poetry/poetry/pull/10678)).\n\n\n## [2.3.0] - 2026-01-18\n\n### Added\n\n- **Add support for exporting `pylock.toml` files with `poetry-plugin-export`** ([#10677](https://github.com/python-poetry/poetry/pull/10677)).\n- Add support for specifying build constraints for dependencies ([#10388](https://github.com/python-poetry/poetry/pull/10388)).\n- Add support for publishing artifacts whose version is determined dynamically by the build-backend ([#10644](https://github.com/python-poetry/poetry/pull/10644)).\n- Add support for editable project plugins ([#10661](https://github.com/python-poetry/poetry/pull/10661)).\n- Check `requires-poetry` before any other validation ([#10593](https://github.com/python-poetry/poetry/pull/10593)).\n- Validate the content of `project.readme` when running `poetry check` ([#10604](https://github.com/python-poetry/poetry/pull/10604)).\n- Add the option to clear all caches by making the cache name in `poetry cache clear` optional ([#10627](https://github.com/python-poetry/poetry/pull/10627)).\n- Automatically update the cache for packages where the locked files differ from cached files ([#10657](https://github.com/python-poetry/poetry/pull/10657)).\n- Suggest to clear the cache if running a command with `--no-cache` solves an issue ([#10585](https://github.com/python-poetry/poetry/pull/10585)).\n- Propose `poetry init` when trying `poetry new` for an existing directory ([#10563](https://github.com/python-poetry/poetry/pull/10563)).\n- Add support for `poetry publish --skip-existing` for new Nexus OSS versions ([#10603](https://github.com/python-poetry/poetry/pull/10603)).\n- Show Poetry's own Python's path in `poetry debug info` ([#10588](https://github.com/python-poetry/poetry/pull/10588)).\n\n### Changed\n\n- **Drop support for Python 3.9** ([#10634](https://github.com/python-poetry/poetry/pull/10634)).\n- **Change the default of `installer.re-resolve` from `true` to `false`** ([#10622](https://github.com/python-poetry/poetry/pull/10622)).\n- **PEP 735 dependency groups are considered in the lock file hash** ([#10621](https://github.com/python-poetry/poetry/pull/10621)).\n- Deprecate `poetry.utils._compat.metadata`, which is sometimes used in plugins, in favor of `importlib.metadata` ([#10634](https://github.com/python-poetry/poetry/pull/10634)).\n- Improve managing free-threaded Python versions with `poetry python` ([#10606](https://github.com/python-poetry/poetry/pull/10606)).\n- Prefer JSON API to HTML API in legacy repositories ([#10672](https://github.com/python-poetry/poetry/pull/10672)).\n- When running `poetry init`, only add the readme field in the `pyproject.toml` if the readme file exists ([#10679](https://github.com/python-poetry/poetry/pull/10679)).\n- Raise an error if no hash can be determined for any distribution link of a package ([#10673](https://github.com/python-poetry/poetry/pull/10673)).\n- Require `dulwich>=0.25.0` ([#10674](https://github.com/python-poetry/poetry/pull/10674)).\n\n### Fixed\n\n- Fix an issue where `poetry remove` did not work for PEP 735 dependency groups with `include-group` items ([#10587](https://github.com/python-poetry/poetry/pull/10587)).\n- Fix an issue where `poetry remove` caused dangling `include-group` references in PEP 735 dependency groups ([#10590](https://github.com/python-poetry/poetry/pull/10590)).\n- Fix an issue where `poetry add` did not work for PEP 735 dependency groups with `include-group` items ([#10636](https://github.com/python-poetry/poetry/pull/10636)).\n- Fix an issue where PEP 735 dependency groups were not considered in the lock file hash ([#10621](https://github.com/python-poetry/poetry/pull/10621)).\n- Fix an issue where wrong markers were locked for a dependency that was required by several groups with different markers ([#10613](https://github.com/python-poetry/poetry/pull/10613)).\n- Fix an issue where non-deterministic markers were created in a method used by `poetry-plugin-export` ([#10667](https://github.com/python-poetry/poetry/pull/10667)).\n- Fix an issue where wrong wheels were chosen for installation in free-threaded Python environments if Poetry itself was not installed with free-threaded Python ([#10614](https://github.com/python-poetry/poetry/pull/10614)).\n- Fix an issue where `poetry publish` used the metadata of the project instead of the metadata of the build artifact ([#10624](https://github.com/python-poetry/poetry/pull/10624)).\n- Fix an issue where `poetry env use` just used another Python version instead of failing when the requested version was not supported by the project ([#10685](https://github.com/python-poetry/poetry/pull/10685)).\n- Fix an issue where `poetry env activate` returned the wrong command for `dash` ([#10696](https://github.com/python-poetry/poetry/pull/10696)).\n- Fix an issue where `data-dir` and `python.installation-dir` could not be set ([#10595](https://github.com/python-poetry/poetry/pull/10595)).\n- Fix an issue where Python and pip executables were not correctly detected on Windows ([#10645](https://github.com/python-poetry/poetry/pull/10645)).\n- Fix an issue where invalid template variables in `virtualenvs.prompt` caused an incomprehensible error message ([#10648](https://github.com/python-poetry/poetry/pull/10648)).\n\n### Docs\n\n- Add a warning about `~/.netrc` for Poetry credential configuration ([#10630](https://github.com/python-poetry/poetry/pull/10630)).\n- Clarify that the local configuration takes precedence over the global configuration ([#10676](https://github.com/python-poetry/poetry/pull/10676)).\n- Add an explanation in which cases `packages` are automatically detected ([#10680](https://github.com/python-poetry/poetry/pull/10680)).\n\n### poetry-core ([`2.3.0`](https://github.com/python-poetry/poetry-core/releases/tag/2.3.0))\n\n- Normalize versions ([#893](https://github.com/python-poetry/poetry-core/pull/893)).\n- Fix an issue where unsatisfiable requirements did not raise an error ([#891](https://github.com/python-poetry/poetry-core/pull/891)).\n- Fix an issue where the implicit main group did not exist if it was explicitly declared as not having any dependencies ([#892](https://github.com/python-poetry/poetry-core/pull/892)).\n- Fix an issue where `python_full_version` markers with pre-release versions were parsed incorrectly ([#893](https://github.com/python-poetry/poetry-core/pull/893)).\n\n\n## [2.2.1] - 2025-09-21\n\n### Fixed\n\n- Fix an issue where `poetry self show` failed with a message about an invalid output format ([#10560](https://github.com/python-poetry/poetry/pull/10560)).\n\n### Docs\n\n- Remove outdated statements about dependency groups ([#10561](https://github.com/python-poetry/poetry/pull/10561)).\n\n### poetry-core ([`2.2.1`](https://github.com/python-poetry/poetry-core/releases/tag/2.2.1))\n\n- Fix an issue where it was not possible to declare a PEP 735 dependency group as optional ([#888](https://github.com/python-poetry/poetry-core/pull/888)).\n\n\n## [2.2.0] - 2025-09-14\n\n### Added\n\n- **Add support for nesting dependency groups** ([#10166](https://github.com/python-poetry/poetry/pull/10166)).\n- **Add support for PEP 735 dependency groups** ([#10130](https://github.com/python-poetry/poetry/pull/10130)).\n- **Add support for PEP 639 license clarity** ([#10413](https://github.com/python-poetry/poetry/pull/10413)).\n- Add a `--format` option to `poetry show` to alternatively output json format ([#10487](https://github.com/python-poetry/poetry/pull/10487)).\n- Add official support for Python 3.14 ([#10514](https://github.com/python-poetry/poetry/pull/10514)).\n\n### Changed\n\n- **Normalize dependency group names** ([#10387](https://github.com/python-poetry/poetry/pull/10387)).\n- Change `installer.no-binary` and `installer.only-binary` so that explicit package names will take precedence over `:all:` ([#10278](https://github.com/python-poetry/poetry/pull/10278)).\n- Improve log output during `poetry install` when a wheel is built from source ([#10404](https://github.com/python-poetry/poetry/pull/10404)).\n- Improve error message in case a file lock could not be acquired while cloning a git repository ([#10535](https://github.com/python-poetry/poetry/pull/10535)).\n- Require `dulwich>=0.24.0` ([#10492](https://github.com/python-poetry/poetry/pull/10492)).\n- Allow `virtualenv>=20.33` again ([#10506](https://github.com/python-poetry/poetry/pull/10506)).\n- Allow `findpython>=0.7` ([#10510](https://github.com/python-poetry/poetry/pull/10510)).\n- Allow `importlib-metadata>=8.7` ([#10511](https://github.com/python-poetry/poetry/pull/10511)).\n\n### Fixed\n\n- Fix an issue where `poetry new` did not create the project structure in an existing empty directory ([#10431](https://github.com/python-poetry/poetry/pull/10431)).\n- Fix an issue where a dependency that was required for a specific Python version was not installed into an environment of a pre-release Python version ([#10516](https://github.com/python-poetry/poetry/pull/10516)).\n\n### poetry-core ([`2.2.0`](https://github.com/python-poetry/poetry-core/releases/tag/2.2.0))\n\n- Deprecate table values and values that are not valid SPDX expressions for `[project.license]` ([#870](https://github.com/python-poetry/poetry-core/pull/870)).\n- Fix an issue where explicitly included files that are in `.gitignore` were not included in the distribution ([#874](https://github.com/python-poetry/poetry-core/pull/874)).\n- Fix an issue where marker operations could result in invalid markers ([#875](https://github.com/python-poetry/poetry-core/pull/875)).\n\n\n## [2.1.4] - 2025-08-05\n\n### Changed\n\n- Require `virtualenv<20.33` to work around an issue where Poetry uses the wrong Python version ([#10491](https://github.com/python-poetry/poetry/pull/10491)).\n- Improve the error messages for the validation of the `pyproject.toml` file ([#10471](https://github.com/python-poetry/poetry/pull/10471)).\n\n### Fixed\n\n- Fix an issue where project plugins were installed even though `poetry install` was called with `--no-plugins` ([#10405](https://github.com/python-poetry/poetry/pull/10405)).\n- Fix an issue where dependency resolution failed for self-referential extras with duplicate dependencies ([#10488](https://github.com/python-poetry/poetry/pull/10488)).\n\n### Docs\n\n- Clarify how to include files that were automatically excluded via VCS ignore settings ([#10442](https://github.com/python-poetry/poetry/pull/10442)).\n- Clarify the behavior of `poetry add` if no version constraint is explicitly specified ([#10445](https://github.com/python-poetry/poetry/pull/10445)).\n\n\n## [2.1.3] - 2025-05-04\n\n### Changed\n\n- Require `importlib-metadata<8.7` for Python 3.9 because of a breaking change in importlib-metadata 8.7 ([#10374](https://github.com/python-poetry/poetry/pull/10374)).\n\n### Fixed\n\n- Fix an issue where re-locking failed for incomplete multiple-constraints dependencies with explicit sources ([#10324](https://github.com/python-poetry/poetry/pull/10324)).\n- Fix an issue where the `--directory` option did not work if a plugin, which accesses the poetry instance during its activation, was installed ([#10352](https://github.com/python-poetry/poetry/pull/10352)).\n- Fix an issue where `poetry env activate -v` printed additional information to stdout instead of stderr so that the output could not be used as designed ([#10353](https://github.com/python-poetry/poetry/pull/10353)).\n- Fix an issue where the original error was not printed if building a git dependency failed ([#10366](https://github.com/python-poetry/poetry/pull/10366)).\n- Fix an issue where wheels for the wrong platform were installed in rare cases. ([#10361](https://github.com/python-poetry/poetry/pull/10361)).\n\n### poetry-core ([`2.1.3`](https://github.com/python-poetry/poetry-core/releases/tag/2.1.3))\n\n- Fix an issue where the union of specific inverse or partially inverse markers was not simplified ([#858](https://github.com/python-poetry/poetry-core/pull/858)).\n- Fix an issue where optional dependencies defined in the `project` section were treated as non-optional when a source was defined for them in the `tool.poetry` section ([#857](https://github.com/python-poetry/poetry-core/pull/857)).\n- Fix an issue where markers with `===` were not parsed correctly ([#860](https://github.com/python-poetry/poetry-core/pull/860)).\n- Fix an issue where local versions with upper case letters caused an error ([#859](https://github.com/python-poetry/poetry-core/pull/859)).\n- Fix an issue where `extra` markers with a value starting with \"in\" were not validated correctly ([#862](https://github.com/python-poetry/poetry-core/pull/862)).\n\n\n## [2.1.2] - 2025-03-29\n\n### Changed\n\n- Improve performance of locking dependencies ([#10275](https://github.com/python-poetry/poetry/pull/10275)).\n\n### Fixed\n\n- Fix an issue where markers were not locked correctly ([#10240](https://github.com/python-poetry/poetry/pull/10240)).\n- Fix an issue where the result of `poetry lock` was not deterministic ([#10276](https://github.com/python-poetry/poetry/pull/10276)).\n- Fix an issue where `poetry env activate` returned the wrong command for `tcsh` ([#10243](https://github.com/python-poetry/poetry/pull/10243)).\n- Fix an issue where `poetry env activate` returned the wrong command for `pwsh` on Linux ([#10256](https://github.com/python-poetry/poetry/pull/10256)).\n\n### Docs\n\n- Update basic usage section to reflect new default layout ([#10203](https://github.com/python-poetry/poetry/pull/10203)).\n\n### poetry-core ([`2.1.2`](https://github.com/python-poetry/poetry-core/releases/tag/2.1.2))\n\n- Improve performance of marker operations ([#851](https://github.com/python-poetry/poetry-core/pull/851)).\n- Fix an issue where incorrect markers were calculated when removing parts covered by the project's Python constraint ([#841](https://github.com/python-poetry/poetry-core/pull/841),\n  [#846](https://github.com/python-poetry/poetry-core/pull/846)).\n- Fix an issue where `extra` markers were not simplified ([#842](https://github.com/python-poetry/poetry-core/pull/842),\n  [#845](https://github.com/python-poetry/poetry-core/pull/845),\n  [#847](https://github.com/python-poetry/poetry-core/pull/847)).\n- Fix an issue where the intersection and union of markers was not deterministic ([#843](https://github.com/python-poetry/poetry-core/pull/843)).\n- Fix an issue where the intersection of `python_version` markers was not recognized as empty ([#849](https://github.com/python-poetry/poetry-core/pull/849)).\n- Fix an issue where `python_version` markers were not simplified ([#848](https://github.com/python-poetry/poetry-core/pull/848),\n  [#851](https://github.com/python-poetry/poetry-core/pull/851)).\n- Fix an issue where Python constraints on a package were converted into invalid markers ([#853](https://github.com/python-poetry/poetry-core/pull/853)).\n\n\n## [2.1.1] - 2025-02-16\n\n### Fixed\n\n- Fix an issue where `poetry env use python` does not choose the Python from the PATH ([#10187](https://github.com/python-poetry/poetry/pull/10187)).\n\n### poetry-core ([`2.1.1`](https://github.com/python-poetry/poetry-core/releases/tag/2.1.1))\n\n- Fix an issue where simplifying a `python_version` marker resulted in an invalid marker ([#838](https://github.com/python-poetry/poetry-core/pull/838)).\n\n\n## [2.1.0] - 2025-02-15\n\n### Added\n\n- **Make `build` command build-system agnostic** ([#10059](https://github.com/python-poetry/poetry/pull/10059),\n  [#10092](https://github.com/python-poetry/poetry/pull/10092)).\n- Add a `--config-settings` option to `poetry build` ([#10059](https://github.com/python-poetry/poetry/pull/10059)).\n- Add support for defining `config-settings` when building dependencies ([#10129](https://github.com/python-poetry/poetry/pull/10129)).\n- **Add (experimental) commands to manage Python installations** ([#10112](https://github.com/python-poetry/poetry/pull/10112)).\n- Use `findpython` to find the Python interpreters ([#10097](https://github.com/python-poetry/poetry/pull/10097)).\n- Add a `--no-truncate` option to `poetry show` ([#9580](https://github.com/python-poetry/poetry/pull/9580)).\n- Re-add support for passwords with empty usernames ([#10088](https://github.com/python-poetry/poetry/pull/10088)).\n- Add better error messages ([#10053](https://github.com/python-poetry/poetry/pull/10053),\n  [#10065]( https://github.com/python-poetry/poetry/pull/10065),\n  [#10126](https://github.com/python-poetry/poetry/pull/10126),\n  [#10127](https://github.com/python-poetry/poetry/pull/10127),\n  [#10132](https://github.com/python-poetry/poetry/pull/10132)).\n\n### Changed\n\n- **`poetry new` defaults to \"src\" layout by default** ([#10135](https://github.com/python-poetry/poetry/pull/10135)).\n- Improve performance of locking dependencies ([#10111](https://github.com/python-poetry/poetry/pull/10111),\n  [#10114](https://github.com/python-poetry/poetry/pull/10114),\n  [#10138](https://github.com/python-poetry/poetry/pull/10138),\n  [#10146](https://github.com/python-poetry/poetry/pull/10146)).\n- Deprecate adding sources without specifying `--priority` ([#10134](https://github.com/python-poetry/poetry/pull/10134)).\n\n### Fixed\n\n- Fix an issue where global options were not handled correctly when positioned after command options ([#10021](https://github.com/python-poetry/poetry/pull/10021),\n  [#10067](https://github.com/python-poetry/poetry/pull/10067),\n  [#10128](https://github.com/python-poetry/poetry/pull/10128)).\n- Fix an issue where building a dependency from source failed because of a conflict between build-system dependencies that were not required for the target environment ([#10048](https://github.com/python-poetry/poetry/pull/10048)).\n- Fix an issue where `poetry init` was not able to find a package on PyPI while adding dependencies interactively ([#10055](https://github.com/python-poetry/poetry/pull/10055)).\n- Fix an issue where the `@latest` descriptor was incorrectly passed to the core requirement parser ([#10069](https://github.com/python-poetry/poetry/pull/10069)).\n- Fix an issue where Boolean environment variables set to `True` (in contrast to `true`) were interpreted as `false` ([#10080](https://github.com/python-poetry/poetry/pull/10080)).\n- Fix an issue where `poetry env activate` reported a misleading error message ([#10087](https://github.com/python-poetry/poetry/pull/10087)).\n- Fix an issue where adding an optional dependency with `poetry add --optional` would not correctly update the lock file ([#10076](https://github.com/python-poetry/poetry/pull/10076)).\n- Fix an issue where `pip` was not installed/updated before other dependencies resulting in a race condition ([#10102](https://github.com/python-poetry/poetry/pull/10102)).\n- Fix an issue where Poetry freezes when multiple threads attempt to unlock the `keyring` simultaneously ([#10062](https://github.com/python-poetry/poetry/pull/10062)).\n- Fix an issue where markers with extras were not locked correctly ([#10119](https://github.com/python-poetry/poetry/pull/10119)).\n- Fix an issue where self-referential extras were not resolved correctly ([#10106](https://github.com/python-poetry/poetry/pull/10106)).\n- Fix an issue where Poetry could not be run from a `zipapp` ([#10074](https://github.com/python-poetry/poetry/pull/10074)).\n- Fix an issue where installation failed with a permission error when using the system environment as a user without write access to system site packages ([#9014](https://github.com/python-poetry/poetry/pull/9014)).\n- Fix an issue where a version of a dependency that is not compatible with the project's python constraint was locked. ([#10141](https://github.com/python-poetry/poetry/pull/10141)).\n- Fix an issue where Poetry wrongly reported that the current project's supported Python range is not compatible with some of the required packages Python requirement ([#10157](https://github.com/python-poetry/poetry/pull/10157)).\n- Fix an issue where the requested extras of a dependency were ignored if the same dependency (with same extras) was specified in multiple groups ([#10158](https://github.com/python-poetry/poetry/pull/10158)).\n\n### Docs\n\n- Sort commands by name in the CLI reference ([#10035](https://github.com/python-poetry/poetry/pull/10035)).\n- Add missing documentation for `env` commands ([#10027](https://github.com/python-poetry/poetry/pull/10027)).\n- Clarify that the `name` and `version` fields are always required if the `project` section is specified ([#10033](https://github.com/python-poetry/poetry/pull/10033)).\n- Add a note about restarting the shell for tab completion changes to take effect ([#10070](https://github.com/python-poetry/poetry/pull/10070)).\n- Fix the example for `project.gui-scripts` [#10121](https://github.com/python-poetry/poetry/pull/10121).\n- Explain how to include files as scripts in the project configuration ([#9572](https://github.com/python-poetry/poetry/pull/9572),\n  [#10133](https://github.com/python-poetry/poetry/pull/10133)).\n- Add additional information on specifying required python versions ([#10104](https://github.com/python-poetry/poetry/pull/10104)).\n\n### poetry-core ([`2.1.0`](https://github.com/python-poetry/poetry-core/releases/tag/2.1.0))\n\n- Fix an issue where inclusive ordering with post releases was inconsistent with PEP 440 ([#379](https://github.com/python-poetry/poetry-core/pull/379)).\n- Fix an issue where invalid URI tokens in PEP 508 requirement strings were silently discarded ([#817](https://github.com/python-poetry/poetry-core/pull/817)).\n- Fix an issue where wrong markers were calculated when removing parts covered by the project's python constraint ([#824](https://github.com/python-poetry/poetry-core/pull/824)).\n- Fix an issue where optional dependencies that are not part of an extra were included in the wheel metadata ([#830](https://github.com/python-poetry/poetry-core/pull/830)).\n- Fix an issue where the `__pycache__` directory and `*.pyc` files were included in sdists and wheels ([#835](https://github.com/python-poetry/poetry-core/pull/835)).\n\n\n## [2.0.1] - 2025-01-11\n\n### Added\n\n- Add support for `poetry search` in legacy sources ([#9949](https://github.com/python-poetry/poetry/pull/9949)).\n- Add a message in the `poetry source show` output when PyPI is implicitly enabled ([#9974](https://github.com/python-poetry/poetry/pull/9974)).\n\n### Changed\n\n- Improve performance for merging markers from overrides at the end of dependency resolution ([#10018](https://github.com/python-poetry/poetry/pull/10018)).\n\n### Fixed\n\n- Fix an issue where `poetry sync` did not remove packages that were not requested ([#9946](https://github.com/python-poetry/poetry/pull/9946)).\n- Fix an issue where `poetry check` failed even though there were just warnings and add a `--strict` option to fail on warnings ([#9983](https://github.com/python-poetry/poetry/pull/9983)).\n- Fix an issue where `poetry update`, `poetry add` and `poetry remove` with `--only` uninstalled packages from other groups ([#10014](https://github.com/python-poetry/poetry/pull/10014)).\n- Fix an issue where `poetry update`, `poetry add` and `poetry remove` uninstalled all extra packages ([#10016](https://github.com/python-poetry/poetry/pull/10016)).\n- Fix an issue where `poetry self update` did not recognize Poetry's own environment ([#9995](https://github.com/python-poetry/poetry/pull/9995)).\n- Fix an issue where read-only system site-packages were not considered when loading an environment with system site-packages ([#9942](https://github.com/python-poetry/poetry/pull/9942)).\n- Fix an issue where an error message in `poetry install` started with `Warning:` instead of `Error:` ([#9945](https://github.com/python-poetry/poetry/pull/9945)).\n- Fix an issue where `Command.set_poetry`, which is used by plugins, was removed ([#9981](https://github.com/python-poetry/poetry/pull/9981)).\n- Fix an issue where the help text of `poetry build --clean` showed a malformed short option instead of the description ([#9994](https://github.com/python-poetry/poetry/pull/9994)).\n\n### Docs\n\n- Add a FAQ entry for the migration from Poetry-specific fields to the `project` section ([#9996](https://github.com/python-poetry/poetry/pull/9996)).\n- Fix examples for `project.readme` and `project.urls` ([#9948](https://github.com/python-poetry/poetry/pull/9948)).\n- Add a warning that package sources are a Poetry-specific feature that is not included in core metadata ([#9935](https://github.com/python-poetry/poetry/pull/9935)).\n- Replace `poetry install --sync` with `poetry sync` in the section about synchronizing dependencies ([#9944](https://github.com/python-poetry/poetry/pull/9944)).\n- Replace `poetry shell` with `poetry env activate` in the basic usage section ([#9963](https://github.com/python-poetry/poetry/pull/9963)).\n- Mention that `project.name` is always required when the `project` section is used ([#9989](https://github.com/python-poetry/poetry/pull/9989)).\n- Fix the constraint of `poetry-plugin-export` in the section about `poetry export` ([#9954](https://github.com/python-poetry/poetry/pull/9954)).\n\n### poetry-core ([`2.0.1`](https://github.com/python-poetry/poetry-core/releases/tag/2.0.1))\n\n- Replace the deprecated core metadata field `Home-page` with `Project-URL: Homepage` ([#807](https://github.com/python-poetry/poetry-core/pull/807)).\n- Fix an issue where includes from `tool.poetry.packages` without a specified `format` were not initialized with the default value resulting in a `KeyError` ([#805](https://github.com/python-poetry/poetry-core/pull/805)).\n- Fix an issue where some `project.urls` entries were not processed correctly resulting in a `KeyError` ([#807](https://github.com/python-poetry/poetry-core/pull/807)).\n- Fix an issue where dynamic `project.dependencies` via `tool.poetry.dependencies` were ignored if `project.optional-dependencies` were defined ([#811](https://github.com/python-poetry/poetry-core/pull/811)).\n\n\n## [2.0.0] - 2025-01-05\n\n### Added\n\n- **Add support for the `project` section in the `pyproject.toml` file according to PEP 621** ([#9135](https://github.com/python-poetry/poetry/pull/9135),\n[#9917](https://github.com/python-poetry/poetry/pull/9917)).\n- **Add support for defining Poetry plugins that are required by the project and automatically installed if not present** ([#9547](https://github.com/python-poetry/poetry/pull/9547)).\n- **Lock resulting markers and groups and add a `installer.re-resolve` option (default: `true`) to allow installation without re-resolving** ([#9427](https://github.com/python-poetry/poetry/pull/9427)).\n- Add a `--local-version` option to `poetry build` ([#9064](https://github.com/python-poetry/poetry/pull/9064)).\n- Add a `--clean` option to `poetry build` ([#9067](https://github.com/python-poetry/poetry/pull/9067)).\n- Add FIPS support for `poetry publish` ([#9101](https://github.com/python-poetry/poetry/pull/9101)).\n- Add the option to use `poetry new` interactively and configure more fields ([#9101](https://github.com/python-poetry/poetry/pull/9101)).\n- Add a config option `installer.only-binary` to enforce the use of binary distribution formats ([#9150](https://github.com/python-poetry/poetry/pull/9150)).\n- Add backend support for legacy repository search ([#9132](https://github.com/python-poetry/poetry/pull/9132)).\n- Add support to resume downloads from connection resets ([#9422](https://github.com/python-poetry/poetry/pull/9422)).\n- Add the option to define a constraint for the required Poetry version to manage the project ([#9547](https://github.com/python-poetry/poetry/pull/9547)).\n- Add an `--all-groups` option to `poetry install` ([#9744](https://github.com/python-poetry/poetry/pull/9744)).\n- Add an `poetry env activate` command as replacement of `poetry shell` ([#9763](https://github.com/python-poetry/poetry/pull/9763)).\n- Add a `--markers` option to `poetry add` to add a dependency with markers ([#9814](https://github.com/python-poetry/poetry/pull/9814)).\n- Add a `--migrate` option to `poetry config` to migrate outdated configs ([#9830](https://github.com/python-poetry/poetry/pull/9830)).\n- Add a `--project` option to search the `pyproject.toml` file in another directory without switching the directory ([#9831](https://github.com/python-poetry/poetry/pull/9831)).\n- Add support for shortened hashes to define git dependencies ([#9748](https://github.com/python-poetry/poetry/pull/9748)).\n- Add partial support for conflicting extras ([#9553](https://github.com/python-poetry/poetry/pull/9553)).\n- Add a `poetry sync` command as replacement of `poetry install --sync` ([#9801](https://github.com/python-poetry/poetry/pull/9801)).\n\n### Changed\n\n- **Change the default behavior of `poetry lock` to `--no-update` and introduce a `--regenerate` option for the old default behavior** ([#9327](https://github.com/python-poetry/poetry/pull/9327)).\n- **Remove the dependency on `poetry-plugin-export` so that `poetry export` is not included per default** ([#5980](https://github.com/python-poetry/poetry/pull/5980)).\n- **Outsource `poetry shell` into `poetry-plugin-shell`** ([#9763](https://github.com/python-poetry/poetry/pull/9763)).\n- **Change the interface of `poetry add --optional` to require an extra the optional dependency is added to** ([#9135](https://github.com/python-poetry/poetry/pull/9135)).\n- **Actually switch the directory when using `--directory`/`-C`** ([#9831](https://github.com/python-poetry/poetry/pull/9831)).\n- **Drop support for Python 3.8** ([#9692](https://github.com/python-poetry/poetry/pull/9692)).\n- Rename `experimental.system-git-client` to `experimental.system-git` ([#9787](https://github.com/python-poetry/poetry/pull/9787), [#9795](https://github.com/python-poetry/poetry/pull/9795)).\n- Replace `virtualenvs.prefer-active-python` by the inverse setting `virtualenvs.use-poetry-python` and prefer the active Python by default ([#9786](https://github.com/python-poetry/poetry/pull/9786)).\n- Deprecate several fields in the `tool.poetry` section in favor of the respective fields in the `project` section in the `pyproject.toml` file ([#9135](https://github.com/python-poetry/poetry/pull/9135)).\n- Deprecate `poetry install --sync` in favor of `poetry sync` ([#9801](https://github.com/python-poetry/poetry/pull/9801)).\n- Upgrade the warning if the current project cannot be installed to an error ([#9333](https://github.com/python-poetry/poetry/pull/9333)).\n- Remove special handling for `platformdirs 2.0` macOS config directory ([#8916](https://github.com/python-poetry/poetry/pull/8916)).\n- Tweak PEP 517 builds ([#9094](https://github.com/python-poetry/poetry/pull/9094)).\n- Use Poetry instead of pip to manage dependencies in isolated build environments ([#9168](https://github.com/python-poetry/poetry/pull/9168),\n[#9227](https://github.com/python-poetry/poetry/pull/9227)).\n- Trust empty `Requires-Dist` with modern metadata ([#9078](https://github.com/python-poetry/poetry/pull/9078)).\n- Do PEP 517 builds instead of parsing `setup.py` to determine dependencies ([#9099](https://github.com/python-poetry/poetry/pull/9099)).\n- Drop support for reading lock files prior version 1.0 (created with Poetry prior 1.1) ([#9345](https://github.com/python-poetry/poetry/pull/9345)).\n- Default to `>=` instead of `^` for the Python requirement when initializing a new project ([#9558](https://github.com/python-poetry/poetry/pull/9558)).\n- Limit `build-system` to the current major version of `poetry-core` when initializing a new project ([#9812](https://github.com/python-poetry/poetry/pull/9812)).\n- Remove pip-based installation, i.e. `installer.modern-installation = false` ([#9392](https://github.com/python-poetry/poetry/pull/9392)).\n- Remove `virtualenvs.options.no-setuptools` config option and never include `setuptools` per default ([#9331](https://github.com/python-poetry/poetry/pull/9331)).\n- Rename exceptions to have an `Error` suffix ([#9705](https://github.com/python-poetry/poetry/pull/9705)).\n- Remove deprecated CLI options and methods and revoke the deprecation of `--dev` ([#9732](https://github.com/python-poetry/poetry/pull/9732)).\n- Ignore installed packages during dependency resolution ([#9851](https://github.com/python-poetry/poetry/pull/9851)).\n- Improve the error message on upload failure ([#9701](https://github.com/python-poetry/poetry/pull/9701)).\n- Improve the error message if the current project cannot be installed to include another root cause ([#9651](https://github.com/python-poetry/poetry/pull/9651)).\n- Improve the output of `poetry show <package>` ([#9750](https://github.com/python-poetry/poetry/pull/9750)).\n- Improve the error message for build errors ([#9870](https://github.com/python-poetry/poetry/pull/9870)).\n- Improve the error message when trying to remove a package from a project without any dependencies ([#9918](https://github.com/python-poetry/poetry/pull/9918)).\n- Drop the direct dependency on `crashtest` ([#9108](https://github.com/python-poetry/poetry/pull/9108)).\n- Require `keyring>=23.3.1` ([#9167](https://github.com/python-poetry/poetry/pull/9167)).\n- Require `build>=1.2.1` ([#9283](https://github.com/python-poetry/poetry/pull/9283)).\n- Require `dulwich>=0.22.6` ([#9748](https://github.com/python-poetry/poetry/pull/9748)).\n\n### Fixed\n\n- Fix an issue where git dependencies with extras could only be cloned if a branch was specified explicitly ([#7028](https://github.com/python-poetry/poetry/pull/7028)).\n- Fix an issue where `poetry env remove` failed if `virtualenvs.in-project` was set to `true` ([#9118](https://github.com/python-poetry/poetry/pull/9118)).\n- Fix an issue where locking packages with a digit at the end of the name and non-standard sdist names failed ([#9189](https://github.com/python-poetry/poetry/pull/9189)).\n- Fix an issue where credentials where not passed when trying to download an URL dependency ([#9202](https://github.com/python-poetry/poetry/pull/9202)).\n- Fix an issue where using uncommon group names with `poetry add` resulted in a broken `pyproject.toml` ([#9277](https://github.com/python-poetry/poetry/pull/9277)).\n- Fix an issue where an inconsistent entry regarding the patch version of Python was kept in `envs.toml` ([#9286](https://github.com/python-poetry/poetry/pull/9286)).\n- Fix an issue where relative paths were not resolved properly when using `poetry build --directory` ([#9433](https://github.com/python-poetry/poetry/pull/9433)).\n- Fix an issue where unrequested extras were not uninstalled when running `poetry install` without an existing lock file ([#9345](https://github.com/python-poetry/poetry/pull/9345)).\n- Fix an issue where the `poetry-check` pre-commit hook did not trigger if only `poetry.lock` has changed ([#9504](https://github.com/python-poetry/poetry/pull/9504)).\n- Fix an issue where files (rather than directories) could not be added as single page source ([#9166](https://github.com/python-poetry/poetry/pull/9166)).\n- Fix an issue where invalid constraints were generated when adding a package with a local version specifier ([#9603](https://github.com/python-poetry/poetry/pull/9603)).\n- Fix several encoding warnings ([#8893](https://github.com/python-poetry/poetry/pull/8893)).\n- Fix an issue where `virtualenvs.prefer-active-python` was not respected ([#9278](https://github.com/python-poetry/poetry/pull/9278)).\n- Fix an issue where the line endings of the lock file were changed ([#9468](https://github.com/python-poetry/poetry/pull/9468)).\n- Fix an issue where installing multiple dependencies from the same git repository failed sporadically due to a race condition ([#9658](https://github.com/python-poetry/poetry/pull/9658)).\n- Fix an issue where installing multiple dependencies from forked monorepos failed sporadically due to a race condition ([#9723](https://github.com/python-poetry/poetry/pull/9723)).\n- Fix an issue where an extra package was not installed if it is required by multiple extras ([#9700](https://github.com/python-poetry/poetry/pull/9700)).\n- Fix an issue where a `direct_url.json` with vcs URLs not compliant with PEP 610 was written ([#9007](https://github.com/python-poetry/poetry/pull/9007)).\n- Fix an issue where other files than wheels were recognized as wheels ([#9770](https://github.com/python-poetry/poetry/pull/9770)).\n- Fix an issue where `installer.max-workers` was ignored for the implicit PyPI source ([#9815](https://github.com/python-poetry/poetry/pull/9815)).\n- Fix an issue where local settings (from `poetry.toml`) were ignored for the implicit PyPI source ([#9816](https://github.com/python-poetry/poetry/pull/9816)).\n- Fix an issue where different `dulwich` versions resulted in different hashes for a git dependency from a tag ([#9849](https://github.com/python-poetry/poetry/pull/9849)).\n- Fix an issue where installing a yanked package with no dependencies failed with an `IndexError` ([#9505](https://github.com/python-poetry/poetry/pull/9505)).\n- Fix an issue where a package could not be added from a source that required an empty password ([#9850](https://github.com/python-poetry/poetry/pull/9850)).\n- Fix an issue where setting `allow-prereleases = false` still allowed pre-releases if no other solution was found ([#9798](https://github.com/python-poetry/poetry/pull/9798)).\n- Fix an issue where the wrong environment was used for checking if an installed package is from system site packages ([#9861](https://github.com/python-poetry/poetry/pull/9861)).\n- Fix an issue where build errors from builds to retrieve metadata information were hidden ([#9870](https://github.com/python-poetry/poetry/pull/9870)).\n- Fix an issue where `poetry check` falsely reported that an invalid source \"pypi\" is referenced in dependencies ([#9475](https://github.com/python-poetry/poetry/pull/9475)).\n- Fix an issue where `poetry install --sync` tried to uninstall system site packages if the virtual environment was created with `virtualenvs.options.system-site-packages = true` ([#9863](https://github.com/python-poetry/poetry/pull/9863)).\n- Fix an issue where HTTP streaming requests were not closed properly when not completely consumed ([#9899](https://github.com/python-poetry/poetry/pull/9899)).\n\n### Docs\n\n- Add information about getting test coverage in the contribution guide ([#9726](https://github.com/python-poetry/poetry/pull/9726)).\n- Mention `pre-commit-update` as an alternative to `pre-commit autoupdate` ([#9716](https://github.com/python-poetry/poetry/pull/9716)).\n- Improve the explanation of `exclude` and `include` ([#9734](https://github.com/python-poetry/poetry/pull/9734)).\n- Add information about compatible release requirements, i.e. `~=` ([#9783](https://github.com/python-poetry/poetry/pull/9783)).\n- Add documentation for using a build script to build extension modules ([#9864](https://github.com/python-poetry/poetry/pull/9864)).\n\n### poetry-core ([`2.0.0`](https://github.com/python-poetry/poetry-core/releases/tag/2.0.0))\n\n- Add support for non PEP440 compliant version in the `platform_release` marker ([#722](https://github.com/python-poetry/poetry-core/pull/722)).\n- Add support for string comparisons with `in` / `not in` in generic constraints ([#722](https://github.com/python-poetry/poetry-core/pull/722)).\n- Add support for script files that are generated by a build script ([#710](https://github.com/python-poetry/poetry-core/pull/710)).\n- Add support for `SOURCE_DATE_EPOCH` when building packages ([#766](https://github.com/python-poetry/poetry-core/pull/766),\n[#781](https://github.com/python-poetry/poetry-core/pull/781)).\n- Create `METADATA` files with version 2.3 instead of 2.2 ([#707](https://github.com/python-poetry/poetry-core/pull/707)).\n- Remove support for `x` in version constraints ([#770](https://github.com/python-poetry/poetry-core/pull/770)).\n- Remove support for scripts with extras ([#708](https://github.com/python-poetry/poetry-core/pull/708)).\n- Remove deprecated features and interfaces ([#702](https://github.com/python-poetry/poetry-core/pull/702),\n[#769](https://github.com/python-poetry/poetry-core/pull/769)).\n- Deprecate `tool.poetry.dev-dependencies` in favor of `tool.poetry.group.dev.dependencies` ([#754](https://github.com/python-poetry/poetry-core/pull/754)).\n- Fix an issue where the `platlib` directory of the wrong Python was used ([#726](https://github.com/python-poetry/poetry-core/pull/726)).\n- Fix an issue where building a wheel in a nested output directory results in an error ([#762](https://github.com/python-poetry/poetry-core/pull/762)).\n- Fix an issue where `+` was not allowed in git URL paths ([#765](https://github.com/python-poetry/poetry-core/pull/765)).\n- Fix an issue where the temporary directory was not cleaned up on error ([#775](https://github.com/python-poetry/poetry-core/pull/775)).\n- Fix an issue where the regular expression for author names was too restrictive ([#517](https://github.com/python-poetry/poetry-core/pull/517)).\n- Fix an issue where basic auth http(s) credentials could not be parsed ([#791](https://github.com/python-poetry/poetry-core/pull/791)).\n\n\n## [1.8.5] - 2024-12-06\n\n### Changed\n\n- Require `pkginfo>=1.12` to fix an issue with an unknown metadata version 2.4 ([#9888](https://github.com/python-poetry/poetry/pull/9888)).\n- Do not fail if the unknown metadata version is only a minor version update ([#9888](https://github.com/python-poetry/poetry/pull/9888)).\n\n\n## [1.8.4] - 2024-10-14\n\n### Added\n\n- **Add official support for Python 3.13** ([#9523](https://github.com/python-poetry/poetry/pull/9523)).\n\n### Changed\n\n- Require `virtualenv>=20.26.6` to mitigate potential command injection when running `poetry shell` in untrusted projects ([#9757](https://github.com/python-poetry/poetry/pull/9757)).\n\n### poetry-core ([`1.9.1`](https://github.com/python-poetry/poetry-core/releases/tag/1.9.1))\n\n- Add `3.13` to the list of available Python versions ([#747](https://github.com/python-poetry/poetry-core/pull/747)).\n\n\n## [1.8.3] - 2024-05-08\n\n### Added\n\n- Add support for untagged CPython builds with versions ending with a `+` ([#9207](https://github.com/python-poetry/poetry/pull/9207)).\n\n### Changed\n\n- Require `pkginfo>=1.10` to ensure support for packages with metadata version 2.3 ([#9130](https://github.com/python-poetry/poetry/pull/9130)).\n- Improve locking on FIPS systems ([#9152](https://github.com/python-poetry/poetry/pull/9152)).\n\n### Fixed\n\n- Fix an issue where unrecognized package metadata versions silently resulted in empty dependencies ([#9203](https://github.com/python-poetry/poetry/pull/9203),\n  [#9226](https://github.com/python-poetry/poetry/pull/9226)).\n- Fix an issue where trailing slashes in git URLs where not handled correctly ([#9205](https://github.com/python-poetry/poetry/pull/9205)).\n- Fix an issue where `poetry self` commands printed a warning that the current project cannot be installed ([#9302](https://github.com/python-poetry/poetry/pull/9302)).\n- Fix an issue where `poetry install` sporadically failed with a `KeyError` due to a race condition ([#9335](https://github.com/python-poetry/poetry/pull/9335)).\n\n### Docs\n\n- Fix incorrect information about `poetry shell` ([#9060](https://github.com/python-poetry/poetry/pull/9060)).\n- Add a git subdirectory example to `poetry add` ([#9080](https://github.com/python-poetry/poetry/pull/9080)).\n- Mention interactive credential configuration ([#9074](https://github.com/python-poetry/poetry/pull/9074)).\n- Add notes for optional advanced installation steps ([#9098](https://github.com/python-poetry/poetry/pull/9098)).\n- Add reference to configuration credentials in documentation of poetry `publish` ([#9110](https://github.com/python-poetry/poetry/pull/9110)).\n- Improve documentation for configuring credentials via environment variables ([#9121](https://github.com/python-poetry/poetry/pull/9121)).\n- Remove misleading wording around virtual environments ([#9213](https://github.com/python-poetry/poetry/pull/9213)).\n- Remove outdated advice regarding seeding keyring backends ([#9164](https://github.com/python-poetry/poetry/pull/9164)).\n- Add a `pyproject.toml` example for a dependency with multiple extras ([#9138](https://github.com/python-poetry/poetry/pull/9138)).\n- Clarify help of `poetry add` ([#9230](https://github.com/python-poetry/poetry/pull/9230)).\n- Add a note how to configure credentials for TestPyPI for `poetry publish` ([#9255](https://github.com/python-poetry/poetry/pull/9255)).\n- Fix information about the `--readme` option in `poetry new` ([#9260](https://github.com/python-poetry/poetry/pull/9260)).\n- Clarify what is special about the Python constraint in `dependencies` ([#9256](https://github.com/python-poetry/poetry/pull/9256)).\n- Update how to uninstall plugins via `pipx` ([#9320](https://github.com/python-poetry/poetry/pull/9320)).\n\n\n## [1.8.2] - 2024-03-02\n\n### Fixed\n\n- Harden `lazy-wheel` error handling if the index server is behaving badly in an unexpected way ([#9051](https://github.com/python-poetry/poetry/pull/9051)).\n- Improve `lazy-wheel` error handling if the index server does not handle HTTP range requests correctly ([#9082](https://github.com/python-poetry/poetry/pull/9082)).\n- Improve `lazy-wheel` error handling if the index server pretends to support HTTP range requests but does not respect them ([#9084](https://github.com/python-poetry/poetry/pull/9084)).\n- Improve `lazy-wheel` to allow redirects for HEAD requests ([#9087](https://github.com/python-poetry/poetry/pull/9087)).\n- Improve debug logging for `lazy-wheel` errors ([#9059](https://github.com/python-poetry/poetry/pull/9059)).\n- Fix an issue where the hash of a metadata file could not be calculated correctly due to an encoding issue ([#9049](https://github.com/python-poetry/poetry/pull/9049)).\n- Fix an issue where `poetry add` failed in non-package mode if no project name was set ([#9046](https://github.com/python-poetry/poetry/pull/9046)).\n- Fix an issue where a hint to non-package mode was not compliant with the final name of the setting ([#9073](https://github.com/python-poetry/poetry/pull/9073)).\n\n\n## [1.8.1] - 2024-02-26\n\n### Fixed\n\n- Update the minimum required version of `packaging` ([#9031](https://github.com/python-poetry/poetry/pull/9031)).\n- Handle unexpected responses from servers that do not support HTTP range requests with negative offsets more robust ([#9030](https://github.com/python-poetry/poetry/pull/9030)).\n\n### Docs\n\n- Rename `master` branch to `main` ([#9022](https://github.com/python-poetry/poetry/pull/9022)).\n\n\n## [1.8.0] - 2024-02-25\n\n### Added\n\n- **Add a `non-package` mode for use cases where Poetry is only used for dependency management** ([#8650](https://github.com/python-poetry/poetry/pull/8650)).\n- **Add support for PEP 658 to fetch metadata without having to download wheels** ([#5509](https://github.com/python-poetry/poetry/pull/5509)).\n- **Add a `lazy-wheel` config option (default: `true`) to reduce wheel downloads during dependency resolution** ([#8815](https://github.com/python-poetry/poetry/pull/8815),\n  [#8941](https://github.com/python-poetry/poetry/pull/8941)).\n- Improve performance of dependency resolution by using shallow copies instead of deep copies ([#8671](https://github.com/python-poetry/poetry/pull/8671)).\n- `poetry check` validates that no unknown sources are referenced in dependencies ([#8709](https://github.com/python-poetry/poetry/pull/8709)).\n- Add archive validation during installation for further hash algorithms ([#8851](https://github.com/python-poetry/poetry/pull/8851)).\n- Add a `to` key in `tool.poetry.packages` to allow custom subpackage names ([#8791](https://github.com/python-poetry/poetry/pull/8791)).\n- Add a config option to disable `keyring` ([#8910](https://github.com/python-poetry/poetry/pull/8910)).\n- Add a `--sync` option to `poetry update` ([#8931](https://github.com/python-poetry/poetry/pull/8931)).\n- Add an `--output` option to `poetry build` ([#8828](https://github.com/python-poetry/poetry/pull/8828)).\n- Add a `--dist-dir` option to `poetry publish` ([#8828](https://github.com/python-poetry/poetry/pull/8828)).\n\n### Changed\n\n- **The implicit PyPI source is disabled if at least one primary source is configured** ([#8771](https://github.com/python-poetry/poetry/pull/8771)).\n- **Deprecate source priority `default`** ([#8771](https://github.com/python-poetry/poetry/pull/8771)).\n- **Upgrade the warning about an inconsistent lockfile to an error** ([#8737](https://github.com/python-poetry/poetry/pull/8737)).\n- Deprecate setting `installer.modern-installation` to `false` ([#8988](https://github.com/python-poetry/poetry/pull/8988)).\n- Drop support for `pip<19` ([#8894](https://github.com/python-poetry/poetry/pull/8894)).\n- Require `requests-toolbelt>=1` ([#8680](https://github.com/python-poetry/poetry/pull/8680)).\n- Allow `platformdirs` 4.x ([#8668](https://github.com/python-poetry/poetry/pull/8668)).\n- Allow and require `xattr` 1.x on macOS ([#8801](https://github.com/python-poetry/poetry/pull/8801)).\n- Improve venv shell activation in `fish` ([#8804](https://github.com/python-poetry/poetry/pull/8804)).\n- Rename `system` to `base` in output of `poetry env info` ([#8832](https://github.com/python-poetry/poetry/pull/8832)).\n- Use pretty name in output of `poetry version` ([#8849](https://github.com/python-poetry/poetry/pull/8849)).\n- Improve error handling for invalid entries in `tool.poetry.scripts` ([#8898](https://github.com/python-poetry/poetry/pull/8898)).\n- Improve verbose output for dependencies with extras during dependency resolution ([#8834](https://github.com/python-poetry/poetry/pull/8834)).\n- Improve message about an outdated lockfile ([#8962](https://github.com/python-poetry/poetry/pull/8962)).\n\n### Fixed\n\n- Fix an issue where `poetry shell` failed when Python has been installed with MSYS2 ([#8644](https://github.com/python-poetry/poetry/pull/8644)).\n- Fix an issue where Poetry commands failed in a terminal with a non-UTF-8 encoding ([#8608](https://github.com/python-poetry/poetry/pull/8608)).\n- Fix an issue where a missing project name caused an incomprehensible error message ([#8691](https://github.com/python-poetry/poetry/pull/8691)).\n- Fix an issue where Poetry failed to install an `sdist` path dependency ([#8682](https://github.com/python-poetry/poetry/pull/8682)).\n- Fix an issue where `poetry install` failed because an unused extra was not available ([#8548](https://github.com/python-poetry/poetry/pull/8548)).\n- Fix an issue where `poetry install --sync` did not remove an unrequested extra ([#8621](https://github.com/python-poetry/poetry/pull/8621)).\n- Fix an issue where `poetry init` did not allow specific characters in the author field ([#8779](https://github.com/python-poetry/poetry/pull/8779)).\n- Fix an issue where Poetry could not download `sdists` from misconfigured servers ([#8701](https://github.com/python-poetry/poetry/pull/8701)).\n- Fix an issue where metadata of sdists that call CLI tools of their build requirements could not be determined ([#8827](https://github.com/python-poetry/poetry/pull/8827)).\n- Fix an issue where Poetry failed to use the currently activated environment ([#8831](https://github.com/python-poetry/poetry/pull/8831)).\n- Fix an issue where `poetry shell` failed in `zsh` if a space was in the venv path ([#7245](https://github.com/python-poetry/poetry/pull/7245)).\n- Fix an issue where scripts with extras could not be installed ([#8900](https://github.com/python-poetry/poetry/pull/8900)).\n- Fix an issue where explicit sources where not propagated correctly ([#8835](https://github.com/python-poetry/poetry/pull/8835)).\n- Fix an issue where debug prints where swallowed when using a build script ([#8760](https://github.com/python-poetry/poetry/pull/8760)).\n- Fix an issue where explicit sources of locked dependencies where not propagated correctly ([#8948](https://github.com/python-poetry/poetry/pull/8948)).\n- Fix an issue where Poetry's own environment was falsely identified as system environment ([#8970](https://github.com/python-poetry/poetry/pull/8970)).\n- Fix an issue where dependencies from a `setup.py` were ignored silently ([#9000](https://github.com/python-poetry/poetry/pull/9000)).\n- Fix an issue where environment variables for `virtualenv.options` were ignored ([#9015](https://github.com/python-poetry/poetry/pull/9015)).\n- Fix an issue where `virtualenvs.options.no-pip` and `virtualenvs.options.no-setuptools` were not normalized ([#9015](https://github.com/python-poetry/poetry/pull/9015)).\n\n### Docs\n\n- Replace deprecated `--no-dev` with `--without dev` in the FAQ ([#8659](https://github.com/python-poetry/poetry/pull/8659)).\n- Recommend `poetry-check` instead of the deprecated `poetry-lock` pre-commit hook ([#8675](https://github.com/python-poetry/poetry/pull/8675)).\n- Clarify the names of the environment variables to provide credentials for repositories ([#8782](https://github.com/python-poetry/poetry/pull/8782)).\n- Add note how to install several version of Poetry in parallel ([#8814](https://github.com/python-poetry/poetry/pull/8814)).\n- Improve description of `poetry show --why` ([#8817](https://github.com/python-poetry/poetry/pull/8817)).\n- Improve documentation of `poetry update` ([#8706](https://github.com/python-poetry/poetry/pull/8706)).\n- Add a warning about passing variables that may start with a hyphen via command line ([#8850](https://github.com/python-poetry/poetry/pull/8850)).\n- Mention that the virtual environment in which Poetry itself is installed should not be activated ([#8833](https://github.com/python-poetry/poetry/pull/8833)).\n- Add note about `poetry run` and externally managed environments ([#8748](https://github.com/python-poetry/poetry/pull/8748)).\n- Update FAQ entry about `tox` for `tox` 4.x ([#8658](https://github.com/python-poetry/poetry/pull/8658)).\n- Fix documentation for default `format` option for `include` and `exclude` value ([#8852](https://github.com/python-poetry/poetry/pull/8852)).\n- Add note about `tox` and configured credentials ([#8888](https://github.com/python-poetry/poetry/pull/8888)).\n- Add note and link how to install `pipx` ([#8878](https://github.com/python-poetry/poetry/pull/8878)).\n- Fix examples for `poetry add` with git dependencies over ssh ([#8911](https://github.com/python-poetry/poetry/pull/8911)).\n- Remove reference to deprecated scripts extras feature ([#8903](https://github.com/python-poetry/poetry/pull/8903)).\n- Change examples to prefer `--only main` instead of `--without dev` ([#8921](https://github.com/python-poetry/poetry/pull/8921)).\n- Mention that the `develop` attribute is a Poetry-specific feature and not propagated to other tools ([#8971](https://github.com/python-poetry/poetry/pull/8971)).\n- Fix examples for adding supplemental and secondary sources ([#8953](https://github.com/python-poetry/poetry/pull/8953)).\n- Add PyTorch example for explicit sources ([#9006](https://github.com/python-poetry/poetry/pull/9006)).\n\n### poetry-core ([`1.9.0`](https://github.com/python-poetry/poetry-core/releases/tag/1.9.0))\n\n- **Deprecate scripts that depend on extras** ([#690](https://github.com/python-poetry/poetry-core/pull/690)).\n- Add support for path dependencies that do not define a build system ([#675](https://github.com/python-poetry/poetry-core/pull/675)).\n- Update list of supported licenses ([#659](https://github.com/python-poetry/poetry-core/pull/659),\n  [#669](https://github.com/python-poetry/poetry-core/pull/669),\n  [#678](https://github.com/python-poetry/poetry-core/pull/678),\n  [#694](https://github.com/python-poetry/poetry-core/pull/694)).\n- Rework list of files included in build artifacts ([#666](https://github.com/python-poetry/poetry-core/pull/666)).\n- Fix an issue where insignificant errors were printed if the working directory is not inside a git repository ([#684](https://github.com/python-poetry/poetry-core/pull/684)).\n- Fix an issue where the project's directory was not recognized as git repository on Windows due to an encoding issue ([#685](https://github.com/python-poetry/poetry-core/pull/685)).\n\n\n## [1.7.1] - 2023-11-16\n\n### Fixed\n\n- Fix an issue where sdists that call CLI tools of their build requirements could not be installed ([#8630](https://github.com/python-poetry/poetry/pull/8630)).\n- Fix an issue where sdists with symlinks could not be installed due to a broken tarfile datafilter ([#8649](https://github.com/python-poetry/poetry/pull/8649)).\n- Fix an issue where `poetry init` failed when trying to add dependencies ([#8655](https://github.com/python-poetry/poetry/pull/8655)).\n- Fix an issue where `poetry install` failed if `virtualenvs.create` was set to `false` ([#8672](https://github.com/python-poetry/poetry/pull/8672)).\n\n\n## [1.7.0] - 2023-11-03\n\n### Added\n\n- **Add official support for Python 3.12** ([#7803](https://github.com/python-poetry/poetry/pull/7803), [#8544](https://github.com/python-poetry/poetry/pull/8544)).\n- **Print a future warning that `poetry-plugin-export` will not be installed by default anymore** ([#8562](https://github.com/python-poetry/poetry/pull/8562)).\n- Add `poetry-install` pre-commit hook ([#8327](https://github.com/python-poetry/poetry/pull/8327)).\n- Add `--next-phase` option to `poetry version` ([#8089](https://github.com/python-poetry/poetry/pull/8089)).\n- Print a warning when overwriting files from another package at installation ([#8386](https://github.com/python-poetry/poetry/pull/8386)).\n- Print a warning if the current project cannot be installed ([#8369](https://github.com/python-poetry/poetry/pull/8369)).\n- Report more details on build backend exceptions ([#8464](https://github.com/python-poetry/poetry/pull/8464)).\n\n### Changed\n\n- Set Poetry as `user-agent` for all HTTP requests ([#8394](https://github.com/python-poetry/poetry/pull/8394)).\n- Do not install `setuptools` per default in Python 3.12 ([#7803](https://github.com/python-poetry/poetry/pull/7803)).\n- Do not install `wheel` per default ([#7803](https://github.com/python-poetry/poetry/pull/7803)).\n- Remove `setuptools` and `wheel` when running `poetry install --sync` if they are not required by the project ([#8600](https://github.com/python-poetry/poetry/pull/8600)).\n- Improve error message about PEP-517 support ([#8463](https://github.com/python-poetry/poetry/pull/8463)).\n- Improve `keyring` handling ([#8227](https://github.com/python-poetry/poetry/pull/8227)).\n- Read the `description` field when extracting metadata from `setup.py` files ([#8545](https://github.com/python-poetry/poetry/pull/8545)).\n\n### Fixed\n\n- **Fix an issue where dependencies of inactive extras were locked and installed** ([#8399](https://github.com/python-poetry/poetry/pull/8399)).\n- **Fix an issue where build requirements were not installed due to a race condition in the artifact cache** ([#8517](https://github.com/python-poetry/poetry/pull/8517)).\n- Fix an issue where packages included in the system site packages were installed even though `virtualenvs.options.system-site-packages` was set ([#8359](https://github.com/python-poetry/poetry/pull/8359)).\n- Fix an issue where git dependencies' submodules with relative URLs were handled incorrectly ([#8020](https://github.com/python-poetry/poetry/pull/8020)).\n- Fix an issue where a failed installation of build dependencies was not noticed directly ([#8479](https://github.com/python-poetry/poetry/pull/8479)).\n- Fix an issue where `poetry shell` did not work completely with `nushell` ([#8478](https://github.com/python-poetry/poetry/pull/8478)).\n- Fix an issue where a confusing error messages was displayed when running `poetry config pypi-token.pypi` without a value ([#8502](https://github.com/python-poetry/poetry/pull/8502)).\n- Fix an issue where a cryptic error message is printed if there is no metadata entry in the lockfile ([#8523](https://github.com/python-poetry/poetry/pull/8523)).\n- Fix an issue with the encoding with special characters in the virtualenv's path ([#8565](https://github.com/python-poetry/poetry/pull/8565)).\n- Fix an issue where the connection pool size was not adjusted to the number of workers ([#8559](https://github.com/python-poetry/poetry/pull/8559)).\n\n### Docs\n\n- Improve the wording regarding a project's supported Python range ([#8423](https://github.com/python-poetry/poetry/pull/8423)).\n- Make `pipx` the preferred (first mentioned) installation method ([#8090](https://github.com/python-poetry/poetry/pull/8090)).\n- Add a warning about `poetry self` on Windows ([#8090](https://github.com/python-poetry/poetry/pull/8090)).\n- Fix example for `poetry add` with a git dependency ([#8438](https://github.com/python-poetry/poetry/pull/8438)).\n- Add information about auto-included files in wheels and sdist ([#8555](https://github.com/python-poetry/poetry/pull/8555)).\n- Fix documentation of the `POETRY_REPOSITORIES_` variables docs ([#8492](https://github.com/python-poetry/poetry/pull/8492)).\n- Add `CITATION.cff` file ([#8510](https://github.com/python-poetry/poetry/pull/8510)).\n\n### poetry-core ([`1.8.1`](https://github.com/python-poetry/poetry-core/releases/tag/1.8.1))\n\n- Add support for creating packages dynamically in the build script ([#629](https://github.com/python-poetry/poetry-core/pull/629)).\n- Improve marker logic for `extra` markers ([#636](https://github.com/python-poetry/poetry-core/pull/636)).\n- Update list of supported licenses ([#635](https://github.com/python-poetry/poetry-core/pull/635), [#646](https://github.com/python-poetry/poetry-core/pull/646)).\n- Fix an issue where projects with extension modules were not installed in editable mode ([#633](https://github.com/python-poetry/poetry-core/pull/633)).\n- Fix an issue where the wrong or no `lib` folder was added to the wheel ([#634](https://github.com/python-poetry/poetry-core/pull/634)).\n\n### poetry-plugin-export ([`^1.6.0`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.6.0))\n\n- Add an `--all-extras` option ([#241](https://github.com/python-poetry/poetry-plugin-export/pull/241)).\n- Fix an issue where git dependencies are exported with the branch name instead of the resolved commit hash ([#213](https://github.com/python-poetry/poetry-plugin-export/pull/213)).\n\n\n## [1.6.1] - 2023-08-21\n\n### Fixed\n\n- Update the minimum required version of `requests` ([#8336](https://github.com/python-poetry/poetry/pull/8336)).\n\n\n## [1.6.0] - 2023-08-20\n\n### Added\n\n- **Add support for repositories that do not provide a supported hash algorithm** ([#8118](https://github.com/python-poetry/poetry/pull/8118)).\n- **Add full support for duplicate dependencies with overlapping markers** ([#7257](https://github.com/python-poetry/poetry/pull/7257)).\n- **Improve performance of `poetry lock` for certain edge cases** ([#8256](https://github.com/python-poetry/poetry/pull/8256)).\n- Improve performance of `poetry install` ([#8031](https://github.com/python-poetry/poetry/pull/8031)).\n- `poetry check` validates that specified `readme` files do exist ([#7444](https://github.com/python-poetry/poetry/pull/7444)).\n- Add a downgrading note when updating to an older version ([#8176](https://github.com/python-poetry/poetry/pull/8176)).\n- Add support for `vox` in the `xonsh` shell ([#8203](https://github.com/python-poetry/poetry/pull/8203)).\n- Add support for `pre-commit` hooks for projects where the pyproject.toml file is located in a subfolder ([#8204](https://github.com/python-poetry/poetry/pull/8204)).\n- Add support for the `git+http://` scheme ([#6619](https://github.com/python-poetry/poetry/pull/6619)).\n\n### Changed\n\n- **Drop support for Python 3.7** ([#7674](https://github.com/python-poetry/poetry/pull/7674)).\n- Move `poetry lock --check` to `poetry check --lock` and deprecate the former ([#8015](https://github.com/python-poetry/poetry/pull/8015)).\n- Change future warning that PyPI will only be disabled automatically if there are no primary sources ([#8151](https://github.com/python-poetry/poetry/pull/8151)).\n\n### Fixed\n\n- Fix an issue where `build-system.requires` were not respected for projects with build scripts ([#7975](https://github.com/python-poetry/poetry/pull/7975)).\n- Fix an issue where the encoding was not handled correctly when calling a subprocess ([#8060](https://github.com/python-poetry/poetry/pull/8060)).\n- Fix an issue where `poetry show --top-level` did not show top level dependencies with extras ([#8076](https://github.com/python-poetry/poetry/pull/8076)).\n- Fix an issue where `poetry init` handled projects with `src` layout incorrectly ([#8218](https://github.com/python-poetry/poetry/pull/8218)).\n- Fix an issue where Poetry wrote `.pth` files with the wrong encoding ([#8041](https://github.com/python-poetry/poetry/pull/8041)).\n- Fix an issue where `poetry install` did not respect the source if the same version of a package has been locked from different sources ([#8304](https://github.com/python-poetry/poetry/pull/8304)).\n\n### Docs\n\n- Document **official Poetry badge** ([#8066](https://github.com/python-poetry/poetry/pull/8066)).\n- Update configuration folder path for macOS ([#8062](https://github.com/python-poetry/poetry/pull/8062)).\n- Add a warning about pip ignoring lock files ([#8117](https://github.com/python-poetry/poetry/pull/8117)).\n- Clarify the use of the `virtualenvs.in-project` setting. ([#8126](https://github.com/python-poetry/poetry/pull/8126)).\n- Change `pre-commit` YAML style to be consistent with pre-commit's own examples ([#8146](https://github.com/python-poetry/poetry/pull/8146)).\n- Fix command for listing installed plugins ([#8200](https://github.com/python-poetry/poetry/pull/8200)).\n- Mention the `nox-poetry` package ([#8173](https://github.com/python-poetry/poetry/pull/8173)).\n- Add an example with a PyPI source in the pyproject.toml file ([#8171](https://github.com/python-poetry/poetry/pull/8171)).\n- Use `reference` instead of deprecated `callable` in the scripts example ([#8211](https://github.com/python-poetry/poetry/pull/8211)).\n\n### poetry-core ([`1.7.0`](https://github.com/python-poetry/poetry-core/releases/tag/1.7.0))\n\n- Improve performance of marker handling ([#609](https://github.com/python-poetry/poetry-core/pull/609)).\n- Allow `|` as a value separator in markers with the operators `in` and `not in` ([#608](https://github.com/python-poetry/poetry-core/pull/608)).\n- Put pretty name (instead of normalized name) in metadata ([#620](https://github.com/python-poetry/poetry-core/pull/620)).\n- Update list of supported licenses ([#623](https://github.com/python-poetry/poetry-core/pull/623)).\n- Fix an issue where PEP 508 dependency specifications with names starting with a digit could not be parsed ([#607](https://github.com/python-poetry/poetry-core/pull/607)).\n- Fix an issue where Poetry considered an unrelated `.gitignore` file resulting in an empty wheel ([#611](https://github.com/python-poetry/poetry-core/pull/611)).\n\n### poetry-plugin-export ([`^1.5.0`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.5.0))\n\n- Fix an issue where markers for dependencies required by an extra were not generated correctly ([#209](https://github.com/python-poetry/poetry-plugin-export/pull/209)).\n\n\n## [1.5.1] - 2023-05-29\n\n### Added\n\n- Improve dependency resolution performance in cases with a lot of backtracking ([#7950](https://github.com/python-poetry/poetry/pull/7950)).\n\n### Changed\n\n- Disable wheel content validation during installation ([#7987](https://github.com/python-poetry/poetry/pull/7987)).\n\n### Fixed\n\n- Fix an issue where partially downloaded wheels were cached ([#7968](https://github.com/python-poetry/poetry/pull/7968)).\n- Fix an issue where `poetry run` did no longer execute relative-path scripts ([#7963](https://github.com/python-poetry/poetry/pull/7963)).\n- Fix an issue where dependencies were not installed in `in-project` environments ([#7977](https://github.com/python-poetry/poetry/pull/7977)).\n- Fix an issue where no solution was found for a transitive dependency on a pre-release of a package ([#7978](https://github.com/python-poetry/poetry/pull/7978)).\n- Fix an issue where cached repository packages were incorrectly parsed, leading to its dependencies being ignored ([#7995](https://github.com/python-poetry/poetry/pull/7995)).\n- Fix an issue where an explicit source was ignored so that a direct origin dependency was used instead ([#7973](https://github.com/python-poetry/poetry/pull/7973)).\n- Fix an issue where the installation of big wheels consumed a lot of memory ([#7987](https://github.com/python-poetry/poetry/pull/7987)).\n\n### Docs\n\n- Add information about multiple constraints dependencies with direct origin and version dependencies ([#7973](https://github.com/python-poetry/poetry/pull/7973)).\n\n### poetry-core ([`1.6.1`](https://github.com/python-poetry/poetry-core/releases/tag/1.6.1))\n\n- Fix an endless recursion in marker handling ([#593](https://github.com/python-poetry/poetry-core/pull/593)).\n- Fix an issue where the wheel tag was not built correctly under certain circumstances ([#591](https://github.com/python-poetry/poetry-core/pull/591)).\n\n### poetry-plugin-export ([`^1.4.0`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.4.0))\n\n- Fix an issue where `--extra-index-url` and `--trusted-host` was not generated for sources with priority `explicit` ([#205](https://github.com/python-poetry/poetry-plugin-export/pull/205)).\n\n\n## [1.5.0] - 2023-05-19\n\n### Added\n\n- **Introduce the new source priorities `explicit` and `supplemental`** ([#7658](https://github.com/python-poetry/poetry/pull/7658),\n  [#6879](https://github.com/python-poetry/poetry/pull/6879)).\n- **Introduce the option to configure the priority of the implicit PyPI source** ([#7801](https://github.com/python-poetry/poetry/pull/7801)).\n- Add handling for corrupt cache files ([#7453](https://github.com/python-poetry/poetry/pull/7453)).\n- Improve caching of URL and git dependencies ([#7693](https://github.com/python-poetry/poetry/pull/7693),\n  [#7473](https://github.com/python-poetry/poetry/pull/7473)).\n- Add option to skip installing directory dependencies ([#6845](https://github.com/python-poetry/poetry/pull/6845),\n  [#7923](https://github.com/python-poetry/poetry/pull/7923)).\n- Add `--executable` option to `poetry env info` ([#7547](https://github.com/python-poetry/poetry/pull/7547)).\n- Add `--top-level` option to `poetry show` ([#7415](https://github.com/python-poetry/poetry/pull/7415)).\n- Add `--lock` option to `poetry remove` ([#7917](https://github.com/python-poetry/poetry/pull/7917)).\n- Add experimental `POETRY_REQUESTS_TIMEOUT` option ([#7081](https://github.com/python-poetry/poetry/pull/7081)).\n- Improve performance of wheel inspection by avoiding unnecessary file copy operations ([#7916](https://github.com/python-poetry/poetry/pull/7916)).\n\n### Changed\n\n- **Remove the old deprecated installer and the corresponding setting `experimental.new-installer`** ([#7356](https://github.com/python-poetry/poetry/pull/7356)).\n- **Introduce `priority` key for sources and deprecate flags `default` and `secondary`** ([#7658](https://github.com/python-poetry/poetry/pull/7658)).\n- Deprecate `poetry run <entry point>` if the entry point was not previously installed via `poetry install` ([#7606](https://github.com/python-poetry/poetry/pull/7606)).\n- Only write the lock file if the installation succeeds ([#7498](https://github.com/python-poetry/poetry/pull/7498)).\n- Do not write the unused package category into the lock file ([#7637](https://github.com/python-poetry/poetry/pull/7637)).\n\n### Fixed\n\n- Fix an issue where Poetry's internal pyproject.toml continually grows larger with empty lines ([#7705](https://github.com/python-poetry/poetry/pull/7705)).\n- Fix an issue where Poetry crashes due to corrupt cache files ([#7453](https://github.com/python-poetry/poetry/pull/7453)).\n- Fix an issue where the `Retry-After` in HTTP responses was not respected and retries were handled inconsistently ([#7072](https://github.com/python-poetry/poetry/pull/7072)).\n- Fix an issue where Poetry silently ignored invalid groups ([#7529](https://github.com/python-poetry/poetry/pull/7529)).\n- Fix an issue where Poetry does not find a compatible Python version if not given explicitly ([#7771](https://github.com/python-poetry/poetry/pull/7771)).\n- Fix an issue where the `direct_url.json` of an editable install from a git dependency was invalid ([#7473](https://github.com/python-poetry/poetry/pull/7473)).\n- Fix an issue where error messages from build backends were not decoded correctly ([#7781](https://github.com/python-poetry/poetry/pull/7781)).\n- Fix an infinite loop when adding certain dependencies ([#7405](https://github.com/python-poetry/poetry/pull/7405)).\n- Fix an issue where pre-commit hooks skip pyproject.toml files in subdirectories ([#7239](https://github.com/python-poetry/poetry/pull/7239)).\n- Fix an issue where pre-commit hooks do not use the expected Python version ([#6989](https://github.com/python-poetry/poetry/pull/6989)).\n- Fix an issue where an unclear error message is printed if the project name is the same as one of its dependencies ([#7757](https://github.com/python-poetry/poetry/pull/7757)).\n- Fix an issue where `poetry install` returns a zero exit status even though the build script failed ([#7812](https://github.com/python-poetry/poetry/pull/7812)).\n- Fix an issue where an existing `.venv` was not used if `in-project` was not set ([#7792](https://github.com/python-poetry/poetry/pull/7792)).\n- Fix an issue where multiple extras passed to `poetry add` were not parsed correctly ([#7836](https://github.com/python-poetry/poetry/pull/7836)).\n- Fix an issue where `poetry shell` did not send a newline to `fish` ([#7884](https://github.com/python-poetry/poetry/pull/7884)).\n- Fix an issue where `poetry update --lock` printed operations that were not executed ([#7915](https://github.com/python-poetry/poetry/pull/7915)).\n- Fix an issue where `poetry add --lock` did perform a full update of all dependencies ([#7920](https://github.com/python-poetry/poetry/pull/7920)).\n- Fix an issue where `poetry shell` did not work with `nushell` ([#7919](https://github.com/python-poetry/poetry/pull/7919)).\n- Fix an issue where subprocess calls failed on Python 3.7 ([#7932](https://github.com/python-poetry/poetry/pull/7932)).\n- Fix an issue where keyring was called even though the password was stored in an environment variable ([#7928](https://github.com/python-poetry/poetry/pull/7928)).\n\n### Docs\n\n- Add information about what to use instead of `--dev` ([#7647](https://github.com/python-poetry/poetry/pull/7647)).\n- Promote semantic versioning less aggressively ([#7517](https://github.com/python-poetry/poetry/pull/7517)).\n- Explain Poetry's own versioning scheme in the FAQ ([#7517](https://github.com/python-poetry/poetry/pull/7517)).\n- Update documentation for configuration with environment variables ([#6711](https://github.com/python-poetry/poetry/pull/6711)).\n- Add details how to disable the virtualenv prompt ([#7874](https://github.com/python-poetry/poetry/pull/7874)).\n- Improve documentation on whether to commit `poetry.lock` ([#7506](https://github.com/python-poetry/poetry/pull/7506)).\n- Improve documentation of `virtualenv.create` ([#7608](https://github.com/python-poetry/poetry/pull/7608)).\n\n### poetry-core ([`1.6.0`](https://github.com/python-poetry/poetry-core/releases/tag/1.6.0))\n\n- Improve error message for invalid markers ([#569](https://github.com/python-poetry/poetry-core/pull/569)).\n- Increase robustness when deleting temporary directories on Windows ([#460](https://github.com/python-poetry/poetry-core/pull/460)).\n- Replace `tomlkit` with `tomli`, which changes the interface of some _internal_ classes ([#483](https://github.com/python-poetry/poetry-core/pull/483)).\n- Deprecate `Package.category` ([#561](https://github.com/python-poetry/poetry-core/pull/561)).\n- Fix a performance regression in marker handling ([#568](https://github.com/python-poetry/poetry-core/pull/568)).\n- Fix an issue where wildcard version constraints were not handled correctly ([#402](https://github.com/python-poetry/poetry-core/pull/402)).\n- Fix an issue where `poetry build` created duplicate Python classifiers if they were specified manually ([#578](https://github.com/python-poetry/poetry-core/pull/578)).\n- Fix an issue where local versions where not handled correctly ([#579](https://github.com/python-poetry/poetry-core/pull/579)).\n\n\n## [1.4.2] - 2023-04-02\n\n### Changed\n\n- When trying to install wheels with invalid `RECORD` files, Poetry does not fail anymore but only prints a warning.\n  This mitigates an unintended change introduced in Poetry 1.4.1 ([#7694](https://github.com/python-poetry/poetry/pull/7694)).\n\n### Fixed\n\n- Fix an issue where relative git submodule urls were not parsed correctly ([#7017](https://github.com/python-poetry/poetry/pull/7017)).\n- Fix an issue where Poetry could freeze when building a project with a build script if it generated enough output to fill the OS pipe buffer ([#7699](https://github.com/python-poetry/poetry/pull/7699)).\n\n\n## [1.4.1] - 2023-03-19\n\n### Fixed\n\n- Fix an issue where `poetry install` did not respect the requirements for building editable dependencies ([#7579](https://github.com/python-poetry/poetry/pull/7579)).\n- Fix an issue where `poetry init` crashed due to bad input when adding packages interactively ([#7569](https://github.com/python-poetry/poetry/pull/7569)).\n- Fix an issue where `poetry install` ignored the `subdirectory` argument of git dependencies ([#7580](https://github.com/python-poetry/poetry/pull/7580)).\n- Fix an issue where installing packages with `no-binary` could result in a false hash mismatch ([#7594](https://github.com/python-poetry/poetry/pull/7594)).\n- Fix an issue where the hash of sdists was neither validated nor written to the `direct_url.json` during installation ([#7594](https://github.com/python-poetry/poetry/pull/7594)).\n- Fix an issue where `poetry install --sync` attempted to remove itself ([#7626](https://github.com/python-poetry/poetry/pull/7626)).\n- Fix an issue where wheels with non-normalized `dist-info` directory names could not be installed ([#7671](https://github.com/python-poetry/poetry/pull/7671)).\n- Fix an issue where `poetry install --compile` compiled with optimization level 1 ([#7666](https://github.com/python-poetry/poetry/pull/7666)).\n\n### Docs\n\n- Clarify the behavior of the `--extras` option ([#7563](https://github.com/python-poetry/poetry/pull/7563)).\n- Expand the FAQ on reasons for slow dependency resolution ([#7620](https://github.com/python-poetry/poetry/pull/7620)).\n\n### poetry-core ([`1.5.2`](https://github.com/python-poetry/poetry-core/releases/tag/1.5.2))\n\n- Fix an issue where wheels built on Windows could contain duplicate entries in the RECORD file ([#555](https://github.com/python-poetry/poetry-core/pull/555)).\n\n\n## [1.4.0] - 2023-02-27\n\n### Added\n\n- **Add a modern installer (`installer.modern-installation`) for faster installation of packages and independence from pip** ([#6205](https://github.com/python-poetry/poetry/pull/6205)).\n- Add support for `Private ::` trove classifiers ([#7271](https://github.com/python-poetry/poetry/pull/7271)).\n- Add the version of poetry in the `@generated` comment at the beginning of the lock file ([#7339](https://github.com/python-poetry/poetry/pull/7339)).\n- Add support for `virtualenvs.prefer-active-python` when running `poetry new` and `poetry init` ([#7100](https://github.com/python-poetry/poetry/pull/7100)).\n\n### Changed\n\n- **Deprecate the old installer, i.e. setting `experimental.new-installer` to `false`** ([#7358](https://github.com/python-poetry/poetry/pull/7358)).\n- Remove unused `platform` field from cached package info and bump the cache version ([#7304](https://github.com/python-poetry/poetry/pull/7304)).\n- Extra dependencies of the root project are now sorted in the lock file ([#7375](https://github.com/python-poetry/poetry/pull/7375)).\n- Remove upper boundary for `importlib-metadata` dependency ([#7434](https://github.com/python-poetry/poetry/pull/7434)).\n- Validate path dependencies during use instead of during construction ([#6844](https://github.com/python-poetry/poetry/pull/6844)).\n- Remove the deprecated `repository` modules ([#7468](https://github.com/python-poetry/poetry/pull/7468)).\n\n### Fixed\n\n- Fix an issue where an unconditional dependency of an extra was not installed in specific environments ([#7175](https://github.com/python-poetry/poetry/pull/7175)).\n- Fix an issue where a pre-release of a dependency was chosen even if a stable release fulfilled the constraint ([#7225](https://github.com/python-poetry/poetry/pull/7225), [#7236](https://github.com/python-poetry/poetry/pull/7236)).\n- Fix an issue where HTTP redirects were not handled correctly during publishing ([#7160](https://github.com/python-poetry/poetry/pull/7160)).\n- Fix an issue where `poetry check` did not handle the `-C, --directory` option correctly ([#7241](https://github.com/python-poetry/poetry/pull/7241)).\n- Fix an issue where the subdirectory information of a git dependency was not written to the lock file ([#7367](https://github.com/python-poetry/poetry/pull/7367)).\n- Fix an issue where the wrong Python version was selected when creating an virtual environment ([#7221](https://github.com/python-poetry/poetry/pull/7221)).\n- Fix an issue where packages that should be kept were uninstalled when calling `poetry install --sync` ([#7389](https://github.com/python-poetry/poetry/pull/7389)).\n- Fix an issue where an incorrect value was set for `sys.argv[0]` when running installed scripts ([#6737](https://github.com/python-poetry/poetry/pull/6737)).\n- Fix an issue where hashes in `direct_url.json` files were not written according to the specification ([#7475](https://github.com/python-poetry/poetry/pull/7475)).\n- Fix an issue where poetry commands failed due to special characters in the path of the project or virtual environment ([#7471](https://github.com/python-poetry/poetry/pull/7471)).\n- Fix an issue where poetry crashed with a `JSONDecodeError` when running a Python script that produced certain warnings ([#6665](https://github.com/python-poetry/poetry/pull/6665)).\n\n### Docs\n\n- Add advice on how to maintain a poetry plugin ([#6977](https://github.com/python-poetry/poetry/pull/6977)).\n- Update tox examples to comply with the latest tox release ([#7341](https://github.com/python-poetry/poetry/pull/7341)).\n- Mention that the `poetry export` can export `constraints.txt` files ([#7383](https://github.com/python-poetry/poetry/pull/7383)).\n- Add clarifications for moving configuration files ([#6864](https://github.com/python-poetry/poetry/pull/6864)).\n- Mention the different types of exact version specifications ([#7503](https://github.com/python-poetry/poetry/pull/7503)).\n\n### poetry-core ([`1.5.1`](https://github.com/python-poetry/poetry-core/releases/tag/1.5.1))\n\n- Improve marker handling ([#528](https://github.com/python-poetry/poetry-core/pull/528),\n  [#534](https://github.com/python-poetry/poetry-core/pull/534),\n  [#530](https://github.com/python-poetry/poetry-core/pull/530),\n  [#546](https://github.com/python-poetry/poetry-core/pull/546),\n  [#547](https://github.com/python-poetry/poetry-core/pull/547)).\n- Validate whether dependencies referenced in `extras` are defined in the main dependency group ([#542](https://github.com/python-poetry/poetry-core/pull/542)).\n- Poetry no longer generates a `setup.py` file in sdists by default ([#318](https://github.com/python-poetry/poetry-core/pull/318)).\n- Fix an issue where trailing newlines were allowed in `tool.poetry.description` ([#505](https://github.com/python-poetry/poetry-core/pull/505)).\n- Fix an issue where the name of the data folder in wheels was not normalized ([#532](https://github.com/python-poetry/poetry-core/pull/532)).\n- Fix an issue where the order of entries in the RECORD file was not deterministic ([#545](https://github.com/python-poetry/poetry-core/pull/545)).\n- Fix an issue where zero padding was not correctly handled in version comparisons ([#540](https://github.com/python-poetry/poetry-core/pull/540)).\n- Fix an issue where sdist builds did not support multiple READMEs ([#486](https://github.com/python-poetry/poetry-core/pull/486)).\n\n### poetry-plugin-export ([`^1.3.0`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.3.0))\n\n- Fix an issue where the export failed if there was a circular dependency on the root package ([#118](https://github.com/python-poetry/poetry-plugin-export/pull/118)).\n\n\n## [1.3.2] - 2023-01-10\n\n### Fixed\n\n- Fix a performance regression when locking dependencies from PyPI ([#7232](https://github.com/python-poetry/poetry/pull/7232)).\n- Fix an issue where passing a relative path via `-C, --directory` fails ([#7266](https://github.com/python-poetry/poetry/pull/7266)).\n\n### Docs\n\n- Update docs to reflect the removal of the deprecated `get-poetry.py` installer from the repository ([#7288](https://github.com/python-poetry/poetry/pull/7288)).\n- Add clarifications for `virtualenvs.path` settings ([#7286](https://github.com/python-poetry/poetry/pull/7286)).\n\n\n## [1.3.1] - 2022-12-12\n\n### Fixed\n\n- Fix an issue where an explicit dependency on `lockfile` was missing, resulting in a broken Poetry in rare circumstances ([7169](https://github.com/python-poetry/poetry/pull/7169)).\n\n\n## [1.3.0] - 2022-12-09\n\n### Added\n\n- Mark the lock file with an `@generated` comment as used by common tooling ([#2773](https://github.com/python-poetry/poetry/pull/2773)).\n- `poetry check` validates trove classifiers and warns for deprecations ([#2881](https://github.com/python-poetry/poetry/pull/2881)).\n- Introduce a top level `-C, --directory` option to set the working path ([#6810](https://github.com/python-poetry/poetry/pull/6810)).\n\n### Changed\n\n- **New lock file format (version 2.0)** ([#6393](https://github.com/python-poetry/poetry/pull/6393)).\n- Path dependency metadata is unconditionally re-locked ([#6843](https://github.com/python-poetry/poetry/pull/6843)).\n- URL dependency hashes are locked ([#7121](https://github.com/python-poetry/poetry/pull/7121)).\n- `poetry update` and `poetry lock` should now resolve dependencies more similarly ([#6477](https://github.com/python-poetry/poetry/pull/6477)).\n- `poetry publish` will report more useful errors when a file does not exist ([#4417](https://github.com/python-poetry/poetry/pull/4417)).\n- `poetry add` will check for duplicate entries using canonical names ([#6832](https://github.com/python-poetry/poetry/pull/6832)).\n- Wheels are preferred to source distributions when gathering metadata ([#6547](https://github.com/python-poetry/poetry/pull/6547)).\n- Git dependencies of extras are only fetched if the extra is requested ([#6615](https://github.com/python-poetry/poetry/pull/6615)).\n- Invoke `pip` with `--no-input` to prevent hanging without feedback ([#6724](https://github.com/python-poetry/poetry/pull/6724), [#6966](https://github.com/python-poetry/poetry/pull/6966)).\n- Invoke `pip` with `--isolated` to prevent the influence of user configuration ([#6531](https://github.com/python-poetry/poetry/pull/6531)).\n- Interrogate environments with Python in isolated (`-I`) mode ([#6628](https://github.com/python-poetry/poetry/pull/6628)).\n- Raise an informative error when multiple version constraints overlap and are incompatible ([#7098](https://github.com/python-poetry/poetry/pull/7098)).\n\n### Fixed\n\n- **Fix an issue where concurrent instances of Poetry would corrupt the artifact cache** ([#6186](https://github.com/python-poetry/poetry/pull/6186)).\n- **Fix an issue where Poetry can hang after being interrupted due to stale locking in cache** ([#6471](https://github.com/python-poetry/poetry/pull/6471)).\n- Fix an issue where the output of commands executed with `--dry-run` contained duplicate entries ([#4660](https://github.com/python-poetry/poetry/pull/4660)).\n- Fix an issue where `requests`'s pool size did not match the number of installer workers ([#6805](https://github.com/python-poetry/poetry/pull/6805)).\n- Fix an issue where `poetry show --outdated` failed with a runtime error related to direct origin dependencies ([#6016](https://github.com/python-poetry/poetry/pull/6016)).\n- Fix an issue where only the last command of an `ApplicationPlugin` is registered ([#6304](https://github.com/python-poetry/poetry/pull/6304)).\n- Fix an issue where git dependencies were fetched unnecessarily when running `poetry lock --no-update` ([#6131](https://github.com/python-poetry/poetry/pull/6131)).\n- Fix an issue where stdout was polluted with messages that should go to stderr ([#6429](https://github.com/python-poetry/poetry/pull/6429)).\n- Fix an issue with `poetry shell` activation and zsh ([#5795](https://github.com/python-poetry/poetry/pull/5795)).\n- Fix an issue where a url dependencies were shown as outdated ([#6396](https://github.com/python-poetry/poetry/pull/6396)).\n- Fix an issue where the `source` field of a dependency with extras was ignored ([#6472](https://github.com/python-poetry/poetry/pull/6472)).\n- Fix an issue where a package from the wrong source was installed for a multiple-constraints dependency with different sources ([#6747](https://github.com/python-poetry/poetry/pull/6747)).\n- Fix an issue where dependencies from different sources where merged during dependency resolution ([#6679](https://github.com/python-poetry/poetry/pull/6679)).\n- Fix an issue where `experimental.system-git-client` could not be used via environment variable ([#6783](https://github.com/python-poetry/poetry/pull/6783)).\n- Fix an issue where Poetry fails with an `AssertionError` due to `distribution.files` being `None` ([#6788](https://github.com/python-poetry/poetry/pull/6788)).\n- Fix an issue where `poetry env info` did not respect `virtualenvs.prefer-active-python` ([#6986](https://github.com/python-poetry/poetry/pull/6986)).\n- Fix an issue where `poetry env list` does not list the in-project environment ([#6979](https://github.com/python-poetry/poetry/pull/6979)).\n- Fix an issue where `poetry env remove` removed the wrong environment ([#6195](https://github.com/python-poetry/poetry/pull/6195)).\n- Fix an issue where the return code of a script was not relayed as exit code ([#6824](https://github.com/python-poetry/poetry/pull/6824)).\n- Fix an issue where the solver could silently swallow `ValueError` ([#6790](https://github.com/python-poetry/poetry/pull/6790)).\n\n### Docs\n\n- Improve documentation of package sources ([#5605](https://github.com/python-poetry/poetry/pull/5605)).\n- Correct the default cache path on Windows ([#7012](https://github.com/python-poetry/poetry/pull/7012)).\n\n### poetry-core ([`1.4.0`](https://github.com/python-poetry/poetry-core/releases/tag/1.4.0))\n\n- The PEP 517 `metadata_directory` is now respected as an input to the `build_wheel` hook ([#487](https://github.com/python-poetry/poetry-core/pull/487)).\n- `ParseConstraintError` is now raised on version and constraint parsing errors, and includes information on the package that caused the error ([#514](https://github.com/python-poetry/poetry-core/pull/514)).\n- Fix an issue where invalid PEP 508 requirements were generated due to a missing space before semicolons ([#510](https://github.com/python-poetry/poetry-core/pull/510)).\n- Fix an issue where relative paths were encoded into package requirements, instead of a file:// URL as required by PEP 508 ([#512](https://github.com/python-poetry/poetry-core/pull/512)).\n\n### poetry-plugin-export ([`^1.2.0`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.2.0))\n\n- Ensure compatibility with Poetry 1.3.0. No functional changes.\n\n### cleo ([`^2.0.0`](https://github.com/python-poetry/poetry-core/releases/tag/2.0.0))\n\n- Fix an issue where shell completions had syntax errors ([#247](https://github.com/python-poetry/cleo/pull/247)).\n- Fix an issue where not reading all the output of a command resulted in a \"Broken pipe\" error ([#165](https://github.com/python-poetry/cleo/pull/165)).\n- Fix an issue where errors were not shown in non-verbose mode ([#166](https://github.com/python-poetry/cleo/pull/166)).\n\n\n## [1.2.2] - 2022-10-10\n\n### Added\n\n- Add forward compatibility for lock file format 2.0, which will be used by Poetry 1.3 ([#6608](https://github.com/python-poetry/poetry/pull/6608)).\n\n### Changed\n\n- Allow `poetry lock` to re-generate the lock file when invalid or incompatible ([#6753](https://github.com/python-poetry/poetry/pull/6753)).\n\n### Fixed\n\n- Fix an issue where the deprecated JSON API was used to query PyPI for available versions of a package ([#6081](https://github.com/python-poetry/poetry/pull/6081)).\n- Fix an issue where versions were escaped wrongly when building the wheel name ([#6476](https://github.com/python-poetry/poetry/pull/6476)).\n- Fix an issue where the installation of dependencies failed if pip is a dependency and is updated in parallel to other dependencies ([#6582](https://github.com/python-poetry/poetry/pull/6582)).\n- Fix an issue where the names of extras were not normalized according to PEP 685 ([#6541](https://github.com/python-poetry/poetry/pull/6541)).\n- Fix an issue where sdist names were not normalized ([#6621](https://github.com/python-poetry/poetry/pull/6621)).\n- Fix an issue where invalid constraints, which are ignored, were only reported in a debug message instead of a warning ([#6730](https://github.com/python-poetry/poetry/pull/6730)).\n- Fix an issue where `poetry shell` was broken in git bash on Windows ([#6560](https://github.com/python-poetry/poetry/pull/6560)).\n\n### Docs\n\n- Rework the README and contribution docs ([#6552](https://github.com/python-poetry/poetry/pull/6552)).\n- Fix for inconsistent docs for multiple-constraint dependencies ([#6604](https://github.com/python-poetry/poetry/pull/6604)).\n- Rephrase plugin configuration ([#6557](https://github.com/python-poetry/poetry/pull/6557)).\n- Add a note about publishable repositories to `publish` ([#6641](https://github.com/python-poetry/poetry/pull/6641)).\n- Fix the path for lazy-loaded bash completion ([#6656](https://github.com/python-poetry/poetry/pull/6656)).\n- Fix a reference to the invalid option `--require` ([#6672](https://github.com/python-poetry/poetry/pull/6672)).\n- Add a PowerShell one-liner to the basic usage section ([#6683](https://github.com/python-poetry/poetry/pull/6683)).\n- Fix the minimum poetry version in the example for plugins ([#6739](https://github.com/python-poetry/poetry/pull/6739)).\n\n### poetry-core ([`1.3.2`](https://github.com/python-poetry/poetry-core/releases/tag/1.3.2))\n\n- Add `3.11` to the list of available Python versions ([#477](https://github.com/python-poetry/poetry-core/pull/477)).\n- Fix an issue where caret constraints of pre-releases with a major version of 0 resulted in an empty version range ([#475](https://github.com/python-poetry/poetry-core/pull/475)).\n\n### poetry-plugin-export ([`^1.1.2`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.1.2))\n\n- Add support for exporting `constraints.txt` files ([#128](https://github.com/python-poetry/poetry-plugin-export/pull/128)).\n- Fix an issue where a relative path passed via `-o` was not interpreted relative to the current working directory ([#130](https://github.com/python-poetry/poetry-plugin-export/pull/130)).\n\n\n## [1.2.1] - 2022-09-16\n\n### Changed\n\n- Bump `poetry-core` to [`1.2.0`](https://github.com/python-poetry/poetry-core/releases/tag/1.2.0).\n- Bump `poetry-plugin-export` to [`^1.0.7`](https://github.com/python-poetry/poetry-plugin-export/releases/tag/1.0.7).\n\n### Fixed\n\n- Fix an issue where `poetry cache clear` did not respect the `-n/--no-interaction` flag ([#6338](https://github.com/python-poetry/poetry/pull/6338)).\n- Fix an issue where `poetry lock --no-update` updated dependencies from non-PyPI package sources ([#6335](https://github.com/python-poetry/poetry/pull/6335)).\n- Fix a `poetry install` performance regression by falling back to internal pip ([#6062](https://github.com/python-poetry/poetry/pull/6062)).\n- Fix an issue where a virtual environment was created unnecessarily when running `poetry export` ([#6282](https://github.com/python-poetry/poetry/pull/6282)).\n- Fix an issue where `poetry lock --no-update` added duplicate hashes to the lock file ([#6389](https://github.com/python-poetry/poetry/pull/6389)).\n- Fix an issue where `poetry install` fails because of missing hashes for `url` dependencies ([#6389](https://github.com/python-poetry/poetry/pull/6389)).\n- Fix an issue where Poetry was not able to update pip in Windows virtual environments ([#6430](https://github.com/python-poetry/poetry/pull/6430)).\n- Fix an issue where Poetry was not able to install releases that contained less common link types ([#5767](https://github.com/python-poetry/poetry/pull/5767)).\n- Fix a `poetry lock` performance regression when checking non-PyPI sources for yanked versions ([#6442](https://github.com/python-poetry/poetry/pull/6442)).\n- Fix an issue where `--no-cache` was not respected when running `poetry install` ([#6479](https://github.com/python-poetry/poetry/pull/6479)).\n- Fix an issue where deprecation warnings for `--dev` were missing ([#6475](https://github.com/python-poetry/poetry/pull/6475)).\n- Fix an issue where Git dependencies failed to clone when `insteadOf` was used in `.gitconfig` using the Dulwich Git client ([#6506](https://github.com/python-poetry/poetry/pull/6506)).\n- Fix an issue where no cache entry is found when calling `poetry cache clear` with a non-normalized package name ([#6537](https://github.com/python-poetry/poetry/pull/6537)).\n- Fix an invalid virtualenv constraint on Poetry ([#6402](https://github.com/python-poetry/poetry/pull/6402)).\n- Fix outdated build system requirements for Poetry ([#6509](https://github.com/python-poetry/poetry/pull/6509)).\n\n### Docs\n\n- Add missing path segment to paths used by install.python-poetry.org ([#6311](https://github.com/python-poetry/poetry/pull/6311)).\n- Add recommendations about how to install Poetry in a CI environment ([#6345](https://github.com/python-poetry/poetry/pull/6345)).\n- Fix examples for `--with` and `--without` ([#6318](https://github.com/python-poetry/poetry/pull/6318)).\n- Update configuration folder path for macOS ([#6395](https://github.com/python-poetry/poetry/pull/6395)).\n- Improve the description of the `virtualenv.create` option ([#6460](https://github.com/python-poetry/poetry/pull/6460)).\n- Clarify that `poetry install` removes dependencies of non-installed extras ([#6229](https://github.com/python-poetry/poetry/pull/6229)).\n- Add a note about `pre-commit autoupdate` and Poetry's hooks ([#6497](https://github.com/python-poetry/poetry/pull/6497)).\n\n\n## [1.2.0] - 2022-08-31\n\n### Docs\n\n- Added note about how to add a git dependency with a subdirectory ([#6218](https://github.com/python-poetry/poetry/pull/6218))\n- Fixed several style issues in the docs ([#6254](https://github.com/python-poetry/poetry/pull/6254))\n- Fixed outdated info about `--only` parameter ([#6263](https://github.com/python-poetry/poetry/pull/6263))\n\n\n## [1.2.0rc2] - 2022-08-26\n\n### Fixed\n\n- Fixed an issue where virtual environments were created unnecessarily when running `poetry self` commands ([#6225](https://github.com/python-poetry/poetry/pull/6225))\n- Ensure that packages' `pretty_name` are written to the lock file ([#6237](https://github.com/python-poetry/poetry/pull/6237))\n\n### Improvements\n\n- Improved the consistency of `Pool().remove_repository()` to make it easier to write poetry plugins ([#6214](https://github.com/python-poetry/poetry/pull/6214))\n\n### Docs\n\n- Removed mentions of Python 2.7 from docs ([#6234](https://github.com/python-poetry/poetry/pull/6234))\n- Added note about the difference between groups and extras ([#6230](https://github.com/python-poetry/poetry/pull/6230))\n\n\n## [1.2.0rc1] - 2022-08-22\n\n### Added\n\n- Added support for subdirectories in git dependencies ([#5172](https://github.com/python-poetry/poetry/pull/5172))\n- Added support for yanked releases and files (PEP-592) ([#5841](https://github.com/python-poetry/poetry/pull/5841))\n- Virtual environments can now be created even with empty project names ([#5856](https://github.com/python-poetry/poetry/pull/5856))\n- Added support for `nushell` in `poetry shell` ([#6063](https://github.com/python-poetry/poetry/pull/6063))\n\n### Changed\n\n- Poetry now falls back to gather metadata for dependencies via pep517 if parsing `pyproject.toml` fails ([#5834](https://github.com/python-poetry/poetry/pull/5834))\n- Replaced Poetry's helper method `canonicalize_name()` with `packaging.utils.canonicalize_name()` ([#6022](https://github.com/python-poetry/poetry/pull/6022))\n- Removed code for the `export` command, which is now provided via plugin ([#6128](https://github.com/python-poetry/poetry/pull/6128))\n- Extras and extras dependencies are now sorted in the lock file ([#6169](https://github.com/python-poetry/poetry/pull/6169))\n- Removed deprecated (1.2-only) CLI options ([#6210](https://github.com/python-poetry/poetry/pull/6210))\n\n### Fixed\n\n- Fixed an issue where symlinks in the lock file were not resolved ([#5850](https://github.com/python-poetry/poetry/pull/5850))\n- Fixed a `tomlkit` regression resulting in inconsistent line endings ([#5870](https://github.com/python-poetry/poetry/pull/5870))\n- Fixed an issue where the `POETRY_PYPI_TOKEN_PYPI` environment variable wasn't respected ([#5911](https://github.com/python-poetry/poetry/pull/5911))\n- Fixed an issue where neither Python nor a managed venv can be found, when using Python from MS Store ([#5931](https://github.com/python-poetry/poetry/pull/5931))\n- Improved error message of `poetry publish` in the event of an upload error ([#6043](https://github.com/python-poetry/poetry/pull/6043))\n- Fixed an issue where `poetry lock` fails without output ([#6058](https://github.com/python-poetry/poetry/pull/6058))\n- Fixed an issue where Windows drive mappings break virtual environment names ([#6110](https://github.com/python-poetry/poetry/pull/6110))\n- `tomlkit` versions with memory leak are now avoided ([#6160](https://github.com/python-poetry/poetry/pull/6160))\n- Fixed an infinite loop in the solver ([#6178](https://github.com/python-poetry/poetry/pull/6178))\n- Fixed an issue where latest version was used instead of locked one for vcs dependencies with extras ([#6185](https://github.com/python-poetry/poetry/pull/6185))\n\n### Docs\n\n- Document use of the `subdirectory` parameter ([#5949](https://github.com/python-poetry/poetry/pull/5949))\n- Document suggested `tox` config for different use cases ([#6026](https://github.com/python-poetry/poetry/pull/6026))\n\n\n## [1.1.15] - 2022-08-22\n\n### Changed\n\n- Poetry now fallback to gather metadata for dependencies via pep517 if parsing pyproject.toml fail ([#6206](https://github.com/python-poetry/poetry/pull/6206))\n- Extras and extras dependencies are now sorted in lock file ([#6207](https://github.com/python-poetry/poetry/pull/6207))\n\n\n## [1.2.0b3] - 2022-07-13\n\n**Important**: This release fixes a critical issue that prevented hashes from being retrieved when locking dependencies,\ndue to a breaking change on PyPI JSON API (see [#5972](https://github.com/python-poetry/poetry/pull/5972)\nand [the upstream change](https://github.com/pypi/warehouse/pull/11775) for more details).\n\nAfter upgrading, you have to clear Poetry cache manually to get that feature working correctly again:\n\n```bash\n$ poetry cache clear pypi --all\n```\n\n### Added\n\n- Added `--only-root` to `poetry install` to install a project without its\n  dependencies ([#5783](https://github.com/python-poetry/poetry/pull/5783))\n\n### Changed\n\n- Improved user experience of `poetry init` ([#5838](https://github.com/python-poetry/poetry/pull/5838))\n- Added default timeout for all HTTP requests, to avoid hanging\n  requests ([#5881](https://github.com/python-poetry/poetry/pull/5881))\n- Updated `poetry init` to better specify how to skip adding\n  dependencies ([#5946](https://github.com/python-poetry/poetry/pull/5946))\n- Updated Poetry repository names to avoid clashes with user-defined\n  repositories ([#5910](https://github.com/python-poetry/poetry/pull/5910))\n\n### Fixed\n\n- Fixed an issue where extras where not handled if they did not match the case-sensitive name of the\n  packages ([#4122](https://github.com/python-poetry/poetry/pull/4122))\n- Fixed configuration of `experimental.system-git-client` option\n  through `poetry config` ([#5818](https://github.com/python-poetry/poetry/pull/5818))\n- Fixed uninstallation of git dependencies on Windows ([#5836](https://github.com/python-poetry/poetry/pull/5836))\n- Fixed an issue where `~` was not correctly expanded\n  in `virtualenvs.path` ([#5848](https://github.com/python-poetry/poetry/pull/5848))\n- Fixed an issue where installing/locking dependencies would hang when setting an incorrect git\n  repository ([#5880](https://github.com/python-poetry/poetry/pull/5880))\n- Fixed an issue in `poetry publish` when keyring was not properly\n  configured ([#5889](https://github.com/python-poetry/poetry/pull/5889))\n- Fixed duplicated line output in console ([#5890](https://github.com/python-poetry/poetry/pull/5890))\n- Fixed an issue where the same wheels where downloaded multiple times during\n  installation ([#5871](https://github.com/python-poetry/poetry/pull/5871))\n- Fixed an issue where dependencies hashes could not be retrieved when locking due to a breaking change on PyPI JSON\n  API ([#5973](https://github.com/python-poetry/poetry/pull/5973))\n- Fixed an issue where a dependency with non-requested extras could not be installed if it is requested with extras by\n  another dependency ([#5770](https://github.com/python-poetry/poetry/pull/5770))\n- Updated git backend to correctly read local/global git config when using dulwich as a git\n  backend ([#5935](https://github.com/python-poetry/poetry/pull/5935))\n- Fixed an issue where optional dependencies where not correctly exported when defining\n  groups ([#5819](https://github.com/python-poetry/poetry/pull/5819))\n\n### Docs\n\n- Fixed configuration instructions for repositories\n  specification ([#5809](https://github.com/python-poetry/poetry/pull/5809))\n- Added a link to dependency specification\n  from `pyproject.toml` ([#5815](https://github.com/python-poetry/poetry/pull/5815))\n- Improved `zsh` autocompletion instructions ([#5859](https://github.com/python-poetry/poetry/pull/5859))\n- Improved installation and update documentations ([#5857](https://github.com/python-poetry/poetry/pull/5857))\n- Improved exact requirements documentation ([#5874](https://github.com/python-poetry/poetry/pull/5874))\n- Added documentation for `@` operator ([#5822](https://github.com/python-poetry/poetry/pull/5822))\n- Improved autocompletion documentation ([#5879](https://github.com/python-poetry/poetry/pull/5879))\n- Improved `scripts` definition documentation ([#5884](https://github.com/python-poetry/poetry/pull/5884))\n\n\n## [1.1.14] - 2022-07-08\n\n### Fixed\n\n- Fixed an issue where dependencies hashes could not be retrieved when locking due to a breaking change on PyPI JSON API ([#5973](https://github.com/python-poetry/poetry/pull/5973))\n\n\n## [1.2.0b2] - 2022-06-07\n\n### Added\n\n- Added support for multiple-constraint direct origin dependencies with the same\n  version ([#5715](https://github.com/python-poetry/poetry/pull/5715))\n- Added support disabling TLS verification for custom package sources via `poetry config certificates.<repository>.cert false` ([#5719](https://github.com/python-poetry/poetry/pull/5719)\n- Added new configuration (`virtualenvs.prompt`) to customize the prompt of the Poetry-managed virtual environment ([#5606](https://github.com/python-poetry/poetry/pull/5606))\n- Added progress indicator to `download_file` (used when downloading dists) ([#5451](https://github.com/python-poetry/poetry/pull/5451))\n- Added `--dry-run` to `poetry version` command ([#5603](https://github.com/python-poetry/poetry/pull/5603))\n- Added `--why` to `poetry show` ([#5444](https://github.com/python-poetry/poetry/pull/5444))\n- Added support for single page (html) repositories ([#5517](https://github.com/python-poetry/poetry/pull/5517))\n- Added support for PEP 508 strings when adding\n  dependencies via `poetry add` command ([#5554](https://github.com/python-poetry/poetry/pull/5554))\n- Added `--no-cache` as a global option ([#5519](https://github.com/python-poetry/poetry/pull/5519))\n- Added cert retrieval for HTTP requests made by Poetry ([#5320](https://github.com/python-poetry/poetry/pull/5320))\n- Added `--skip-existing` to `poetry publish` ([#2812](https://github.com/python-poetry/poetry/pull/2812))\n- Added `--all-extras` to `poetry install` ([#5452](https://github.com/python-poetry/poetry/pull/5452))\n- Added new `poetry self` sub-commands to manage plugins and/or system environment packages, eg: keyring backends ([#5450](https://github.com/python-poetry/poetry/pull/5450))\n- Added new configuration (`installer.no-binary`) to allow selection of non-binary distributions when installing a dependency ([#5609](https://github.com/python-poetry/poetry/pull/5609))\n\n### Changed\n\n- `poetry plugin` commands are now deprecated in favor of the more generic `poetry self`\n  commands ([#5450](https://github.com/python-poetry/poetry/pull/5450))\n- When creating new projects, Poetry no longer restricts README extensions to `md` and `rst` ([#5357](https://github.com/python-poetry/poetry/pull/5357))\n- Changed the provider to allow fallback to installed packages ([#5704](https://github.com/python-poetry/poetry/pull/5704))\n- Solver now correctly handles and prefers direct reference constraints (vcs, file etc.) over public version identifiers ([#5654](https://github.com/python-poetry/poetry/pull/5654))\n- Changed the build script behavior to create an ephemeral build environment when a build script is\n  specified ([#5401](https://github.com/python-poetry/poetry/pull/5401))\n- Improved performance when determining PEP 517 metadata from sources ([#5601](https://github.com/python-poetry/poetry/pull/5601))\n- Project package sources no longer need to be redefined as global repositories when configuring credentials ([#5563](https://github.com/python-poetry/poetry/pull/5563))\n- Replaced external git command use with dulwich, in order to force the legacy behaviour set `experimental.system-git-client` configuration to `true` ([#5428](https://github.com/python-poetry/poetry/pull/5428))\n- Improved http request handling for sources and multiple paths on same netloc ([#5518](https://github.com/python-poetry/poetry/pull/5518))\n- Made `no-pip` and `no-setuptools` configuration explicit ([#5455](https://github.com/python-poetry/poetry/pull/5455))\n- Improved application logging, use of `-vv` now provides more debug information ([#5503](https://github.com/python-poetry/poetry/pull/5503))\n- Renamed implicit group `default` to `main` ([#5465](https://github.com/python-poetry/poetry/pull/5465))\n- Replaced in-tree implementation of `poetry export`\n  with `poetry-plugin-export` ([#5413](https://github.com/python-poetry/poetry/pull/5413))\n- Changed the password manager behavior to use a `\"null\"` keyring when\n  disabled ([#5251](https://github.com/python-poetry/poetry/pull/5251))\n- Incremental improvement of Solver performance ([#5335](https://github.com/python-poetry/poetry/pull/5335))\n- Newly created virtual environments on macOS now are excluded from Time Machine backups ([#4599](https://github.com/python-poetry/poetry/pull/4599))\n- Poetry no longer raises an exception when a package is not found on PyPI ([#5698](https://github.com/python-poetry/poetry/pull/5698))\n- Update `packaging` dependency to use major version 21, this change forces Poetry to drop support for managing Python 2.7 environments ([#4749](https://github.com/python-poetry/poetry/pull/4749))\n\n### Fixed\n\n- Fixed `poetry update --dry-run` to not modify `poetry.lock` ([#5718](https://github.com/python-poetry/poetry/pull/5718), [#3666](https://github.com/python-poetry/poetry/issues/3666), [#3766](https://github.com/python-poetry/poetry/issues/3766))\n- Fixed [#5537](https://github.com/python-poetry/poetry/issues/5537) where export fails to resolve dependencies with more than one\n  path ([#5688](https://github.com/python-poetry/poetry/pull/5688))\n- Fixed an issue where the environment variables `POETRY_CONFIG_DIR` and `POETRY_CACHE_DIR` were not being respected ([#5672](https://github.com/python-poetry/poetry/pull/5672))\n- Fixed [#3628](https://github.com/python-poetry/poetry/issues/3628) and [#4702](https://github.com/python-poetry/poetry/issues/4702) by handling invalid distributions\n  gracefully ([#5645](https://github.com/python-poetry/poetry/pull/5645))\n- Fixed an issue where the provider ignored subdirectory when merging and improve subdirectory support for vcs\n  deps ([#5648](https://github.com/python-poetry/poetry/pull/5648))\n- Fixed an issue where users could not select an empty choice when selecting\n  dependencies ([#4606](https://github.com/python-poetry/poetry/pull/4606))\n- Fixed an issue where `poetry init -n` crashes in a root directory ([#5612](https://github.com/python-poetry/poetry/pull/5612))\n- Fixed an issue where Solver errors arise due to wheels having different Python\n  constraints ([#5616](https://github.com/python-poetry/poetry/pull/5616))\n- Fixed an issue where editable path dependencies using `setuptools` could not be correctly installed ([#5590](https://github.com/python-poetry/poetry/pull/5590))\n- Fixed flicker when displaying executor operations ([#5556](https://github.com/python-poetry/poetry/pull/5556))\n- Fixed an issue where the `poetry lock --no-update` only sorted by name and not by name and\n  version ([#5446](https://github.com/python-poetry/poetry/pull/5446))\n- Fixed an issue where the Solver fails when a dependency has multiple constrained dependency definitions for the same\n  package ([#5403](https://github.com/python-poetry/poetry/pull/5403))\n- Fixed an issue where dependency resolution takes a while because Poetry checks all possible combinations\n  even markers are mutually exclusive ([#4695](https://github.com/python-poetry/poetry/pull/4695))\n- Fixed incorrect version selector constraint ([#5500](https://github.com/python-poetry/poetry/pull/5500))\n- Fixed an issue where `poetry lock --no-update` dropped\n  packages ([#5435](https://github.com/python-poetry/poetry/pull/5435))\n- Fixed an issue where packages were incorrectly grouped when\n  exporting ([#5156](https://github.com/python-poetry/poetry/pull/5156))\n- Fixed an issue where lockfile always updates when using private\n  sources ([#5362](https://github.com/python-poetry/poetry/pull/5362))\n- Fixed an issue where the solver did not account for selected package features ([#5305](https://github.com/python-poetry/poetry/pull/5305))\n- Fixed an issue with console script execution of editable dependencies on Windows ([#3339](https://github.com/python-poetry/poetry/pull/3339))\n- Fixed an issue where editable builder did not write PEP-610 metadata ([#5703](https://github.com/python-poetry/poetry/pull/5703))\n- Fixed an issue where Poetry 1.1 lock files were incorrectly identified as not fresh ([#5458](https://github.com/python-poetry/poetry/pull/5458))\n\n### Docs\n\n- Updated plugin management commands ([#5450](https://github.com/python-poetry/poetry/pull/5450))\n- Added the `--readme` flag to documentation ([#5357](https://github.com/python-poetry/poetry/pull/5357))\n- Added example for multiple maintainers ([#5661](https://github.com/python-poetry/poetry/pull/5661))\n- Updated documentation for issues [#4800](https://github.com/python-poetry/poetry/issues/4800), [#3709](https://github.com/python-poetry/poetry/issues/3709), [#3573](https://github.com/python-poetry/poetry/issues/3573), [#2211](https://github.com/python-poetry/poetry/issues/2211) and [#2414](https://github.com/python-poetry/poetry/pull/2414) ([#5656](https://github.com/python-poetry/poetry/pull/5656))\n- Added `poetry.toml` note in configuration ([#5492](https://github.com/python-poetry/poetry/pull/5492))\n- Add documentation for `poetry about`, `poetry help`, `poetrylist`, and the `--full-path` and `--all` options\n  documentation ([#5664](https://github.com/python-poetry/poetry/pull/5664))\n- Added more clarification to the `--why` flag ([#5653](https://github.com/python-poetry/poetry/pull/5653))\n- Updated documentation to refer to PowerShell for Windows, including\n  instructions ([#3978](https://github.com/python-poetry/poetry/pull/3978), [#5618](https://github.com/python-poetry/poetry/pull/5618))\n- Added PEP 508 name requirement ([#5642](https://github.com/python-poetry/poetry/pull/5642))\n- Added example for each section of pyproject.toml ([#5585](https://github.com/python-poetry/poetry/pull/5642))\n- Added documentation for `--local` to fix issue [#5623](https://github.com/python-poetry/poetry/issues/5623) ([#5629](https://github.com/python-poetry/poetry/pull/5629))\n- Added troubleshooting documentation for using proper quotation with\n  ZSH ([#4847](https://github.com/python-poetry/poetry/pull/4847))\n- Added information on git and basic http auth ([#5578](https://github.com/python-poetry/poetry/pull/5578))\n- Removed ambiguity about PEP 440 and semver ([#5576](https://github.com/python-poetry/poetry/pull/5576))\n- Removed Pipenv comparison ([#5561](https://github.com/python-poetry/poetry/pull/5561))\n- Improved dependency group related documentation ([#5338](https://github.com/python-poetry/poetry/pull/5338))\n- Added documentation for default directories used by Poetry ([#5391](https://github.com/python-poetry/poetry/pull/5301))\n- Added warning about credentials preserved in shell history ([#5726](https://github.com/python-poetry/poetry/pull/5726))\n- Improved documentation of the `readme` option, including multiple files and additional formats ([#5158](https://github.com/python-poetry/poetry/pull/5158))\n- Improved contributing documentation ([#5708](https://github.com/python-poetry/poetry/pull/5708))\n- Remove all references to `--dev-only` option ([#5771](https://github.com/python-poetry/poetry/pull/5771))\n\n\n## [1.2.0b1] - 2022-03-17\n\n### Fixed\n\n- Fixed an issue where the system environment couldn't be detected ([#4406](https://github.com/python-poetry/poetry/pull/4406)).\n- Fixed another issue where the system environment couldn't be detected ([#4433](https://github.com/python-poetry/poetry/pull/4433)).\n- Replace deprecated requests parameter in uploader ([#4580](https://github.com/python-poetry/poetry/pull/4580)).\n- Fix an issue where venv are detected as broken when using MSys2 on windows ([#4482](https://github.com/python-poetry/poetry/pull/4482)).\n- Fixed an issue where the cache breaks on windows ([#4531](https://github.com/python-poetry/poetry/pull/4531)).\n- Fixed an issue where a whitespace before a semicolon was missing on `poetry export` ([#4575](https://github.com/python-poetry/poetry/issues/4575)).\n- Fixed an issue where markers were not correctly assigned to nested dependencies ([#3511](https://github.com/python-poetry/poetry/issues/3511)).\n- Recognize one digit version in wheel filenames ([#3338](https://github.com/python-poetry/poetry/pull/3338)).\n- Fixed an issue when `locale` is unset ([#4038](https://github.com/python-poetry/poetry/pull/4038)).\n- Fixed an issue where the fallback to another interpreter didn't work ([#3475](https://github.com/python-poetry/poetry/pull/3475)).\n- Merge any marker constraints into constraints with specific markers ([#4590](https://github.com/python-poetry/poetry/pull/4590)).\n- Normalize path before hashing so that the generated venv name is independent of case on Windows ([#4813](https://github.com/python-poetry/poetry/pull/4813)).\n- Fixed an issue where a dependency wasn't upgrade by using `@latest` on `poetry update` ([#4945](https://github.com/python-poetry/poetry/pull/4945)).\n- Fixed an issue where conda envs in windows are always reported as broken([#5007](https://github.com/python-poetry/poetry/pull/5007)).\n- Fixed an issue where Poetry doesn't find its own venv on `poetry self update` ([#5049](https://github.com/python-poetry/poetry/pull/5049)).\n- Fix misuse of pretty_constraint ([#4932](https://github.com/python-poetry/poetry/pull/4932)).\n- Fixed an issue where the reported python version used for venv creation wasn't correct ([#5086](https://github.com/python-poetry/poetry/pull/5086)).\n- Fixed an issue where the searched package wasn't display in the interactive dialog of `poetry init` ([#5076](https://github.com/python-poetry/poetry/pull/5076)).\n- Fixed an issue where Poetry raises an exception on `poetry show` when no lock files exists ([#5242](https://github.com/python-poetry/poetry/pull/5242)).\n- Fixed an issue where Poetry crashes when optional `vcs_info.requested_version` in `direct_url.json` wasn't included ([#5274](https://github.com/python-poetry/poetry/pull/5274)).\n- Fixed an issue where dependencies with extras were updated despite using `--no-update` ([#4618](https://github.com/python-poetry/poetry/pull/4618)).\n- Fixed various places where poetry writes messages to stdout instead of stderr ([#4110](https://github.com/python-poetry/poetry/pull/4110), [#5179](https://github.com/python-poetry/poetry/pull/5179)).\n- Ensured that when complete packages are created dependency inherits source and resolved refs from package ([#4604](https://github.com/python-poetry/poetry/pull/4604)).\n- Ensured that when complete packages are created dependency inherits subdirectory from package if supported ([#4604](https://github.com/python-poetry/poetry/pull/4604)).\n- Fixed an issue where `POETRY_EXPERIMENTAL_NEW_INSTALLER` needs to be set to an empty string to disable it ([#3811](https://github.com/python-poetry/poetry/pull/3811)).\n\n### Added\n\n- `poetry show <package>` now also shows which packages depend on it ([#2351](https://github.com/python-poetry/poetry/pull/2351)).\n- The info dialog by `poetry about` now contains version information about installed poetry and poetry-core ([#5288](https://github.com/python-poetry/poetry/pull/5288)).\n- Print error message when `poetry publish` fails ([#3549](https://github.com/python-poetry/poetry/pull/3549)).\n- Added in info output to `poetry lock --check` ([#5081](https://github.com/python-poetry/poetry/pull/5081)).\n- Added new argument `--all` for `poetry env remove` to delete all venv of a project at once ([#3212](https://github.com/python-poetry/poetry/pull/3212)).\n- Added new argument `--without-urls` for `poetry export` to exclude source repository urls from the exported file ([#4763](https://github.com/python-poetry/poetry/pull/4763)).\n- Added a new `installer.max-workers` property to the configuration ([#3516](https://github.com/python-poetry/poetry/pull/3516)).\n- Added experimental option `virtualenvs.prefer-active-python` to detect current activated python ([#4852](https://github.com/python-poetry/poetry/pull/4852)).\n- Added better windows shell support ([#5053](https://github.com/python-poetry/poetry/pull/5053)).\n\n### Changed\n\n- Drop python3.6 support ([#5055](https://github.com/python-poetry/poetry/pull/5055)).\n- Exit with callable return code in generated script ([#4456](https://github.com/python-poetry/poetry/pull/4456)).\n- Internal use of the `pep517` high level interfaces for package metadata inspections have been replaced with the `build` package. ([#5155](https://github.com/python-poetry/poetry/pull/5155)).\n- Poetry now raises an error if the python version in the project environment is no longer compatible with the project ([#4520](https://github.com/python-poetry/poetry/pull/4520)).\n\n\n## [1.1.13] - 2022-02-10\n\n### Fixed\n\n- Fixed an issue where envs in MSYS2 always reported as broken ([#4942](https://github.com/python-poetry/poetry/pull/4942))\n- Fixed an issue where conda envs in windows are always reported as broken([#5008](https://github.com/python-poetry/poetry/pull/5008))\n- Fixed an issue where Poetry doesn't find its own venv on `poetry self update` ([#5048](https://github.com/python-poetry/poetry/pull/5048))\n\n\n## [1.1.12] - 2021-11-27\n\n### Fixed\n\n- Fixed broken caches on Windows due to `Path` starting with a slash ([#4549](https://github.com/python-poetry/poetry/pull/4549))\n- Fixed `JSONDecodeError` when installing packages by updating `cachecontrol` version ([#4831](https://github.com/python-poetry/poetry/pull/4831))\n- Fixed dropped markers in dependency walk ([#4686](https://github.com/python-poetry/poetry/pull/4686))\n\n\n## [1.1.11] - 2021-10-04\n\n### Fixed\n\n- Fixed errors when installing packages on Python 3.10. ([#4592](https://github.com/python-poetry/poetry/pull/4592))\n- Fixed an issue where the wrong `git` executable could be used on Windows. ([python-poetry/poetry-core#213](https://github.com/python-poetry/poetry-core/pull/213))\n- Fixed an issue where the Python 3.10 classifier was not automatically added. ([python-poetry/poetry-core#215](https://github.com/python-poetry/poetry-core/pull/215))\n\n\n## [1.1.10] - 2021-09-21\n\n### Fixed\n\n- Fixed an issue where non-sha256 hashes were not checked. ([#4529](https://github.com/python-poetry/poetry/pull/4529))\n\n\n## [1.1.9] - 2021-09-18\n\n### Fixed\n\n- Fixed a security issue where file hashes were not checked prior to installation. ([#4420](https://github.com/python-poetry/poetry/pull/4420), [#4444](https://github.com/python-poetry/poetry/pull/4444), [python-poetry/poetry-core#193](https://github.com/python-poetry/poetry-core/pull/193))\n- Fixed the detection of the system environment when the setting `virtualenvs.create` is deactivated. ([#4507](https://github.com/python-poetry/poetry/pull/4507))\n- Fixed an issue where unsafe parameters could be passed to `git` commands. ([python-poetry/poetry-core#203](https://github.com/python-poetry/poetry-core/pull/203))\n- Fixed an issue where the wrong `git` executable could be used on Windows. ([python-poetry/poetry-core#205](https://github.com/python-poetry/poetry-core/pull/205))\n\n\n## [1.1.8] - 2021-08-19\n\n### Fixed\n\n- Fixed an error with repository prioritization when specifying secondary repositories. ([#4241](https://github.com/python-poetry/poetry/pull/4241))\n- Fixed the detection of the system environment when the setting `virtualenvs.create` is deactivated. ([#4330](https://github.com/python-poetry/poetry/pull/4330), [#4407](https://github.com/python-poetry/poetry/pull/4407))\n- Fixed the evaluation of relative path dependencies. ([#4246](https://github.com/python-poetry/poetry/pull/4246))\n- Fixed environment detection for Python 3.10 environments. ([#4387](https://github.com/python-poetry/poetry/pull/4387))\n- Fixed an error in the evaluation of `in/not in` markers ([python-poetry/poetry-core#189](https://github.com/python-poetry/poetry-core/pull/189))\n\n\n## [1.2.0a2] - 2021-08-01\n\n### Added\n\n- Poetry now supports dependency groups. ([#4260](https://github.com/python-poetry/poetry/pull/4260))\n- The `install` command now supports a `--sync` option to synchronize the environment with the lock file. ([#4336](https://github.com/python-poetry/poetry/pull/4336))\n\n### Changed\n\n- Improved the way credentials are retrieved to better support keyring backends. ([#4086](https://github.com/python-poetry/poetry/pull/4086))\n- The `--remove-untracked` option of the `install` command is now deprecated in favor of the new `--sync` option. ([#4336](https://github.com/python-poetry/poetry/pull/4336))\n- The user experience when installing dependency groups has been improved. ([#4336](https://github.com/python-poetry/poetry/pull/4336))\n\n### Fixed\n\n- Fixed performance issues when resolving dependencies. ([#3839](https://github.com/python-poetry/poetry/pull/3839))\n- Fixed an issue where transitive dependencies of directory or VCS dependencies were not installed or otherwise removed. ([#4202](https://github.com/python-poetry/poetry/pull/4202))\n- Fixed the behavior of the `init` command in non-interactive mode. ([#2899](https://github.com/python-poetry/poetry/pull/2899))\n- Fixed the detection of the system environment when the setting `virtualenvs.create` is deactivated. ([#4329](https://github.com/python-poetry/poetry/pull/4329))\n- Fixed the display of possible solutions for some common errors. ([#4332](https://github.com/python-poetry/poetry/pull/4332))\n\n\n## [1.1.7] - 2021-06-25\n\n**Note**: Lock files might need to be regenerated for the first fix below to take effect.\nYou can use `poetry lock` to do so **without** the `--no-update` option.\n\n### Changed\n\n- This release is compatible with the `install-poetry.py` installation script to ease the migration path from `1.1` releases to `1.2` releases. ([#4192](https://github.com/python-poetry/poetry/pull/4192))\n\n### Fixed\n\n- Fixed an issue where transitive dependencies of directory or VCS dependencies were not installed or otherwise removed. ([#4203](https://github.com/python-poetry/poetry/pull/4203))\n- Fixed an issue where the combination of the `--tree` and `--no-dev` options for the show command was still displaying development dependencies. ([#3992](https://github.com/python-poetry/poetry/pull/3992))\n\n## [1.2.0a1] - 2021-05-21\n\nThis release is the first testing release of the upcoming 1.2.0 version.\n\nIt **drops** support for Python 2.7 and 3.5.\n\n### Added\n\n- Poetry now supports a plugin system to alter or expand Poetry's functionality. ([#3733](https://github.com/python-poetry/poetry/pull/3733))\n- Poetry now supports [PEP 610](https://www.python.org/dev/peps/pep-0610/). ([#3876](https://github.com/python-poetry/poetry/pull/3876))\n- Several configuration options to better control the way virtual environments are created are now available. ([#3157](https://github.com/python-poetry/poetry/pull/3157), [#3711](https://github.com/python-poetry/poetry/pull/3711)).\n- The `new` command now supports namespace packages. ([#2768](https://github.com/python-poetry/poetry/pull/2768))\n- The `add` command now supports the `--editable` option to add packages in editable mode. ([#3940](https://github.com/python-poetry/poetry/pull/3940))\n\n### Changed\n\n- Python 2.7 and 3.5 are no longer supported. ([#3405](https://github.com/python-poetry/poetry/pull/3405))\n- The usage of the `get-poetry.py` script is now deprecated and is replaced by the `install-poetry.py` script. ([#3706](https://github.com/python-poetry/poetry/pull/3706))\n- Directory dependencies are now in non-develop mode by default. ([poetry-core#98](https://github.com/python-poetry/poetry-core/pull/98))\n- Improved support for PEP 440 specific versions that do not abide by semantic versioning. ([poetry-core#140](https://github.com/python-poetry/poetry-core/pull/140))\n- Improved the CLI experience and performance by migrating to the latest version of Cleo. ([#3618](https://github.com/python-poetry/poetry/pull/3618))\n- Packages previously considered as unsafe (`pip`, `setuptools`, `wheels` and `distribute`) can now be managed as any other package. ([#2826](https://github.com/python-poetry/poetry/pull/2826))\n- The `new` command now defaults to the Markdown format for README files. ([#2768](https://github.com/python-poetry/poetry/pull/2768))\n\n### Fixed\n\n- Fixed an error where command line options were not taken into account when using the `run` command. ([#3618](https://github.com/python-poetry/poetry/pull/3618))\n- Fixed an error in the way custom repositories were resolved. ([#3406](https://github.com/python-poetry/poetry/pull/3406))\n\n\n## [1.1.6] - 2021-04-14\n\n### Fixed\n\n- Fixed export format for path dependencies. ([#3121](https://github.com/python-poetry/poetry/pull/3121))\n- Fixed errors caused by environment modification when executing some commands. ([#3253](https://github.com/python-poetry/poetry/pull/3253))\n- Fixed handling of wheel files with single-digit versions. ([#3338](https://github.com/python-poetry/poetry/pull/3338))\n- Fixed an error when handling single-digit Python markers. ([poetry-core#156](https://github.com/python-poetry/poetry-core/pull/156))\n- Fixed dependency markers not being properly copied when changing the constraint leading to resolution errors. ([poetry-core#163](https://github.com/python-poetry/poetry-core/pull/163))\n- Fixed an error where VCS dependencies were always updated. ([#3947](https://github.com/python-poetry/poetry/pull/3947))\n- Fixed an error where the incorrect version of a package was locked when using environment markers. ([#3945](https://github.com/python-poetry/poetry/pull/3945))\n\n\n## [1.1.5] - 2021-03-04\n\n### Fixed\n\n- Fixed an error in the `export` command when no lock file existed and a verbose flag was passed to the command. ([#3310](https://github.com/python-poetry/poetry/pull/3310))\n- Fixed an error where the `pyproject.toml` was not reverted when using the `add` command. ([#3622](https://github.com/python-poetry/poetry/pull/3622))\n- Fixed errors when using non-HTTPS indices. ([#3622](https://github.com/python-poetry/poetry/pull/3622))\n- Fixed errors when handling simple indices redirection. ([#3622](https://github.com/python-poetry/poetry/pull/3622))\n- Fixed errors when trying to handle newer wheels by using the latest version of `poetry-core` and `packaging`. ([#3677](https://github.com/python-poetry/poetry/pull/3677))\n- Fixed an error when using some versions of `poetry-core` due to an incorrect import . ([#3696](https://github.com/python-poetry/poetry/pull/3696))\n\n\n## [1.1.4] - 2020-10-23\n\n### Added\n\n- Added `installer.parallel` boolean flag (defaults to `true`) configuration to enable/disable parallel execution of operations when using the new installer. ([#3088](https://github.com/python-poetry/poetry/pull/3088))\n\n### Changed\n\n- When using system environments as an unprivileged user, user site and bin directories are created if they do not already exist. ([#3107](https://github.com/python-poetry/poetry/pull/3107))\n\n### Fixed\n\n- Fixed editable installation of poetry projects when using system environments. ([#3107](https://github.com/python-poetry/poetry/pull/3107))\n- Fixed locking of nested extra activations. If you were affected by this issue, you will need to regenerate the lock file using `poetry lock --no-update`. ([#3229](https://github.com/python-poetry/poetry/pull/3229))\n- Fixed prioritisation of non-default custom package sources. ([#3251](https://github.com/python-poetry/poetry/pull/3251))\n- Fixed detection of installed editable packages when non-poetry managed `.pth` file exists. ([#3210](https://github.com/python-poetry/poetry/pull/3210))\n- Fixed scripts generated by editable builder to use valid import statements. ([#3214](https://github.com/python-poetry/poetry/pull/3214))\n- Fixed recursion error when locked dependencies contain cyclic dependencies. ([#3237](https://github.com/python-poetry/poetry/pull/3237))\n- Fixed propagation of editable flag for VCS dependencies. ([#3264](https://github.com/python-poetry/poetry/pull/3264))\n\n\n## [1.1.3] - 2020-10-14\n\n### Changed\n\n- Python version support deprecation warning is now written to `stderr`. ([#3131](https://github.com/python-poetry/poetry/pull/3131))\n\n### Fixed\n\n- Fixed `KeyError` when `PATH` is not defined in environment variables. ([#3159](https://github.com/python-poetry/poetry/pull/3159))\n- Fixed error when using `config` command in a directory with an existing `pyproject.toml` without any Poetry configuration. ([#3172](https://github.com/python-poetry/poetry/pull/3172))\n- Fixed incorrect inspection of package requirements when same dependency is specified multiple times with unique markers. ([#3147](https://github.com/python-poetry/poetry/pull/3147))\n- Fixed `show` command to use already resolved package metadata. ([#3117](https://github.com/python-poetry/poetry/pull/3117))\n- Fixed multiple issues with `export` command output when using `requirements.txt` format. ([#3119](https://github.com/python-poetry/poetry/pull/3119))\n\n\n## [1.1.2] - 2020-10-06\n\n### Changed\n\n- Dependency installation of editable packages and all uninstall operations are now performed serially within their corresponding priority groups. ([#3099](https://github.com/python-poetry/poetry/pull/3099))\n- Improved package metadata inspection of nested poetry projects within project path dependencies. ([#3105](https://github.com/python-poetry/poetry/pull/3105))\n\n### Fixed\n\n- Fixed export of `requirements.txt` when project dependency contains git dependencies. ([#3100](https://github.com/python-poetry/poetry/pull/3100))\n\n\n## [1.1.1] - 2020-10-05\n\n### Added\n\n- Added `--no-update` option to `lock` command. ([#3034](https://github.com/python-poetry/poetry/pull/3034))\n\n### Fixed\n\n- Fixed resolution of packages with missing required extras. ([#3035](https://github.com/python-poetry/poetry/pull/3035))\n- Fixed export of `requirements.txt` dependencies to include development dependencies. ([#3024](https://github.com/python-poetry/poetry/pull/3024))\n- Fixed incorrect selection of unsupported binary distribution formats when selecting a package artifact to install. ([#3058](https://github.com/python-poetry/poetry/pull/3058))\n- Fixed incorrect use of system executable when building package distributions via `build` command. ([#3056](https://github.com/python-poetry/poetry/pull/3056))\n- Fixed errors in `init` command when specifying `--dependency` in non-interactive mode when a `pyproject.toml` file already exists. ([#3076](https://github.com/python-poetry/poetry/pull/3076))\n- Fixed incorrect selection of configured source url when a publish repository url configuration with the same name already exists. ([#3047](https://github.com/python-poetry/poetry/pull/3047))\n- Fixed dependency resolution issues when the same package is specified in multiple dependency extras. ([#3046](https://github.com/python-poetry/poetry/pull/3046))\n\n\n## [1.1.0] - 2020-10-01\n\n### Changed\n\n- The `init` command will now use existing `pyproject.toml` if possible ([#2448](https://github.com/python-poetry/poetry/pull/2448)).\n- Error messages when metadata information retrieval fails have been improved  ([#2997](https://github.com/python-poetry/poetry/pull/2997)).\n\n### Fixed\n\n- Fixed parsing of version constraint for `rc` prereleases ([#2978](https://github.com/python-poetry/poetry/pull/2978)).\n- Fixed how some metadata information are extracted from `setup.cfg` files ([#2957](https://github.com/python-poetry/poetry/pull/2957)).\n- Fixed return codes returned by the executor ([#2981](https://github.com/python-poetry/poetry/pull/2981)).\n- Fixed whitespaces not being accepted for the list of extras when adding packages ([#2985](https://github.com/python-poetry/poetry/pull/2985)).\n- Fixed repositories specified in the `pyproject.toml` file not being taken into account for authentication when downloading packages ([#2990](https://github.com/python-poetry/poetry/pull/2990)).\n- Fixed permission errors when installing the root project if the `site-packages` directory is not writeable ([#3002](https://github.com/python-poetry/poetry/pull/3002)).\n- Fixed environment marker propagation when exporting to the `requirements.txt` format ([#3002](https://github.com/python-poetry/poetry/pull/3002)).\n- Fixed errors when paths in run command contained spaces ([#3015](https://github.com/python-poetry/poetry/pull/3015)).\n\n\n## [1.1.0rc1] - 2020-09-25\n\n### Changed\n\n- The `virtualenvs.in-project` setting will now always be honored, if set explicitly, regardless of the presence of a `.venv` directory ([#2771](https://github.com/python-poetry/poetry/pull/2771)).\n- Adding packages already present in the `pyproject.toml` file will no longer raise an error ([#2886](https://github.com/python-poetry/poetry/pull/2886)).\n- Errors when authenticating against custom repositories will now be logged ([#2577](https://github.com/python-poetry/poetry/pull/2577)).\n\n### Fixed\n\n- Fixed an error on Python 3.5 when resolving URL dependencies ([#2954](https://github.com/python-poetry/poetry/pull/2954)).\n- Fixed the `dependency` option of the `init` command being ignored ([#2587](https://github.com/python-poetry/poetry/pull/2587)).\n- Fixed the `show` command displaying erroneous information following the changes in the lock file format ([#2967](https://github.com/python-poetry/poetry/pull/2967)).\n- Fixed dependency resolution errors due to invalid python constraints propagation ([#2968](https://github.com/python-poetry/poetry/pull/2968)).\n\n\n## [1.1.0b4] - 2020-09-23\n\n### Changed\n\n- When running under Python 2.7 on Windows, install command will be limited to one worker to mitigate threading issue ([#2941](https://github.com/python-poetry/poetry/pull/2941)).\n\n\n## [1.1.0b3] - 2020-09-18\n\n### Changed\n\n- Improved the error reporting when HTTP error are encountered for legacy repositories ([#2459](https://github.com/python-poetry/poetry/pull/2459)).\n- When displaying the name of packages retrieved from remote repositories, the original name will now be used ([#2305](https://github.com/python-poetry/poetry/pull/2305)).\n- Failed package downloads will now be retried on connection errors ([#2813](https://github.com/python-poetry/poetry/pull/2813)).\n- Path dependencies will now be installed as editable only when `develop` option is set to `true` ([#2887](https://github.com/python-poetry/poetry/pull/2887)).\n\n### Fixed\n\n- Fixed the detection of the type of installed packages ([#2722](https://github.com/python-poetry/poetry/pull/2722)).\n- Fixed deadlocks when installing packages on systems not supporting non-ascii characters ([#2721](https://github.com/python-poetry/poetry/pull/2721)).\n- Fixed handling of wildcard constraints for packages with prereleases only ([#2821](https://github.com/python-poetry/poetry/pull/2821)).\n- Fixed dependencies of some packages not being discovered by ensuring we use the PEP-516 backend if specified ([#2810](https://github.com/python-poetry/poetry/pull/2810)).\n- Fixed recursion errors when retrieving extras ([#2787](https://github.com/python-poetry/poetry/pull/2787)).\n- Fixed `PyPI` always being displayed when publishing even for custom repositories ([#2905](https://github.com/python-poetry/poetry/pull/2905)).\n- Fixed handling of packages extras when resolving dependencies ([#2887](https://github.com/python-poetry/poetry/pull/2887)).\n\n\n## [1.1.0b2] - 2020-07-24\n\n### Changed\n\n- Added support for build scripts without the `setup.py` file generation in the editable builder ([#2718](https://github.com/python-poetry/poetry/pull/2718)).\n\n### Fixed\n\n- Fixed an error occurring when using older lock files ([#2717](https://github.com/python-poetry/poetry/pull/2717)).\n\n\n## [1.1.0b1] - 2020-07-24\n\n### Changed\n\n- Virtual environments will now exclusively be built with `virtualenv` ([#2666](https://github.com/python-poetry/poetry/pull/2666)).\n- Support for Python 2.7 and 3.5 is now officially deprecated and a warning message will be displayed ([#2683](https://github.com/python-poetry/poetry/pull/2683)).\n- Improved metadata inspection of packages by using the PEP-517 build system ([#2632](https://github.com/python-poetry/poetry/pull/2632)).\n\n### Fixed\n\n- Fixed parallel tasks not being cancelled when the installation is interrupted or has failed ([#2656](https://github.com/python-poetry/poetry/pull/2656)).\n- Fixed an error where the editable builder would not expose all packages ([#2664](https://github.com/python-poetry/poetry/pull/2656)).\n- Fixed an error for Python 2.7 when a file could not be downloaded in the installer ([#2709](https://github.com/python-poetry/poetry/pull/2709)).\n- Fixed the lock file `content-hash` value not being updated when using the `add` and `remove` commands ([#2710](https://github.com/python-poetry/poetry/pull/2710)).\n- Fixed incorrect resolution errors being raised for packages with python requirements ([#2712](https://github.com/python-poetry/poetry/pull/2712)).\n- Fixed an error causing the build log messages to no longer be displayed ([#2715](https://github.com/python-poetry/poetry/pull/2715)).\n\n\n## [1.0.10] - 2020-07-21\n\n### Changed\n\n- The lock files are now versioned to ease transitions for lock file format changes, with warnings being displayed on incompatibility detection ([#2695](https://github.com/python-poetry/poetry/pull/2695)).\n- The `init` and `new` commands will now provide hints on invalid given licenses ([#1634](https://github.com/python-poetry/poetry/pull/1634)).\n\n### Fixed\n\n- Fixed error messages when the authors specified in the `pyproject.toml` file are invalid ([#2525](https://github.com/python-poetry/poetry/pull/2525)).\n- Fixed empty `.venv` directories being deleted ([#2064](https://github.com/python-poetry/poetry/pull/2064)).\n- Fixed the `shell` command for `tcsh` shells ([#2583](https://github.com/python-poetry/poetry/pull/2583)).\n- Fixed errors when installing directory or file dependencies in some cases ([#2582](https://github.com/python-poetry/poetry/pull/2582)).\n\n\n## [1.1.0a3] - 2020-07-10\n\n### Added\n\n- New installer which provides a faster and better experience ([#2595](https://github.com/python-poetry/poetry/pull/2595)).\n\n### Fixed\n\n- Fixed resolution error when handling duplicate dependencies with environment markers ([#2622](https://github.com/python-poetry/poetry/pull/2622)).\n- Fixed erroneous resolution errors when resolving packages to install ([#2625](https://github.com/python-poetry/poetry/pull/2625)).\n- Fixed errors when detecting installed editable packages ([#2602](https://github.com/python-poetry/poetry/pull/2602)).\n\n\n## [1.1.0a2] - 2020-06-26\n\nNote that lock files generated with this release are not compatible with previous releases of Poetry.\n\n### Added\n\n- The `install` command now supports a `--remove-untracked` option to ensure only packages from the lock file are present in the environment ([#2172](https://github.com/python-poetry/poetry/pull/2172)).\n- Some errors will now be provided with possible solutions and links to the documentation ([#2396](https://github.com/python-poetry/poetry/pull/2396)).\n\n### Changed\n\n- Editable installations of Poetry projects have been improved and are now faster ([#2360](https://github.com/python-poetry/poetry/pull/2360)).\n- Improved the accuracy of the dependency resolver in case of dependencies with environment markers ([#2361](https://github.com/python-poetry/poetry/pull/2361))\n- Environment markers of dependencies are no longer stored in the lock file ([#2361](https://github.com/python-poetry/poetry/pull/2361)).\n- Improved the way connection errors are handled when publishing ([#2285](https://github.com/python-poetry/poetry/pull/2285)).\n\n### Fixed\n\n- Fixed errors when handling duplicate dependencies with environment markers ([#2342](https://github.com/python-poetry/poetry/pull/2342)).\n- Fixed the detection of installed packages ([#2360](https://github.com/python-poetry/poetry/pull/2360)).\n\n\n## [1.1.0a1] - 2020-03-27\n\nThis release **must** be downloaded via the `get-poetry.py` script and not via the `self update` command.\n\n### Added\n\n- Added a new `--dry-run` option to the `publish` command ([#2199](https://github.com/python-poetry/poetry/pull/2199)).\n\n### Changed\n\n- The core features of Poetry have been extracted in to a separate library: `poetry-core` ([#2212](https://github.com/python-poetry/poetry/pull/2212)).\n- The build backend is no longer `poetry.masonry.api` but `poetry.core.masonry.api` which requires `poetry-core>=1.0.0a5` ([#2212](https://github.com/python-poetry/poetry/pull/2212)).\n- The exceptions are now beautifully displayed in the terminal with various level of details depending on the verbosity ([2230](https://github.com/python-poetry/poetry/pull/2230)).\n\n\n## [1.0.9] - 2020-06-09\n\n### Fixed\n\n- Fixed an issue where packages from custom indices where continuously updated ([#2525](https://github.com/python-poetry/poetry/pull/2525)).\n- Fixed errors in the way Python environment markers were parsed and generated ([#2526](https://github.com/python-poetry/poetry/pull/2526)).\n\n\n## [1.0.8] - 2020-06-05\n\n### Fixed\n\n- Fixed a possible error when installing the root package ([#2505](https://github.com/python-poetry/poetry/pull/2505)).\n- Fixed an error where directory and VCS dependencies were not installed ([#2505](https://github.com/python-poetry/poetry/pull/2505)).\n\n\n## [1.0.7] - 2020-06-05\n\n### Fixed\n\n- Fixed an error when trying to execute some packages `setup.py` file ([#2349](https://github.com/python-poetry/poetry/pull/2349)).\n\n\n## [1.0.6] - 2020-06-05\n\n### Changed\n\n- The `self update` command has been updated in order to handle future releases of Poetry ([#2429](https://github.com/python-poetry/poetry/pull/2429)).\n\n### Fixed\n\n- Fixed an error were a new line was not written when displaying the virtual environment's path with `env info` ([#2196](https://github.com/python-poetry/poetry/pull/2196)).\n- Fixed a misleading error message when the `packages` property was empty ([#2265](https://github.com/python-poetry/poetry/pull/2265)).\n- Fixed shell detection by using environment variables ([#2147](https://github.com/python-poetry/poetry/pull/2147)).\n- Fixed the removal of VCS dependencies ([#2239](https://github.com/python-poetry/poetry/pull/2239)).\n- Fixed generated wheel ABI tags for Python 3.8 ([#2121](https://github.com/python-poetry/poetry/pull/2121)).\n- Fixed a regression when building stub-only packages ([#2000](https://github.com/python-poetry/poetry/pull/2000)).\n- Fixed errors when parsing PEP-440 constraints with whitespace ([#2347](https://github.com/python-poetry/poetry/pull/2347)).\n- Fixed PEP 508 representation of VCS dependencies ([#2349](https://github.com/python-poetry/poetry/pull/2349)).\n- Fixed errors when source distributions were read-only ([#1140](https://github.com/python-poetry/poetry/pull/1140)).\n- Fixed dependency resolution errors and inconsistencies with directory, file and VCS dependencies ([#2398](https://github.com/python-poetry/poetry/pull/2398)).\n- Fixed custom repositories information not being properly locked ([#2484](https://github.com/python-poetry/poetry/pull/2484)).\n\n\n## [1.0.5] - 2020-02-29\n\n### Fixed\n\n- Fixed an error when building distributions if the `git` executable was not found ([#2105](https://github.com/python-poetry/poetry/pull/2105)).\n- Fixed various errors when reading Poetry's TOML files by upgrading [tomlkit](https://github.com/sdispater/tomlkit).\n\n\n## [1.0.4] - 2020-02-28\n\n### Fixed\n\n- Fixed the PyPI URL used when installing packages ([#2099](https://github.com/python-poetry/poetry/pull/2099)).\n- Fixed errors when the author's name contains special characters ([#2006](https://github.com/python-poetry/poetry/pull/2006)).\n- Fixed VCS excluded files detection when building wheels ([#1947](https://github.com/python-poetry/poetry/pull/1947)).\n- Fixed packages detection when building sdists ([#1626](https://github.com/python-poetry/poetry/pull/1626)).\n- Fixed the local `.venv` virtual environment not being displayed in `env list` ([#1762](https://github.com/python-poetry/poetry/pull/1762)).\n- Fixed incompatibilities with the most recent versions of `virtualenv` ([#2096](https://github.com/python-poetry/poetry/pull/2096)).\n- Fixed Poetry's own vendor dependencies being retrieved when updating dependencies ([#1981](https://github.com/python-poetry/poetry/pull/1981)).\n- Fixed encoding of credentials in URLs ([#1911](https://github.com/python-poetry/poetry/pull/1911)).\n- Fixed url constraints not being accepted in multi-constraints dependencies ([#2035](https://github.com/python-poetry/poetry/pull/2035)).\n- Fixed an error where credentials specified via environment variables were not retrieved ([#2061](https://github.com/python-poetry/poetry/pull/2061)).\n- Fixed an error where git dependencies referencing tags were not locked to the corresponding commit ([#1948](https://github.com/python-poetry/poetry/pull/1948)).\n- Fixed an error when parsing packages `setup.py` files ([#2041](https://github.com/python-poetry/poetry/pull/2041)).\n- Fixed an error when parsing some git URLs ([#2018](https://github.com/python-poetry/poetry/pull/2018)).\n\n\n## [1.0.3] - 2020-01-31\n\n### Fixed\n\n- Fixed an error which caused the configuration environment variables (like `POETRY_HTTP_BASIC_XXX_PASSWORD`) to not be used ([#1909](https://github.com/python-poetry/poetry/pull/1909)).\n- Fixed an error where the `--help` option was not working ([#1910](https://github.com/python-poetry/poetry/pull/1910)).\n- Fixed an error where packages from private indices were not decompressed properly ([#1851](https://github.com/python-poetry/poetry/pull/1851)).\n- Fixed an error where the version of some PEP-508-formatted wheel dependencies was not properly retrieved ([#1932](https://github.com/python-poetry/poetry/pull/1932)).\n- Fixed internal regexps to avoid potential catastrophic backtracking errors ([#1913](https://github.com/python-poetry/poetry/pull/1913)).\n- Fixed performance issues when custom indices were defined in the `pyproject.toml` file ([#1892](https://github.com/python-poetry/poetry/pull/1892)).\n- Fixed the `get_requires_for_build_wheel()` function of `masonry.api` which wasn't returning the proper result ([#1875](https://github.com/python-poetry/poetry/pull/1875)).\n\n\n## [1.0.2] - 2020-01-10\n\n### Fixed\n\n- Reverted a previous fix ([#1796](https://github.com/python-poetry/poetry/pull/1796)) which was causing errors for projects with file and/or directory dependencies ([#1865](https://github.com/python-poetry/poetry/pull/1865)).\n\n\n## [1.0.1] - 2020-01-10\n\n### Fixed\n\n- Fixed an error in `env use` where the wrong Python executable was being used to check compatibility ([#1736](https://github.com/python-poetry/poetry/pull/1736)).\n- Fixed an error where VCS dependencies were not properly categorized as development dependencies ([#1725](https://github.com/python-poetry/poetry/pull/1725)).\n- Fixed an error where some shells would no longer be usable after using the `shell` command ([#1673](https://github.com/python-poetry/poetry/pull/1673)).\n- Fixed an error where explicitly included files where not included in wheel distributions ([#1750](https://github.com/python-poetry/poetry/pull/1750)).\n- Fixed an error where some Git dependencies url were not properly parsed ([#1756](https://github.com/python-poetry/poetry/pull/1756)).\n- Fixed an error in the `env` commands on Windows if the path to the executable contained a space ([#1774](https://github.com/python-poetry/poetry/pull/1774)).\n- Fixed several errors and UX issues caused by `keyring` on some systems ([#1788](https://github.com/python-poetry/poetry/pull/1788)).\n- Fixed errors when trying to detect installed packages ([#1786](https://github.com/python-poetry/poetry/pull/1786)).\n- Fixed an error when packaging projects where Python packages were not properly detected ([#1592](https://github.com/python-poetry/poetry/pull/1592)).\n- Fixed an error where local file dependencies were exported as editable when using the `export` command ([#1840](https://github.com/python-poetry/poetry/pull/1840)).\n- Fixed the way environment markers are propagated and evaluated when resolving dependencies ([#1829](https://github.com/python-poetry/poetry/pull/1829), [#1789](https://github.com/python-poetry/poetry/pull/1789)).\n- Fixed an error in the PEP-508 compliant representation of directory and file dependencies ([#1796](https://github.com/python-poetry/poetry/pull/1796)).\n- Fixed an error where invalid virtual environments would be silently used. They will not be recreated and a warning will be displayed ([#1797](https://github.com/python-poetry/poetry/pull/1797)).\n- Fixed an error where dependencies were not properly detected when reading the `setup.py` file in some cases ([#1764](https://github.com/python-poetry/poetry/pull/1764)).\n\n\n## [1.0.0] - 2019-12-12\n\n### Added\n\n- Added an `export` command to export the lock file to other formats (only `requirements.txt` is currently supported).\n- Added a `env info` command to get basic information about the current environment.\n- Added a `env use` command to control the Python version used by the project.\n- Added a `env list` command to list the virtualenvs associated with the current project.\n- Added a `env remove` command to delete virtualenvs associated with the current project.\n- Added support for `POETRY_HOME` declaration within `get-poetry.py`.\n- Added support for declaring a specific source for dependencies.\n- Added support for disabling PyPI and making another repository the default one.\n- Added support for declaring private repositories as secondary.\n- Added the ability to specify packages on a per-format basis.\n- Added support for custom urls in metadata.\n- Full environment markers are now supported for dependencies via the `markers` property.\n- Added the ability to specify git dependencies directly in `add`, it no longer requires the `--git` option.\n- Added the ability to specify path dependencies directly in `add`, it no longer requires the `--path` option.\n- Added support for url dependencies ([#1260](https://github.com/python-poetry/poetry/pull/1260)).\n- Publishing to PyPI using [API tokens](https://pypi.org/help/#apitoken) is now supported ([#1275](https://github.com/python-poetry/poetry/pull/1275)).\n- Licenses can now be identified by their full name.\n- Added support for custom certificate authority and client certificates for private repositories.\n- Poetry can now detect and use Conda environments.\n\n### Changed\n\n- Slightly changed the lock file, making it potentially incompatible with previous Poetry versions.\n- The `cache:clear` command has been renamed to `cache clear`.\n- The `debug:info` command has been renamed to `debug info`.\n- The `debug:resolve` command has been renamed to `debug resolve`.\n- The `self:update` command has been renamed to `self update`.\n- Changed the way virtualenvs are stored (names now depend on the project's path).\n- The `--git` option of the `add` command has been removed.\n- The `--path` option of the `add` command has been removed.\n- The `add` command will now automatically select the latest prerelease if only prereleases are available.\n- The `add` command can now update a dependencies if an explicit constraint is given ([#1221](https://github.com/python-poetry/poetry/pull/1221)).\n- Removed the `--develop` option from the `install` command.\n- Improved UX when searching for packages in the `init` command.\n- The `shell` command has been improved.\n- The `poetry run` command now uses `os.execvp()` rather than spawning a new subprocess.\n- Specifying dependencies with `allows-prereleases` in the `pyproject.toml` file is deprecated for consistency with the `add` command. Use `allow-prereleases` instead.\n- Improved the error message when the lock file is invalid.\n- Whenever Poetry needs to use the \"system\" Python, it will now call `sys.executable` instead of the `python` command.\n- Improved the error message displayed on conflicting Python requirements ([#1681](https://github.com/python-poetry/poetry/pull/1681)).\n- Improved the `site-packages` directory detection ([#1683](https://github.com/python-poetry/poetry/pull/1683)).\n\n### Fixed\n\n- Fixed transitive extra dependencies being removed when updating a specific dependency.\n- The `pyproject.toml` configuration is now properly validated.\n- Fixed installing Poetry-based packages breaking with `pip`.\n- Fixed packages with empty markers being added to the lock file.\n- Fixed invalid lock file generation in some cases.\n- Fixed local version identifier handling in wheel file names.\n- Fixed packages with invalid metadata triggering an error instead of being skipped.\n- Fixed the generation of invalid lock files in some cases.\n- Git dependencies are now properly locked to a specific revision when specifying a branch or a tag.\n- Fixed the behavior of the `~=` operator.\n- Fixed dependency resolution for conditional development dependencies.\n- Fixed generated dependency constraints when they contain inequality operators.\n- The `run` command now properly handles the `--` separator.\n- Fixed some issues with `path` dependencies being seen as `git` dependencies.\n- Fixed various issues with the way `extra` markers in dependencies were handled.\n- Fixed the option conflicts in the `run` command.\n- Fixed wrong latest version being displayed when executing `show -l`.\n- Fixed `TooManyRedirects` errors being raised when resolving dependencies.\n- Fixed custom indices dependencies being constantly updated.\n- Fixed the behavior of the `--install` option of the debug resolve command.\n- Fixed an error in `show` when using the `-o/--outdated` option.\n- Fixed PEP 508 url dependency handling.\n- Fixed excluded files via the `exclude` being included in distributions.\n- Fixed an error in `env use` if the `virtualenvs.in-project` setting is activated ([#1682](https://github.com/python-poetry/poetry/pull/1682))\n- Fixed handling of `empty` and `any` markers in unions of markers ([#1650](https://github.com/python-poetry/poetry/pull/1650)).\n\n\n## [0.12.17] - 2019-07-03\n\n### Fixed\n\n- Fixed dependency resolution with circular dependencies.\n- Fixed encoding errors when reading files on Windows. (Thanks to [@vlcinsky](https://github.com/vlcinsky))\n- Fixed unclear errors when executing commands in virtual environments. (Thanks to [@Imaclean74](https://github.com/Imaclean74))\n- Fixed handling of `.venv` when it's not a directory. (Thanks to [@mpanarin](https://github.com/mpanarin))\n\n\n## [0.12.16] - 2019-05-17\n\n### Fixed\n\n- Fixed packages with no hashes retrieval for legacy repositories.\n- Fixed multiple constraints for dev dependencies.\n- Fixed dependency resolution failing on badly formed package versions instead of skipping.\n- Fixed permissions of built wheels.\n\n\n## [0.12.15] - 2019-05-03\n\n### Fixed\n\n- Fixed an `AttributeError` in the editable builder.\n- Fixed resolution of packages with only Python 3 wheels and sdist when resolving for legacy repositories.\n- Fixed non-sha256 hashes retrieval for legacy repositories.\n\n\n## [0.12.14] - 2019-04-26\n\n### Fixed\n\n- Fixed root package installation for pure Python packages.\n\n\n## [0.12.13] - 2019-04-26\n\n### Fixed\n\n- Fixed root package installation with `pip>=19.0`.\n- Fixed packages not being removed after using the `remove` command.\n\n\n## [0.12.12] - 2019-04-11\n\n### Fixed\n\n- Fix lock idempotency.\n- Fix markers evaluation for `python_version` with precision < 3.\n- Fix permissions of the `dist-info` files.\n- Fix `prepare_metadata_for_build_wheel()` missing in the build backend.\n- Fix metadata inconsistency between wheels and sdists.\n- Fix parsing of `platform_release` markers.\n- Fix metadata information when the project has git dependencies.\n- Fix error reporting when publishing fails.\n- Fix retrieval of `extras_require` in some `setup.py` files. (Thanks to [@asodeur](https://github.com/asodeur))\n- Fix wheel compression when building. (Thanks to [@ccosby](https://github.com/ccosby))\n- Improve retrieval of information for packages with two python specific wheels.\n- Fix request authentication when credentials are included in URLs. (Thanks to [@connorbrinton](https://github.com/connorbrinton))\n\n\n## [0.12.11] - 2019-01-13\n\n### Fixed\n\n- Fixed the way packages information are retrieved for legacy repositories.\n- Fixed an error when adding packages with invalid versions.\n- Fixed an error when resolving directory dependencies with no sub dependencies.\n- Fixed an error when locking packages with no description.\n- Fixed path resolution for transitive file dependencies.\n- Fixed multiple constraints handling for the root package.\n- Fixed exclude functionality on case sensitive systems.\n\n\n## [0.12.10] - 2018-11-22\n\n### Fixed\n\n- Fixed `run` not executing scripts.\n- Fixed environment detection.\n- Fixed handling of authentication for legacy repositories.\n\n\n## [0.12.9] - 2018-11-19\n\n### Fixed\n\n- Fixed executables from outside the virtualenv not being accessible.\n- Fixed a possible error when building distributions with the `exclude` option.\n- Fixed the `run` command for namespaced packages.\n- Fixed errors for virtualenvs with spaces in their path.\n- Fixed prerelease versions being selected with the `add` command.\n\n\n## [0.12.8] - 2018-11-13\n\n### Fixed\n\n- Fixed permission errors when adding/removing git dependencies on Windows.\n- Fixed `Pool` not raising an exception when no package could be found.\n- Fixed reading `bz2` source distribution.\n- Fixed handling of arbitrary equals in `InstalledRepository`.\n\n\n## [0.12.7] - 2018-11-08\n\n### Fixed\n\n- Fixed reading of some `setup.py` files.\n- Fixed a `KeyError` when getting information for packages which require reading setup files.\n- Fixed the building of wheels with C extensions and an `src` layout.\n- Fixed extras being selected when resolving dependencies even when not required.\n- Fixed performance issues when packaging projects if a lot of files were excluded.\n- Fixed installation of files.\n- Fixed extras not being retrieved for legacy repositories.\n- Fixed invalid transitive constraints raising an error for legacy repositories.\n\n\n## [0.12.6] - 2018-11-05\n\n### Changed\n\n- Poetry will now try to read, without executing, setup files (`setup.py` and/or `setup.cfg`) if the `egg_info` command fails when resolving dependencies.\n\n### Fixed\n\n- Fixed installation of directory dependencies.\n- Fixed handling of dependencies with a `not in` marker operator.\n- Fixed support for VCS dependencies.\n- Fixed the `exclude` property not being respected if no VCS was available.\n\n\n## [0.12.5] - 2018-10-26\n\n### Fixed\n\n- Fixed installation of Poetry git dependencies with a build system.\n- Fixed possible errors when resolving dependencies for specific packages.\n- Fixed handling of Python versions compatibility.\n- Fixed the dependency resolver picking up unnecessary dependencies due to not using the `python_full_version` marker.\n- Fixed the `Python-Requires` metadata being invalid for single Python versions.\n\n\n## [0.12.4] - 2018-10-21\n\n### Fixed\n\n- Fixed possible error on some combinations of markers.\n- Fixed venv detection so that it only uses `VIRTUAL_ENV` to detect activated virtualenvs.\n\n\n## [0.12.3] - 2018-10-18\n\n### Fixed\n\n- Fixed the `--no-dev` option in `install` not working properly.\n- Fixed prereleases being selected even if another constraint conflicted with them.\n- Fixed an error when installing current package in development mode if the generated `setup.py` had special characters.\n- Fixed an error in `install` for applications not following a known structure.\n- Fixed an error when trying to retrieve the current environment.\n- Fixed `debug:info` not showing the current project's virtualenv.\n\n\n## [0.12.2] - 2018-10-17\n\n### Fixed\n\n- Fixed an error when installing from private repositories.\n- Fixed an error when trying to move the lock file on Python 2.7.\n\n\n## [0.12.1] - 2018-10-17\n\n### Fixed\n\n- Fixed an error when license is unspecified.\n\n\n## [0.12.0] - 2018-10-17\n\n### Added\n\n- Added a brand new installer.\n- Added support for multi-constraints dependencies.\n- Added a cache version system.\n- Added a `--lock` option to `update` to only update the lock file without executing operations. (Thanks to [@greysteil](https://github.com/greysteil))\n- Added support for the `Project-URL` metadata.\n- Added support for optional scripts.\n- Added a `--no-dev` option to `show`. (Thanks to [@rodcloutier](https://github.com/rodcloutier))\n\n### Changed\n\n- Improved virtualenv detection and management.\n- Wildcard `python` dependencies are now equivalent to `~2.7 || ^3.4`.\n- Changed behavior of the resolver for conditional dependencies.\n- The `install` command will now install the current project in editable mode.\n- The `develop` command is now deprecated in favor of `install`.\n- Improved the `check` command.\n- Empty passwords are now supported when publishing.\n\n### Fixed\n\n- Fixed a memory leak in the resolver.\n- Fixed a recursion error on duplicate dependencies with only different extras.\n- Fixed handling of extras.\n- Fixed duplicate entries in both sdist and wheel.\n- Fixed excluded files appearing in the `package_data` of the generated `setup.py`.\n- Fixed transitive directory dependencies installation.\n- Fixed file permissions for configuration and authentication files.\n- Fixed an error in `cache:clear` for Python 2.7.\n- Fixed publishing for the first time with a prerelease.\n\n\n## [0.11.5] - 2018-09-04\n\n### Fixed\n\n- Fixed a recursion error with circular dependencies.\n- Fixed the `config` command setting incorrect values for paths.\n- Fixed an `OSError` on Python >= 3.5 for `git` dependencies with recursive symlinks.\n- Fixed the possible deletion of system paths by `cache:clear`.\n- Fixed a performance issue when parsing the lock file by upgrading `tomlkit`.\n\n\n## [0.11.4] - 2018-07-30\n\n### Fixed\n\n- Fixed wrong wheel being selected when resolving dependencies.\n- Fixed an error when publishing.\n- Fixed an error when building wheels with the `packages` property set.\n- Fixed single value display in `config` command.\n\n\n## [0.11.3] - 2018-07-26\n\n### Changed\n\n- Poetry now only uses [TOML Kit](https://github.com/sdispater/tomlkit) for TOML files manipulation.\n- Improved dependency resolution debug information.\n\n### Fixed\n\n- Fixed missing dependency information for some packages.\n- Fixed handling of single versions when packaging.\n- Fixed dependency information retrieval from `.zip` and `.bz2` archives.\n- Fixed searching for and installing packages from private repositories with authentication. (Thanks to [@MarcDufresne](https://github.com/MarcDufresne))\n- Fixed a potential error when checking the `pyproject.toml` validity. (Thanks to [@ojii](https://github.com/ojii))\n- Fixed the lock file not tracking the `extras` information from `pyproject.toml`. (Thanks to [@cauebs](https://github.com/cauebs))\n- Fixed missing trailing slash in the Simple API urls for private repositories. (Thanks to [@bradsbrown](https://github.com/bradsbrown))\n\n\n## [0.11.2] - 2018-07-03\n\n### Fixed\n\n- Fixed missing dependencies when resolving in some cases.\n- Fixed path dependencies not working in `dev-dependencies`.\n- Fixed license validation in `init`. (Thanks to [@cauebs](https://github.com/cauebs))\n\n\n## [0.11.1] - 2018-06-29\n\n### Fixed\n\n- Fixed an error when locking dependencies on Python 2.7.\n\n\n## [0.11.0] - 2018-06-28\n\n### Added\n\n- Added support for `packages`, `include` and `exclude` properties.\n- Added a new `shell` command. (Thanks to [@cauebs](https://github.com/cauebs))\n- Added license validation in `init` command.\n\n### Changed\n\n- Changed the dependency installation order, deepest dependencies are now installed first.\n- Improved solver error messages.\n- `poetry` now always reads/writes the `pyproject.toml` file with the `utf-8` encoding.\n- `config --list` now lists all available settings.\n- `init` no longer adds `pytest` to development dependencies.\n\n### Fixed\n\n- Fixed handling of duplicate dependencies with different constraints.\n- Fixed system requirements in lock file for sub dependencies.\n- Fixed detection of new prereleases.\n- Fixed unsafe packages being locked.\n- Fixed versions detection in custom repositories.\n- Fixed package finding with multiple custom repositories.\n- Fixed handling of root incompatibilities.\n- Fixed an error where packages from custom repositories would not be found.\n- Fixed wildcard Python requirement being wrongly set in distributions metadata.\n- Fixed installation of packages from a custom repository.\n- Fixed `remove` command's case sensitivity. (Thanks to [@cauebs](https://github.com/cauebs))\n- Fixed detection of `.egg-info` directory for non-poetry projects. (Thanks to [@gtors](https://github.com/gtors))\n- Fixed only-wheel builds. (Thanks to [@gtors](https://github.com/gtors))\n- Fixed key and array order in lock file to avoid having differences when relocking.\n- Fixed errors when `git` could not be found.\n\n\n## [0.10.3] - 2018-06-04\n\n### Fixed\n\n- Fixed `self:update` command on Windows.\n- Fixed `self:update` not picking up new versions.\n- Fixed a `RuntimeError` on Python 3.7.\n- Fixed bad version number being picked with private repositories.\n- Fixed handling of duplicate dependencies with same constraint.\n- Fixed installation from custom repositories.\n- Fixed setting an explicit version in `version` command.\n- Fixed parsing of wildcards version constraints.\n\n\n## [0.10.2] - 2018-05-31\n\n### Fixed\n\n- Fixed handling of `in` environment markers with commas.\n- Fixed a `UnicodeDecodeError` when an error occurs in venv.\n- Fixed Python requirements not properly set when resolving dependencies.\n- Fixed terminal coloring being activated even if not supported.\n- Fixed wrong executable being picked up on Windows in `poetry run`.\n- Fixed error when listing distribution links for private repositories.\n- Fixed handling of PEP 440 `~=` version constraint.\n\n\n## [0.10.1] - 2018-05-28\n\n### Fixed\n\n- Fixed packages not found for prerelease version constraints when resolving dependencies.\n- Fixed `init` and `add` commands.\n\n\n## [0.10.0] - 2018-05-28\n\n### Added\n\n- Added a new, more efficient dependency resolver.\n- Added a new `init` command to generate a `pyproject.toml` file in existing projects.\n- Added a new setting `settings.virtualenvs.in-project` to make `poetry` create the project's virtualenv inside the project's directory.\n- Added the `--extras` and `--python` options to `debug:resolve` to help debug dependency resolution.\n- Added a `--src` option to `new` command to create an `src` layout.\n- Added support for specifying the `platform` for dependencies.\n- Added the `--python` option to the `add` command.\n- Added the `--platform` option to the `add` command.\n- Added a `--develop` option to the install command to install path dependencies in development/editable mode.\n- Added a `develop` command to install the current project in development mode.\n\n### Changed\n\n- Improved the `show` command to make it easier to check if packages are properly installed.\n- The `script` command has been deprecated, use `run` instead.\n- The `publish` command no longer build packages by default. Use `--build` to retrieve the previous behavior.\n- Improved support for private repositories.\n- Expanded version constraints now keep the original version's precision.\n- The lock file hash no longer uses the project's name and version.\n- The `LICENSE` file, or similar, is now automatically added to the built packages.\n\n### Fixed\n\n- Fixed the dependency resolver selecting incompatible packages.\n- Fixed override of dependency with dependency with extras in `dev-dependencies`.\n\n\n## [0.9.1] - 2018-05-18\n\n### Fixed\n\n- Fixed handling of package names with dots. (Thanks to [bertjwregeer](https://github.com/bertjwregeer))\n- Fixed path dependencies being resolved from the current path instead of the `pyproject.toml` file. (Thanks to [radix](https://github.com/radix))\n\n\n## [0.9.0] - 2018-05-07\n\n### Added\n\n- Added the `cache:clear` command.\n- Added support for `git` dependencies in the `add` command.\n- Added support for `path` dependencies in the `add` command.\n- Added support for extras in the `add` command.\n- Added support for directory dependencies.\n- Added support for `src/` layout for packages.\n- Added automatic detection of `.venv` virtualenvs.\n\n### Changed\n\n- Drastically improved dependency resolution speed.\n- Dependency resolution caches now use sha256 hashes.\n- Changed CLI error style.\n- Improved debugging of dependency resolution.\n- Poetry now attempts to find `pyproject.toml` not only in the directory it was\n  invoked in, but in all its parents up to the root. This allows to run Poetry\n  commands in project subdirectories.\n- Made the email address for authors optional.\n\n### Fixed\n\n- Fixed handling of extras when resolving dependencies.\n- Fixed `self:update` command for some installation.\n- Fixed handling of extras when building projects.\n- Fixed handling of wildcard dependencies wen packaging/publishing.\n- Fixed an error when adding a new packages with prereleases in lock file.\n- Fixed packages name normalization.\n\n\n## [0.8.6] - 2018-04-30\n\n### Fixed\n\n- Fixed config files not being created.\n\n\n## [0.8.5] - 2018-04-19\n\n### Fixed\n\n- Fixed a bug in dependency resolution which led to installation errors.\n- Fixed a bug where malformed sdists would lead to dependency resolution failing.\n\n\n## [0.8.4] - 2018-04-18\n\n### Fixed\n\n- Fixed a bug where dependencies constraints in lock were too strict.\n- Fixed unicode error in `search` command for Python 2.7.\n- Fixed error with git dependencies.\n\n\n## [0.8.3] - 2018-04-16\n\n### Fixed\n\n- Fixed platform verification which led to missing packages.\n- Fixed duplicates in `pyproject.lock`.\n\n\n## [0.8.2] - 2018-04-14\n\n### Fixed\n\n- Fixed `add` command picking up prereleases by default.\n- Fixed dependency resolution on Windows when unpacking distributions.\n- Fixed dependency resolution with post releases.\n- Fixed dependencies being installed even if not necessary for current system.\n\n\n## [0.8.1] - 2018-04-13\n\n### Fixed\n\n- Fixed resolution with bad (empty) releases.\n- Fixed `version` for prereleases.\n- Fixed `search` not working outside of a project.\n- Fixed `self:update` not working outside of a project.\n\n\n## [0.8.0] - 2018-04-13\n\n### Added\n\n- Added support for Python 2.7.\n- Added a fallback mechanism for missing dependencies.\n- Added the `search` command.\n- Added support for local files as dependencies.\n- Added the `self:update` command.\n\n### Changes\n\n- Improved dependency resolution time by using cache control.\n\n### Fixed\n\n- Fixed `install_requires` and `extras` in generated sdist.\n- Fixed dependency resolution crash with malformed dependencies.\n- Fixed errors when `license` metadata is not set.\n- Fixed missing information in lock file.\n\n\n## [0.7.1] - 2018-04-05\n\n### Fixed\n\n- Fixed dependency resolution for custom repositories.\n\n\n## [0.7.0] - 2018-04-04\n\n### Added\n\n- Added compatibility with Python 3.4 and 3.5.\n- Added the `version` command to automatically bump the package's version.\n- Added a standalone installer to install `poetry` isolated.\n- Added support for classifiers in `pyproject.toml`.\n- Added the `script` command.\n\n### Changed\n\n- Improved dependency resolution to avoid unnecessary operations.\n- Improved dependency resolution speed.\n- Improved CLI reactivity by deferring imports.\n- License classifier is not automatically added to classifiers.\n\n### Fixed\n\n- Fixed handling of markers with the `in` operator.\n- Fixed `update` not properly adding new packages to the lock file.\n- Fixed solver adding uninstall operations for non-installed packages.\n- Fixed `new` command creating invalid `pyproject.toml` files.\n\n\n## [0.6.5] - 2018-03-22\n\n### Fixed\n\n- Fixed handling of extras in wheels metadata.\n\n\n## [0.6.4] - 2018-03-21\n\n### Added\n\n- Added a `debug:info` command to get information about current environment.\n\n### Fixed\n\n- Fixed Python version retrieval inside virtualenvs.\n- Fixed optional dependencies being set as required in sdist.\n- Fixed `--optional` option in the `add` command not being used.\n\n\n## [0.6.3] - 2018-03-20\n\n### Fixed\n\n- Fixed built wheels not getting information from the virtualenv.\n- Fixed building wheel with conditional extensions.\n- Fixed missing files in built wheel with extensions.\n- Fixed call to venv binaries on windows.\n- Fixed subdependencies representation in lock file.\n\n\n## [0.6.2] - 2018-03-19\n\n### Changed\n\n- Changed how wildcard constraints are handled.\n\n### Fixed\n\n- Fixed errors with pip 9.0.2.\n\n\n## [0.6.1] - 2018-02-18\n\n### Fixed\n\n- Fixed wheel entry points being written on a single line.\n- Fixed wheel metadata (Tag and Root-Is-Purelib).\n\n\n## [0.6.0] - 2018-03-16\n\n### Added\n\n- Added support for virtualenv autogeneration (Python 3.6+ only).\n- Added the `run` command to execute commands inside the created virtualenvs.\n- Added the `debug:resolve` command to debug dependency resolution.\n- Added `pyproject.toml` file validation.\n- Added support for Markdown readme files.\n\n### Fixed\n\n- Fixed color displayed in `show` command for semver-compatible updates.\n- Fixed Python requirements in publishing metadata.\n- Fixed `update` command reinstalling every dependency.\n\n\n## [0.5.0] - 2018-03-14\n\n### Added\n\n- Added experimental support for package with C extensions.\n\n### Changed\n\n- Added hashes check when installing packages.\n\n### Fixed\n\n- Fixed handling of post releases.\n- Fixed python restricted dependencies not being checked against virtualenv version.\n- Fixed python/platform constraint not being picked up for subdependencies.\n- Fixed skipped packages appearing as installing.\n- Fixed platform specification not being used when resolving dependencies.\n\n\n## [0.4.2] - 2018-03-10\n\n### Fixed\n\n- Fixed TypeError when `requires_dist` is null on PyPI.\n\n\n## [0.4.1] - 2018-03-08\n\n### Fixed\n\n- Fixed missing entry point\n\n\n## [0.4.0] - 2018-03-08\n\n### Added\n\n- Added packaging support (sdist and pure-python wheel).\n- Added the `build` command.\n- Added support for extras definition.\n- Added support for dependencies extras specification.\n- Added the `config` command.\n- Added the `publish` command.\n\n### Changed\n\n- Dependencies system constraints are now respected when installing packages.\n- Complied with PEP 440\n\n### Fixed\n\n- Fixed `show` command for VCS dependencies.\n- Fixed handling of releases with bad markers in PyPiRepository.\n\n\n## [0.3.0] - 2018-03-05\n\n### Added\n\n- Added `show` command.\n- Added the `--dry-run` option to the `add` command.\n\n### Changed\n\n- Changed the `poetry.toml` file for the new, standardized `pyproject.toml`.\n- Dependencies of each package is now stored in the lock file.\n- Improved TOML file management.\n- Dependency resolver now respects the root package python version requirements.\n\n### Fixed\n\n- Fixed the `add` command for packages with dots in their names.\n\n\n## [0.2.0] - 2018-03-01\n\n### Added\n\n- Added `remove` command.\n- Added basic support for VCS (git) dependencies.\n- Added support for private repositories.\n\n### Changed\n\n- Changed `poetry.lock` format.\n\n### Fixed\n\n- Fixed dependencies solving that would lead to dependencies not being written to lock.\n\n\n## [0.1.0] - 2018-02-28\n\nInitial release\n\n\n\n[Unreleased]: https://github.com/python-poetry/poetry/compare/2.3.2...main\n[2.3.2]: https://github.com/python-poetry/poetry/releases/tag/2.3.2\n[2.3.1]: https://github.com/python-poetry/poetry/releases/tag/2.3.1\n[2.3.0]: https://github.com/python-poetry/poetry/releases/tag/2.3.0\n[2.2.1]: https://github.com/python-poetry/poetry/releases/tag/2.2.1\n[2.2.0]: https://github.com/python-poetry/poetry/releases/tag/2.2.0\n[2.1.4]: https://github.com/python-poetry/poetry/releases/tag/2.1.4\n[2.1.3]: https://github.com/python-poetry/poetry/releases/tag/2.1.3\n[2.1.2]: https://github.com/python-poetry/poetry/releases/tag/2.1.2\n[2.1.1]: https://github.com/python-poetry/poetry/releases/tag/2.1.1\n[2.1.0]: https://github.com/python-poetry/poetry/releases/tag/2.1.0\n[2.0.1]: https://github.com/python-poetry/poetry/releases/tag/2.0.1\n[2.0.0]: https://github.com/python-poetry/poetry/releases/tag/2.0.0\n[1.8.5]: https://github.com/python-poetry/poetry/releases/tag/1.8.5\n[1.8.4]: https://github.com/python-poetry/poetry/releases/tag/1.8.4\n[1.8.3]: https://github.com/python-poetry/poetry/releases/tag/1.8.3\n[1.8.2]: https://github.com/python-poetry/poetry/releases/tag/1.8.2\n[1.8.1]: https://github.com/python-poetry/poetry/releases/tag/1.8.1\n[1.8.0]: https://github.com/python-poetry/poetry/releases/tag/1.8.0\n[1.7.1]: https://github.com/python-poetry/poetry/releases/tag/1.7.1\n[1.7.0]: https://github.com/python-poetry/poetry/releases/tag/1.7.0\n[1.6.1]: https://github.com/python-poetry/poetry/releases/tag/1.6.1\n[1.6.0]: https://github.com/python-poetry/poetry/releases/tag/1.6.0\n[1.5.1]: https://github.com/python-poetry/poetry/releases/tag/1.5.1\n[1.5.0]: https://github.com/python-poetry/poetry/releases/tag/1.5.0\n[1.4.2]: https://github.com/python-poetry/poetry/releases/tag/1.4.2\n[1.4.1]: https://github.com/python-poetry/poetry/releases/tag/1.4.1\n[1.4.0]: https://github.com/python-poetry/poetry/releases/tag/1.4.0\n[1.3.2]: https://github.com/python-poetry/poetry/releases/tag/1.3.2\n[1.3.1]: https://github.com/python-poetry/poetry/releases/tag/1.3.1\n[1.3.0]: https://github.com/python-poetry/poetry/releases/tag/1.3.0\n[1.2.2]: https://github.com/python-poetry/poetry/releases/tag/1.2.2\n[1.2.1]: https://github.com/python-poetry/poetry/releases/tag/1.2.1\n[1.2.0]: https://github.com/python-poetry/poetry/releases/tag/1.2.0\n[1.2.0rc2]: https://github.com/python-poetry/poetry/releases/tag/1.2.0rc2\n[1.2.0rc1]: https://github.com/python-poetry/poetry/releases/tag/1.2.0rc1\n[1.2.0b3]: https://github.com/python-poetry/poetry/releases/tag/1.2.0b3\n[1.2.0b2]: https://github.com/python-poetry/poetry/releases/tag/1.2.0b2\n[1.2.0b1]: https://github.com/python-poetry/poetry/releases/tag/1.2.0b1\n[1.2.0a2]: https://github.com/python-poetry/poetry/releases/tag/1.2.0a2\n[1.2.0a1]: https://github.com/python-poetry/poetry/releases/tag/1.2.0a1\n[1.1.15]: https://github.com/python-poetry/poetry/releases/tag/1.1.15\n[1.1.14]: https://github.com/python-poetry/poetry/releases/tag/1.1.14\n[1.1.13]: https://github.com/python-poetry/poetry/releases/tag/1.1.13\n[1.1.12]: https://github.com/python-poetry/poetry/releases/tag/1.1.12\n[1.1.11]: https://github.com/python-poetry/poetry/releases/tag/1.1.11\n[1.1.10]: https://github.com/python-poetry/poetry/releases/tag/1.1.10\n[1.1.9]: https://github.com/python-poetry/poetry/releases/tag/1.1.9\n[1.1.8]: https://github.com/python-poetry/poetry/releases/tag/1.1.8\n[1.1.7]: https://github.com/python-poetry/poetry/releases/tag/1.1.7\n[1.1.6]: https://github.com/python-poetry/poetry/releases/tag/1.1.6\n[1.1.5]: https://github.com/python-poetry/poetry/releases/tag/1.1.5\n[1.1.4]: https://github.com/python-poetry/poetry/releases/tag/1.1.4\n[1.1.3]: https://github.com/python-poetry/poetry/releases/tag/1.1.3\n[1.1.2]: https://github.com/python-poetry/poetry/releases/tag/1.1.2\n[1.1.1]: https://github.com/python-poetry/poetry/releases/tag/1.1.1\n[1.1.0]: https://github.com/python-poetry/poetry/releases/tag/1.1.0\n[1.1.0rc1]: https://github.com/python-poetry/poetry/releases/tag/1.1.0rc1\n[1.1.0b4]: https://github.com/python-poetry/poetry/releases/tag/1.1.0b4\n[1.1.0b3]: https://github.com/python-poetry/poetry/releases/tag/1.1.0b3\n[1.1.0b2]: https://github.com/python-poetry/poetry/releases/tag/1.1.0b2\n[1.1.0b1]: https://github.com/python-poetry/poetry/releases/tag/1.1.0b1\n[1.1.0a3]: https://github.com/python-poetry/poetry/releases/tag/1.1.0a3\n[1.1.0a2]: https://github.com/python-poetry/poetry/releases/tag/1.1.0a2\n[1.1.0a1]: https://github.com/python-poetry/poetry/releases/tag/1.1.0a1\n[1.0.10]: https://github.com/python-poetry/poetry/releases/tag/1.0.10\n[1.0.9]: https://github.com/python-poetry/poetry/releases/tag/1.0.9\n[1.0.8]: https://github.com/python-poetry/poetry/releases/tag/1.0.8\n[1.0.7]: https://github.com/python-poetry/poetry/releases/tag/1.0.7\n[1.0.6]: https://github.com/python-poetry/poetry/releases/tag/1.0.6\n[1.0.5]: https://github.com/python-poetry/poetry/releases/tag/1.0.5\n[1.0.4]: https://github.com/python-poetry/poetry/releases/tag/1.0.4\n[1.0.3]: https://github.com/python-poetry/poetry/releases/tag/1.0.3\n[1.0.2]: https://github.com/python-poetry/poetry/releases/tag/1.0.2\n[1.0.1]: https://github.com/python-poetry/poetry/releases/tag/1.0.1\n[1.0.0]: https://github.com/python-poetry/poetry/releases/tag/1.0.0\n[0.12.17]: https://github.com/python-poetry/poetry/releases/tag/0.12.17\n[0.12.16]: https://github.com/python-poetry/poetry/releases/tag/0.12.16\n[0.12.15]: https://github.com/python-poetry/poetry/releases/tag/0.12.15\n[0.12.14]: https://github.com/python-poetry/poetry/releases/tag/0.12.14\n[0.12.13]: https://github.com/python-poetry/poetry/releases/tag/0.12.13\n[0.12.12]: https://github.com/python-poetry/poetry/releases/tag/0.12.12\n[0.12.11]: https://github.com/python-poetry/poetry/releases/tag/0.12.11\n[0.12.10]: https://github.com/python-poetry/poetry/releases/tag/0.12.10\n[0.12.9]: https://github.com/python-poetry/poetry/releases/tag/0.12.9\n[0.12.8]: https://github.com/python-poetry/poetry/releases/tag/0.12.8\n[0.12.7]: https://github.com/python-poetry/poetry/releases/tag/0.12.7\n[0.12.6]: https://github.com/python-poetry/poetry/releases/tag/0.12.6\n[0.12.5]: https://github.com/python-poetry/poetry/releases/tag/0.12.5\n[0.12.4]: https://github.com/python-poetry/poetry/releases/tag/0.12.4\n[0.12.3]: https://github.com/python-poetry/poetry/releases/tag/0.12.3\n[0.12.2]: https://github.com/python-poetry/poetry/releases/tag/0.12.2\n[0.12.1]: https://github.com/python-poetry/poetry/releases/tag/0.12.1\n[0.12.0]: https://github.com/python-poetry/poetry/releases/tag/0.12.0\n[0.11.5]: https://github.com/python-poetry/poetry/releases/tag/0.11.5\n[0.11.4]: https://github.com/python-poetry/poetry/releases/tag/0.11.4\n[0.11.3]: https://github.com/python-poetry/poetry/releases/tag/0.11.3\n[0.11.2]: https://github.com/python-poetry/poetry/releases/tag/0.11.2\n[0.11.1]: https://github.com/python-poetry/poetry/releases/tag/0.11.1\n[0.11.0]: https://github.com/python-poetry/poetry/releases/tag/0.11.0\n[0.10.3]: https://github.com/python-poetry/poetry/releases/tag/0.10.3\n[0.10.2]: https://github.com/python-poetry/poetry/releases/tag/0.10.2\n[0.10.1]: https://github.com/python-poetry/poetry/releases/tag/0.10.1\n[0.10.0]: https://github.com/python-poetry/poetry/releases/tag/0.10.0\n[0.9.1]: https://github.com/python-poetry/poetry/releases/tag/0.9.1\n[0.9.0]: https://github.com/python-poetry/poetry/releases/tag/0.9.0\n[0.8.6]: https://github.com/python-poetry/poetry/releases/tag/0.8.6\n[0.8.5]: https://github.com/python-poetry/poetry/releases/tag/0.8.5\n[0.8.4]: https://github.com/python-poetry/poetry/releases/tag/0.8.4\n[0.8.3]: https://github.com/python-poetry/poetry/releases/tag/0.8.3\n[0.8.2]: https://github.com/python-poetry/poetry/releases/tag/0.8.2\n[0.8.1]: https://github.com/python-poetry/poetry/releases/tag/0.8.1\n[0.8.0]: https://github.com/python-poetry/poetry/releases/tag/0.8.0\n[0.7.1]: https://github.com/python-poetry/poetry/releases/tag/0.7.1\n[0.7.0]: https://github.com/python-poetry/poetry/releases/tag/0.7.0\n[0.6.5]: https://github.com/python-poetry/poetry/releases/tag/0.6.5\n[0.6.4]: https://github.com/python-poetry/poetry/releases/tag/0.6.4\n[0.6.3]: https://github.com/python-poetry/poetry/releases/tag/0.6.3\n[0.6.2]: https://github.com/python-poetry/poetry/releases/tag/0.6.2\n[0.6.1]: https://github.com/python-poetry/poetry/releases/tag/0.6.1\n[0.6.0]: https://github.com/python-poetry/poetry/releases/tag/0.6.0\n[0.5.0]: https://github.com/python-poetry/poetry/releases/tag/0.5.0\n[0.4.2]: https://github.com/python-poetry/poetry/releases/tag/0.4.2\n[0.4.1]: https://github.com/python-poetry/poetry/releases/tag/0.4.1\n[0.4.0]: https://github.com/python-poetry/poetry/releases/tag/0.4.0\n[0.3.0]: https://github.com/python-poetry/poetry/releases/tag/0.3.0\n[0.2.0]: https://github.com/python-poetry/poetry/releases/tag/0.2.0\n[0.1.0]: https://github.com/python-poetry/poetry/releases/tag/0.1.0\n"
  },
  {
    "path": "CITATION.cff",
    "content": "cff-version: 1.2.0\ntitle: \"Poetry: Python packaging and dependency management made easy\"\nmessage: >-\n  If you use this software, please cite it using the\n  metadata from this file.\nauthors:\n  - family-names: Eustace\n    given-names: Sébastien\n  - name: \"The Poetry contributors\"\nabstract:  >-\n  Poetry helps you declare, manage and install dependencies of Python projects, ensuring you have the right stack everywhere.\n  Poetry replaces setup.py, requirements.txt, setup.cfg, MANIFEST.in and Pipfile with a simple pyproject.toml based project format.\nlicense: MIT\nlicense-url: \"https://github.com/python-poetry/poetry/blob/main/LICENSE\"\nrepository-code: \"https://github.com/python-poetry/poetry\"\nkeywords:\n  - python\n  - packaging\n  - dependency management\ntype: software\nurl: \"https://python-poetry.org\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at sebastien@eustace.io. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2018-present Sébastien Eustace\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Poetry: Python packaging and dependency management made easy\n\n[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)\n[![Stable Version](https://img.shields.io/pypi/v/poetry?label=stable)][PyPI Releases]\n[![Pre-release Version](https://img.shields.io/github/v/release/python-poetry/poetry?label=pre-release&include_prereleases&sort=semver)][PyPI Releases]\n[![Python Versions](https://img.shields.io/pypi/pyversions/poetry)][PyPI]\n[![Download Stats](https://img.shields.io/pypi/dm/poetry)](https://pypistats.org/packages/poetry)\n[![Discord](https://img.shields.io/discord/487711540787675139?logo=discord)][Discord]\n\nPoetry helps you declare, manage and install dependencies of Python projects,\nensuring you have the right stack everywhere.\n\n![Poetry Install](https://raw.githubusercontent.com/python-poetry/poetry/main/assets/install.gif)\n\nPoetry replaces `setup.py`, `requirements.txt`, `setup.cfg`, `MANIFEST.in` and `Pipfile` with a simple `pyproject.toml`\nbased project format.\n\n```toml\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[project]\nname = \"my-package\"\nversion = \"0.1.0\"\ndescription = \"The description of the package\"\nreadme = \"README.md\"\n\nlicense = \"MIT\"\nlicense-files = [\"LICENSE\"]\n\n# No Python upper bound for package metadata\nrequires-python = \">=3.9\"\n\nauthors = [\n    { name = \"Sébastien Eustace\", email = \"sebastien@eustace.io\" },\n]\n\n# Keywords (translated to tags on the package index)\nkeywords = [\"packaging\", \"poetry\"]\n\ndependencies = [\n    # equivalent to ^3.8.1 with semver constraints\n    \"aiohttp (>=3.8.1,<4.0.0)\",\n    # dependency with extras\n    \"requests[security] (>=2.28,<3.0)\",\n    # version-specific dependency with prereleases allowed (see below)\n    \"tomli (>=2.0.1,<3.0.0) ; python_version < '3.11'\",\n    # git dependency with branch specified\n    \"cleo @ git+https://github.com/python-poetry/cleo.git@main\",\n]\n\n[project.urls]\nrepository = \"https://github.com/python-poetry/poetry\"\nhomepage = \"https://python-poetry.org\"\n\n# Scripts are easily expressed\n[project.scripts]\nmy_package_cli = \"my_package.console:run\"\n\n[project.optional-dependencies]\n# optional dependency to be installed via 'poetry install -E my-extra'\nmy-extra = [\"pendulum (>=3.1.0,<4.0.0)\"]\n\n[tool.poetry.dependencies]\n# Python upper bound for locking\npython = \">=3.9,<4.0\"\n# Version-specific dependencies with prereleases allowed\ntomli = { allow-prereleases = true }\n\n# Dependency groups are supported for organizing your dependencies\n[dependency-groups]\ndev = [\"pytest (>=7.1.2,<8.0.0)\", \"pytest-cov (>=3.0,<4.0)\"]\ndocs = [\"Sphinx (>=5.1.1,<6.0.0)\"]\n\n# ...and can be installed only when explicitly requested\n# via 'poetry install --with docs'\n[tool.poetry.group.docs]\noptional = true\n\n# Alternatively, you can use Poetry specific syntax\n# to specify dependency groups\n[tool.poetry.group.lint]\noptional = true\n\n[tool.poetry.group.lint.dependencies]\nruff = \">=0.10.0\"\n```\n\n## Installation\n\nPoetry supports multiple installation methods, including a simple script found at [install.python-poetry.org]. For full\ninstallation instructions, including advanced usage of the script, alternate install methods, and CI best practices, see\nthe full [installation documentation].\n\n## Documentation\n\n[Documentation] for the current version of Poetry (as well as the development branch and recently out of support\nversions) is available from the [official website].\n\n## Contribute\n\nPoetry is a large, complex project always in need of contributors. For those new to the project, a list of\n[suggested issues] to work on in Poetry and poetry-core is available. The full [contributing documentation] also\nprovides helpful guidance.\n\n## Resources\n\n* [Releases][PyPI Releases]\n* [Official Website]\n* [Documentation]\n* [Issue Tracker]\n* [Discord]\n\n  [PyPI]: https://pypi.org/project/poetry/\n  [PyPI Releases]: https://pypi.org/project/poetry/#history\n  [Official Website]: https://python-poetry.org\n  [Documentation]: https://python-poetry.org/docs/\n  [Issue Tracker]: https://github.com/python-poetry/poetry/issues\n  [Suggested Issues]: https://github.com/python-poetry/poetry/contribute\n  [Contributing Documentation]: https://python-poetry.org/docs/contributing\n  [Discord]: https://discord.com/invite/awxPgve\n  [install.python-poetry.org]: https://install.python-poetry.org\n  [Installation Documentation]: https://python-poetry.org/docs/#installation\n\n## Related Projects\n\n* [poetry-core](https://github.com/python-poetry/poetry-core): PEP 517 build-system for Poetry projects, and\ndependency-free core functionality of the Poetry frontend\n* [poetry-plugin-export](https://github.com/python-poetry/poetry-plugin-export): Export Poetry projects/lock files to\nforeign formats like requirements.txt\n* [poetry-plugin-bundle](https://github.com/python-poetry/poetry-plugin-bundle): Install Poetry projects/lock files to\nexternal formats like virtual environments\n* [install.python-poetry.org](https://github.com/python-poetry/install.python-poetry.org): The official Poetry\ninstallation script\n* [website](https://github.com/python-poetry/website): The official Poetry website and blog\n\n## Supporters\n\nThanks to [JetBrains](https://www.jetbrains.com) for supporting us with licenses for their tools.\n\n[<img src=\"https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg\" width=\"150\" alt=\"JetBrains logo.\" />](https://www.jetbrains.com)\n"
  },
  {
    "path": "docs/_index.md",
    "content": "---\ntitle: \"Introduction\"\ndraft: false\ntype: docs\nlayout: \"single\"\n\nmenu:\n  docs:\n    weight: 0\n---\n\n# Introduction\n\nPoetry is a tool for **dependency management** and **packaging** in Python.\nIt allows you to declare the libraries your project depends on and it will manage (install/update) them for you.\nPoetry offers a lockfile to ensure repeatable installs, and can build your project for distribution.\n\n\n## System requirements\n\nPoetry requires **Python 3.10+**. It is multi-platform and the goal is to make it work equally well\non Linux, macOS and Windows.\n\n## Installation\n\n{{% note %}}\nIf you are viewing documentation for the development branch, you may wish to install a preview or development version of Poetry.\nSee the **advanced** installation instructions to use a preview or alternate version of Poetry.\n{{% /note %}}\n\n{{< tabs tabTotal=\"4\" tabID1=\"installing-with-pipx\" tabID2=\"installing-with-the-official-installer\" tabID3=\"installing-manually\" tabID4=\"ci-recommendations\" tabName1=\"With pipx\" tabName2=\"With the official installer\" tabName3=\"Manually (advanced)\" tabName4=\"CI recommendations\">}}\n\n{{< tab tabID=\"installing-with-pipx\" >}}\n\n[`pipx`](https://github.com/pypa/pipx) is used to install Python CLI applications globally while still isolating them in virtual environments.\n`pipx` will manage upgrades and uninstalls when used to install Poetry.\n\n{{< steps >}}\n{{< step >}}\n**Install pipx**\n\nIf `pipx` is not already installed, you can follow any of the options in the\n[official pipx installation instructions](https://pipx.pypa.io/stable/installation/).\nAny non-ancient version of `pipx` will do.\n\n{{< /step >}}\n{{< step >}}\n**Install Poetry**\n\n```bash\npipx install poetry\n```\n{{< /step >}}\n{{< step >}}\n**Install Poetry (advanced)**\n\n{{% note %}}\nYou can skip this step, if you simply want the latest version and already installed Poetry as described in the\nprevious step. This step details advanced usages of this installation method. For example, installing Poetry from\nsource, having multiple versions installed at the same time etc.\n{{% /note %}}\n\n`pipx` can install different versions of Poetry, using the same syntax as pip:\n\n```bash\npipx install poetry==1.8.4\n```\n\n`pipx` can also install versions of Poetry in parallel, which allows for easy testing of alternate or prerelease\nversions. Each version is given a unique, user-specified suffix, which will be used to create a unique binary name:\n\n```bash\npipx install --suffix=@1.8.4 poetry==1.8.4\npoetry@1.8.4 --version\n```\n\n```bash\npipx install --suffix=@preview --pip-args=--pre poetry\npoetry@preview --version\n```\n\nFinally, `pipx` can install any valid [pip requirement spec](https://pip.pypa.io/en/stable/cli/pip_install/), which\nallows for installations of the development version from `git`, or even for local testing of pull requests:\n\n```bash\npipx install --suffix @main git+https://github.com/python-poetry/poetry.git@main\npipx install --suffix @pr1234 git+https://github.com/python-poetry/poetry.git@refs/pull/1234/head\n```\n\n{{< /step >}}\n{{< step >}}\n**Update Poetry**\n\n```bash\npipx upgrade poetry\n```\n{{< /step >}}\n{{< step >}}\n**Uninstall Poetry**\n\n```bash\npipx uninstall poetry\n```\n{{< /step >}}\n{{< /steps >}}\n\n{{< /tab >}}\n{{< tab tabID=\"installing-with-the-official-installer\" >}}\n\nWe provide a custom installer that will install Poetry in a new virtual environment\nand allows Poetry to manage its own environment.\n\n{{< steps >}}\n{{< step >}}\n**Install Poetry**\n\nThe installer script is available directly at [install.python-poetry.org](https://install.python-poetry.org),\nand is developed in [its own repository](https://github.com/python-poetry/install.python-poetry.org).\nThe script can be executed directly (i.e. 'curl python') or downloaded and then executed from disk\n(e.g. in a CI environment).\n\n**Linux, macOS, Windows (WSL)**\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 -\n```\n\n**Windows (Powershell)**\n```powershell\n(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -\n```\n\n{{% note %}}\nIf you have installed Python through the Microsoft Store, replace `py` with `python` in the command\nabove.\n{{% /note %}}\n\n{{< /step >}}\n{{< step >}}\n**Install Poetry (advanced)**\n\n{{% note %}}\nYou can skip this step, if you simply want the latest version and already installed Poetry as described in the\nprevious step. This step details advanced usages of this installation method. For example, installing Poetry from\nsource, using a pre-release build, configuring a different installation location etc.\n{{% /note %}}\n\nBy default, Poetry is installed into a platform and user-specific directory:\n\n- `~/Library/Application Support/pypoetry` on macOS.\n- `~/.local/share/pypoetry` on Linux/Unix.\n- `%APPDATA%\\pypoetry` on Windows.\n\nIf you wish to change this, you may define the `$POETRY_HOME` environment variable:\n\n```bash\ncurl -sSL https://install.python-poetry.org | POETRY_HOME=/etc/poetry python3 -\n```\n\nIf you want to install prerelease versions, you can do so by passing the `--preview` option to the installation script\nor by using the `$POETRY_PREVIEW` environment variable:\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 - --preview\ncurl -sSL https://install.python-poetry.org | POETRY_PREVIEW=1 python3 -\n```\n\nSimilarly, if you want to install a specific version, you can use `--version` option or the `$POETRY_VERSION`\nenvironment variable:\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 - --version 1.8.4\ncurl -sSL https://install.python-poetry.org | POETRY_VERSION=1.8.4 python3 -\n```\n\nYou can also install Poetry from a `git` repository by using the `--git` option:\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 - --git https://github.com/python-poetry/poetry.git@main\n````\nIf you want to install different versions of Poetry in parallel, a good approach is the installation with pipx and suffix.\n\n{{< /step >}}\n{{< step >}}\n**Add Poetry to your PATH**\n\nThe installer creates a `poetry` wrapper in a well-known, platform-specific directory:\n\n- `$HOME/.local/bin` on Unix.\n- `%APPDATA%\\Python\\Scripts` on Windows.\n- `$POETRY_HOME/bin` if `$POETRY_HOME` is set.\n\n{{% note %}}\nIf you have installed Python through the Microsoft Store, the `poetry` binary\nwill be installed to a different location, for example:\n\n```\n%LOCALAPPDATA%\\Packages\\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\n\\LocalCache\\Roaming\\Python\\Scripts\n```\n\nReplace `3.12` with your installed Python version and `qbz5n2kfra8p0` with your suffix.\n{{% /note %}}\n\nIf this directory is not present in your `$PATH`, you can add it in order to invoke Poetry\nas `poetry`.\n\nAlternatively, the full path to the `poetry` binary can always be used:\n\n- `~/Library/Application Support/pypoetry/venv/bin/poetry` on macOS.\n- `~/.local/share/pypoetry/venv/bin/poetry` on Linux/Unix.\n- `%APPDATA%\\pypoetry\\venv\\Scripts\\poetry` on Windows.\n- `$POETRY_HOME/venv/bin/poetry` if `$POETRY_HOME` is set.\n\n{{< /step >}}\n{{< step >}}\n**Use Poetry**\n\nOnce Poetry is installed and in your `$PATH`, you can execute the following:\n\n```bash\npoetry --version\n```\n\nIf you see something like `Poetry (version 2.0.0)`, your installation is ready to use!\n{{< /step >}}\n{{< step >}}\n**Update Poetry**\n\nPoetry is able to update itself when installed using the official installer.\n\n{{% warning %}}\nEspecially on Windows, `self update` may be problematic\nso that a re-install with the installer should be preferred.\n{{% /warning %}}\n\n```bash\npoetry self update\n```\n\nIf you want to install pre-release versions, you can use the `--preview` option.\n\n```bash\npoetry self update --preview\n```\n\nAnd finally, if you want to install a specific version, you can pass it as an argument\nto `self update`.\n\n```bash\npoetry self update 1.8.4\n```\n\n{{< /step >}}\n{{< step >}}\n**Uninstall Poetry**\n\nIf you decide Poetry isn't your thing, you can completely remove it from your system\nby running the installer again with the `--uninstall` option or by setting\nthe `POETRY_UNINSTALL` environment variable before executing the installer.\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 - --uninstall\ncurl -sSL https://install.python-poetry.org | POETRY_UNINSTALL=1 python3 -\n```\n{{< /step >}}\n{{< /steps >}}\n\n{{< /tab >}}\n{{< tab tabID=\"installing-manually\" >}}\n\nPoetry can be installed manually using `pip` and the `venv` module. By doing so you will essentially perform the steps carried\nout by the official installer. As this is an advanced installation method, these instructions are Unix-only and omit specific\nexamples such as installing from `git`.\n\nThe variable `$VENV_PATH` will be used to indicate the path at which the virtual environment was created.\n\n```bash\npython3 -m venv $VENV_PATH\n$VENV_PATH/bin/pip install -U pip setuptools\n$VENV_PATH/bin/pip install poetry\n```\n\nPoetry will be available at `$VENV_PATH/bin/poetry` and can be invoked directly or symlinked elsewhere.\n\nTo uninstall Poetry, simply delete the entire `$VENV_PATH` directory.\n\n{{< /tab >}}\n{{< tab tabID=\"ci-recommendations\" >}}\nUnlike development environments, where making use of the latest tools is desirable, in a CI environment reproducibility\nshould be made the priority. Here are some suggestions for installing Poetry in such an environment.\n\n**Version pinning**\n\nWhatever method you use, it is highly recommended to explicitly control the version of Poetry used, so that you are able\nto upgrade after performing your own validation. Each install method has a different syntax for setting the version that\nis used in the following examples.\n\n**Using pipx**\n\nJust as `pipx` is a powerful tool for development use, it is equally useful in a CI environment\nand should be one of your top choices for use of Poetry in CI.\n\n```bash\npipx install poetry==2.0.0\n```\n\n**Using install.python-poetry.org**\n\n{{% note %}}\nThe official installer script ([install.python-poetry.org](https://install.python-poetry.org)) offers a streamlined and\nsimplified installation of Poetry, sufficient for developer use or for simple pipelines. However, in a CI environment\nthe other two supported installation methods (pipx and manual) should be seriously considered.\n{{% /note %}}\n\nDownloading a copy of the installer script to a place accessible by your CI pipelines (or maintaining a copy of the\n[repository](https://github.com/python-poetry/install.python-poetry.org)) is strongly suggested, to ensure your\npipeline's stability and to maintain control over what code is executed.\n\nBy default, the installer will install to a user-specific directory. In more complex pipelines that may make accessing\nPoetry difficult (especially in cases like multi-stage container builds). It is highly suggested to make use of\n`$POETRY_HOME` when using the official installer in CI, as that way the exact paths can be controlled.\n\n```bash\nexport POETRY_HOME=/opt/poetry\npython3 install-poetry.py --version 2.0.0\n$POETRY_HOME/bin/poetry --version\n```\n\n**Using pip (aka manually)**\n\nFor maximum control in your CI environment, installation with `pip` is fully supported and something you should\nconsider. While this requires more explicit commands and knowledge of Python packaging from you, it in return offers the\nbest debugging experience, and leaves you subject to the fewest external tools.\n\n```bash\nexport POETRY_HOME=/opt/poetry\npython3 -m venv $POETRY_HOME\n$POETRY_HOME/bin/pip install poetry==2.0.0\n$POETRY_HOME/bin/poetry --version\n```\n\n{{% note %}}\nIf you install Poetry via `pip`, ensure you have Poetry installed into an isolated environment that is **not the same**\nas the target environment managed by Poetry. If Poetry and your project are installed into the same environment, Poetry\nis likely to upgrade or uninstall its own dependencies (causing hard-to-debug and understand errors).\n{{% /note %}}\n\n{{< /tab >}}\n{{< /tabs >}}\n\n{{% warning %}}\nPoetry should always be installed in a dedicated virtual environment to isolate it from the rest of your system.\nEach of the above described installation methods ensures that.\nIt should in no case be installed in the environment of the project that is to be managed by Poetry.\nThis ensures that Poetry's own dependencies will not be accidentally upgraded or uninstalled.\nIn addition, the isolated virtual environment in which poetry is installed should not be activated for running poetry commands.\n{{% /warning %}}\n\n## Enable tab completion for Bash, Fish, or Zsh\n\n`poetry` supports generating completion scripts for Bash, Fish, and Zsh.\n\n{{% note %}}\nYou may need to restart your shell in order for these changes to take effect.\n{{% /note %}}\n\nSee `poetry help completions` for full details, but the gist is as simple as using one of the following:\n\n### Bash\n\n#### Auto-loaded (recommended)\n\n```bash\npoetry completions bash >> ~/.bash_completion\n```\n\n#### Lazy-loaded\n\n```bash\npoetry completions bash > ${XDG_DATA_HOME:-~/.local/share}/bash-completion/completions/poetry\n```\n\n### Fish\n\n```fish\npoetry completions fish > ~/.config/fish/completions/poetry.fish\n```\n\n### Zsh\n\n```zsh\npoetry completions zsh > ~/.zfunc/_poetry\n```\n\nYou must then add the following lines in your `~/.zshrc`, if they do not already exist:\n\n```bash\nfpath+=~/.zfunc\nautoload -Uz compinit && compinit\n```\n\n#### Oh My Zsh\n\n```zsh\nmkdir $ZSH_CUSTOM/plugins/poetry\npoetry completions zsh > $ZSH_CUSTOM/plugins/poetry/_poetry\n```\nYou must then add `poetry` to your plugins array in `~/.zshrc`:\n\n```text\nplugins(\n\tpoetry\n\t...\n\t)\n```\n\n#### Prezto\n\n```zsh\npoetry completions zsh > ~/.zprezto/modules/completion/external/src/_poetry\n```\n\nIf completions still don't work, try removing `~/.cache/prezto/zcompcache` and starting a new shell.\n"
  },
  {
    "path": "docs/basic-usage.md",
    "content": "---\ntitle: \"Basic usage\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 10\n---\n\n# Basic usage\n\nFor the basic usage introduction we will be installing `pendulum`, a datetime library.\nIf you have not yet installed Poetry, refer to the [Introduction]({{< relref \"docs\" >}} \"Introduction\") chapter.\n\n## Project setup\n\nFirst, let's create our new project, let's call it `poetry-demo`:\n\n```bash\npoetry new poetry-demo\n```\n\nThis will create the `poetry-demo` directory with the following content:\n\n```text\npoetry-demo\n├── pyproject.toml\n├── README.md\n├── src\n│   └── poetry_demo\n│       └── __init__.py\n└── tests\n    └── __init__.py\n```\n\nThe `pyproject.toml` file is what is the most important here. This will orchestrate\nyour project and its dependencies. For now, it looks like this:\n\n```toml\n[project]\nname = \"poetry-demo\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\n    {name = \"Sébastien Eustace\", email = \"sebastien@eustace.io\"}\n]\nreadme = \"README.md\"\nrequires-python = \">=3.9\"\ndependencies = [\n]\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\nPoetry assumes your package contains a package with the same name as `project.name` located in the root of your\nproject. If this is not the case, populate [`tool.poetry.packages`]({{< relref \"pyproject#packages\" >}}) to specify\nyour packages and their locations.\n\nSimilarly, the traditional `MANIFEST.in` file is replaced by the `project.readme`, `tool.poetry.include`, and\n`tool.poetry.exclude` sections. `tool.poetry.exclude` is additionally implicitly populated by your `.gitignore`. For\nfull documentation on the project format, see the [pyproject section]({{< relref \"pyproject\" >}}) of the documentation.\n\n### Setting a Python Version\n\n{{% note %}}\nUnlike with other packages, Poetry will not automatically install a python interpreter for you.\nIf you want to run Python files in your package like a script or application, you must _bring your own_ python interpreter to run them.\n{{% /note %}}\n\nPoetry will require you to explicitly specify what versions of Python you intend to support, and its universal locking\nwill guarantee that your project is installable (and all dependencies claim support for) all supported Python versions.\nAgain, it's important to remember that -- unlike other dependencies -- setting a Python version is merely specifying which versions of Python you intend to support.\n\nFor example, in this `pyproject.toml` file:\n\n```toml\n[project]\nrequires-python = \">=3.9\"\n```\n\nwe are allowing any version of Python 3 that is greater or equal than `3.9.0`.\n\nWhen you run `poetry install`, you must have access to some version of a Python interpreter that satisfies this constraint available on your system.\nPoetry will not install a Python interpreter for you.\n\n### Initialising a pre-existing project\n\nInstead of creating a new project, Poetry can be used to 'initialize' a pre-populated\ndirectory. To interactively create a `pyproject.toml` file in directory `pre-existing-project`:\n\n```bash\ncd pre-existing-project\npoetry init\n```\n\n### Operating modes\n\nPoetry can be operated in two different modes. The default mode is the **package mode**, which is the right mode\nif you want to package your project into an sdist or a wheel and perhaps publish it to a package index.\nIn this mode, some metadata such as `name` and `version`, which are required for packaging, are mandatory.\nFurther, the project itself will be installed in editable mode when running `poetry install`.\n\nIf you want to use Poetry only for dependency management but not for packaging, you can use the **non-package mode**:\n\n```toml\n[tool.poetry]\npackage-mode = false\n```\n\nIn this mode, metadata such as `name` and `version` are optional.\nTherefore, it is not possible to build a distribution or publish the project to a package index.\nFurther, when running `poetry install`, Poetry does not try to install the project itself,\nbut only its dependencies (same as `poetry install --no-root`).\n\n{{% note %}}\nIn the [pyproject section]({{< relref \"pyproject\" >}}) you can see which fields are required in package mode.\n{{% /note %}}\n\n### Specifying dependencies\nIf you want to add dependencies to your project, you can specify them in the\n`project` section.\n\n```toml\n[project]\n# ...\ndependencies = [\n    \"pendulum (>=2.1,<3.0)\"\n]\n```\n\nAs you can see, it takes a mapping of **package names** and **version constraints**.\n\nPoetry uses this information to search for the right set of files in package \"repositories\" that you register\nin the `tool.poetry.source` section, or on [PyPI](https://pypi.org) by default.\n\nAlso, instead of modifying the `pyproject.toml` file by hand, you can use the `add` command.\n\n```bash\n$ poetry add pendulum\n```\n\nIt will automatically find a suitable version constraint **and install** the package and sub-dependencies.\n\nPoetry supports a rich [dependency specification]({{< relref \"dependency-specification\" >}}) syntax, including caret,\ntilde, wildcard, inequality and\n[multiple constraints]({{< relref \"dependency-specification#multiple-constraints-dependencies\" >}}) requirements.\n\n## Using your virtual environment\n\nBy default, Poetry creates a virtual environment in `{cache-dir}/virtualenvs`.\nYou can change the [`cache-dir`]({{< relref \"configuration#cache-dir\" >}} \"cache-dir configuration documentation\") value\nby editing the Poetry configuration.\nAdditionally, you can use the\n[`virtualenvs.in-project`]({{< relref \"configuration#virtualenvsin-project\" >}}) configuration variable to create\nvirtual environments within your project directory.\n\nThere are several ways to run commands within this virtual environment.\n\n{{% note %}}\n**External virtual environment management**\n\nPoetry will detect and respect an existing virtual environment that has been externally activated. This is a powerful\nmechanism that is intended to be an alternative to Poetry's built-in, simplified environment management.\n\nTo take advantage of this, simply activate a virtual environment using your preferred method or tooling, before running\nany Poetry commands that expect to manipulate an environment.\n{{% /note %}}\n\n### Using `poetry run`\n\nTo run your script simply use `poetry run python your_script.py`.\nLikewise if you have command line tools such as `pytest` or `black` you can run them using `poetry run pytest`.\n\n{{% note %}}\nIf managing your own virtual environment externally, you do not need to use `poetry run` since\nyou will, presumably, already have activated that virtual environment and made available the correct python instance.\nFor example, these commands should output the same python path:\n\n```shell\nconda activate your_env_name\nwhich python\npoetry run which python\neval \"$(poetry env activate)\"\nwhich python\n```\n\n{{% /note %}}\n\n### Activating the virtual environment\n\nSee [Activating the virtual environment]({{< relref \"managing-environments#activating-the-environment\" >}}).\n\n## Version constraints\n\nIn our example, we are requesting the `pendulum` package with the version constraint `>=2.1.0 <3.0.0`.\nThis means any version greater or equal to 2.1.0 and less than 3.0.0.\n\nPlease read [Dependency specification]({{< relref \"dependency-specification\" >}} \"Dependency specification documentation\")\nfor more in-depth information on versions, how versions relate to each other, and on the different ways you can specify\ndependencies.\n\n{{% note %}}\n**How does Poetry download the right files?**\n\nWhen you specify a dependency in `pyproject.toml`, Poetry first takes the name of the package\nthat you have requested and searches for it in any repository you have registered using the `repositories` key.\nIf you have not registered any extra repositories, or it does not find a package with that name in the\nrepositories you have specified, it falls back to PyPI.\n\nWhen Poetry finds the right package, it then attempts to find the best match for the version constraint you have\nspecified.\n{{% /note %}}\n\n## Installing dependencies\n\nTo install the defined dependencies for your project, just run the [`install`]({{< relref \"cli#install\" >}}) command.\n\n```bash\npoetry install\n```\n\nWhen you run this command, one of two things may happen:\n\n### Installing without `poetry.lock`\n\nIf you have never run the command before and there is also no `poetry.lock` file present,\nPoetry simply resolves all dependencies listed in your `pyproject.toml` file and downloads the latest version of their files.\n\nWhen Poetry has finished installing, it writes all the packages and their exact versions that it downloaded to the `poetry.lock` file,\nlocking the project to those specific versions.\nYou should commit the `poetry.lock` file to your project repo so that all people working on the project are locked to the same versions of dependencies (more below).\n\n### Installing with `poetry.lock`\n\nThis brings us to the second scenario. If there is already a `poetry.lock` file as well as a `pyproject.toml` file\nwhen you run `poetry install`, it means either you ran the `install` command before,\nor someone else on the project ran the `install` command and committed the `poetry.lock` file to the project (which is good).\n\nEither way, running `install` when a `poetry.lock` file is present resolves and installs all dependencies that you listed in `pyproject.toml`,\nbut Poetry uses the exact versions listed in `poetry.lock` to ensure that the package versions are consistent for everyone working on your project.\nAs a result you will have all dependencies requested by your `pyproject.toml` file,\nbut they may not all be at the very latest available versions\n(some dependencies listed in the `poetry.lock` file may have released newer versions since the file was created).\nThis is by design, it ensures that your project does not break because of unexpected changes in dependencies.\n\n### Committing your `poetry.lock` file to version control\n\n#### As an application developer\n\nApplication developers commit `poetry.lock` to get more reproducible builds.\n\nCommitting this file to VC is important because it will cause anyone who sets up the project\nto use the exact same versions of the dependencies that you are using.\nYour CI server, production machines, other developers in your team,\neverything and everyone runs on the same dependencies,\nwhich mitigates the potential for bugs affecting only some parts of the deployments.\nEven if you develop alone, in six months when reinstalling the project you can feel confident\nthe dependencies installed are still working even if your dependencies released many new versions since then.\n(See note below about using the update command.)\n\n{{% warning %}} If you have added the recommended [`[build-system]`]({{< relref \"pyproject#poetry-and-pep-517\" >}}) section to your project's pyproject.toml then you _can_ successfully install your project and its dependencies into a virtual environment using a command like `pip install -e .`. However, pip will not use the lock file to determine dependency versions as the poetry-core build system is intended for library developers (see next section).\n{{% /warning %}}\n\n#### As a library developer\n\nLibrary developers have more to consider. Your users are application developers, and your library will run in a Python environment you don't control.\n\nThe application ignores your library's lock file. It can use whatever dependency version meets the constraints in your `pyproject.toml`. The application will probably use the latest compatible dependency version. If your library's `poetry.lock` falls behind some new dependency version that breaks things for your users, you're likely to be the last to find out about it.\n\nA simple way to avoid such a scenario is to omit the `poetry.lock` file. However, by doing so, you sacrifice reproducibility and performance to a certain extent. Without a lockfile, it can be difficult to find the reason for failing tests, because in addition to obvious code changes an unnoticed library update might be the culprit. Further, Poetry will have to lock before installing a dependency if `poetry.lock` has been omitted. Depending on the number of dependencies, locking may take a significant amount of time.\n\nIf you do not want to give up the reproducibility and performance benefits, consider a regular refresh of `poetry.lock` to stay up-to-date and reduce the risk of sudden breakage for users.\n\n### Installing dependencies only\n\nThe current project is installed in [editable](https://pip.pypa.io/en/stable/topics/local-project-installs/) mode by default.\n\nIf you want to install the dependencies only, run the `install` command with the `--no-root` flag:\n\n```bash\npoetry install --no-root\n```\n\n## Updating dependencies to their latest versions\n\nAs mentioned above, the `poetry.lock` file prevents you from automatically getting the latest versions\nof your dependencies.\nTo update to the latest versions, use the `update` command.\nThis will fetch the latest matching versions (according to your `pyproject.toml` file)\nand update the lock file with the new versions.\n(This is equivalent to deleting the `poetry.lock` file and running `install` again.)\n\n{{% note %}}\nPoetry will display a **Warning** when executing an install command if `poetry.lock` and `pyproject.toml`\nare not synchronized.\n{{% /note %}}\n"
  },
  {
    "path": "docs/building-extension-modules.md",
    "content": "---\ntitle: \"Building extension modules\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 125\n---\n\n# Building Extension Modules\n\n{{% warning %}}\nWhile this feature has been around since almost the beginning of the Poetry project and has needed minimal changes,\nit is still considered unstable. You can participate in the discussions about stabilizing this feature\n[here](https://github.com/python-poetry/poetry/issues/2740).\n\nAnd as always, your contributions towards the goal of improving this feature are also welcome.\n{{% /warning %}}\n\nPoetry allows a project developer to introduce support for, build and distribute native extensions within their project.\nIn order to achieve this, at the highest level, the following steps are required.\n\n{{< steps >}}\n{{< step >}}\n**Add Build Dependencies**\n\nThe build dependencies, in this context, refer to those Python packages that are required in order to successfully execute\nyour build script. Common examples include `cython`, `meson`, `maturin`, `setuptools` etc., depending on how your\nextension is built.\n\n{{% note %}}\nYou must assume that only Python built-ins are available by default in a build environment. This means, if you need\neven packages like `setuptools`, it must be explicitly declared.\n{{% /note %}}\n\nThe necessary build dependencies must be added to the `build-system.requires` section of your `pyproject.toml` file.\n\n```toml\n[build-system]\nrequires = [\"poetry-core\", \"setuptools\", \"cython\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{% note %}}\nIt is recommended that you consider specifying version constraints to all entries in `build-system.requires` in order to\navoid surprises if one of the packages introduce a breaking change. For example, you can set `cython` to\n`cython>=3.0.11,<4.0.0` to ensure no major version upgrades are used.\n{{% /note %}}\n\n{{% note %}}\nIf you wish to develop the build script within your project's virtual environment, then you must also add the\ndependencies to your project explicitly to a dependency group - the name of which is not important.\n\n```sh\npoetry add --group=build setuptools cython\n```\n{{% /note %}}\n\n{{< /step >}}\n\n{{< step >}}\n**Add Build Script**\n\nThe build script can be a free-form Python script that uses any dependency specified in the previous step. This can be\nnamed as needed, but **must** be located within the project root directory (or a subdirectory) and also **must**\nbe included in your source distribution. You can see the [example snippets section]({{< relref \"#example-snippets\" >}})\nfor inspiration.\n\n{{% note %}}\nThe build script is always executed from the project root. And it is expected to move files around to their destinations\nas expected by Poetry as per your `pyproject.toml` file.\n{{% /note %}}\n\n```toml\n[tool.poetry.build]\nscript = \"relative/path/to/build-extension.py\"\n```\n\n{{% note %}}\nThe name of the build script is arbitrary. Common practice has been to name it `build.py`, however, this is not\nmandatory. You **should** consider [using a subdirectory]({{< relref \"#can-i-store-the-build-script-in-a-subdirectory\" >}})\nif feasible.\n{{% /note %}}\n\n{{< /step >}}\n\n{{< step >}}\n**Specify Distribution Files**\n\n{{% warning %}}\nThe following is an example, and should not be considered as complete.\n{{% /warning %}}\n\n```toml\n[tool.poetry]\n...\ninclude = [\n    { path = \"package/**/*.so\", format = \"wheel\" },\n    # sources must be present in sdist, can be ignored if you only have *.pyx sources\n    { path = \"package/**/*.c\", format = \"sdist\" },\n]\n```\n\nThe key takeaway here should be the following. You can refer to the [`pyproject.toml`]({{< relref \"pyproject#exclude-and-include\" >}})\ndocumentation for information on each of the relevant sections.\n\n1. Include your build outputs in your wheel.\n2. Exclude your build inputs from your wheel.\n3. Include your build inputs to your source distribution.\n\n{{< /step >}}\n\n{{< /steps >}}\n\n## Example Snippets\n\n### Cython\n\n{{< tabs tabTotal=\"3\" tabID1=\"cython-pyproject\" tabName1=\"pyproject.toml\" tabID2=\"cython-build-script\" tabName2=\"build-extension.py\" tabID3=\"cython-src-tree\" tabName3=\"Source Tree\">}}\n\n{{< tab tabID=\"cython-pyproject\" >}}\n\n```toml\n[build-system]\nrequires = [\"poetry-core\", \"cython\", \"setuptools\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[tool.poetry]\n...\npackages = [\n    { include = \"package\", from = \"src\"},\n]\ninclude = [\n    { path = \"src/package/**/*.so\", format = \"wheel\" },\n]\n# if not already excluded via .gitignore\nexclude = [\n    \"**/*.c\"\n]\n\n[tool.poetry.build]\nscript = \"scripts/build-extension.py\"\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"cython-build-script\" >}}\n\n```py\nfrom __future__ import annotations\n\nimport os\nimport shutil\n\nfrom pathlib import Path\n\nfrom Cython.Build import cythonize\nfrom setuptools import Distribution\nfrom setuptools import Extension\nfrom setuptools.command.build_ext import build_ext\n\n\nCOMPILE_ARGS = [\"-march=native\", \"-O3\", \"-msse\", \"-msse2\", \"-mfma\", \"-mfpmath=sse\"]\nLINK_ARGS = []\nINCLUDE_DIRS = []\nLIBRARIES = [\"m\"]\n\n\ndef build() -> None:\n    extensions = [\n        Extension(\n            \"*\",\n            [\"src/package/*.pyx\"],\n            extra_compile_args=COMPILE_ARGS,\n            extra_link_args=LINK_ARGS,\n            include_dirs=INCLUDE_DIRS,\n            libraries=LIBRARIES,\n        )\n    ]\n    ext_modules = cythonize(\n        extensions,\n        include_path=INCLUDE_DIRS,\n        compiler_directives={\"binding\": True, \"language_level\": 3},\n    )\n\n    distribution = Distribution({\n        \"name\": \"package\",\n        \"ext_modules\": ext_modules\n    })\n\n    cmd = build_ext(distribution)\n    cmd.ensure_finalized()\n    cmd.run()\n\n    # Copy built extensions back to the project\n    for output in cmd.get_outputs():\n        output = Path(output)\n        relative_extension = Path(\"src\") / output.relative_to(cmd.build_lib)\n\n        shutil.copyfile(output, relative_extension)\n        mode = os.stat(relative_extension).st_mode\n        mode |= (mode & 0o444) >> 2\n        os.chmod(relative_extension, mode)\n\n\nif __name__ == \"__main__\":\n    build()\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"cython-src-tree\" >}}\n\n```\nscripts/\n└── build-extension.py\nsrc/\n└── package\n    ├── example.pyx\n    └── __init__.py\n```\n\n{{< /tab >}}\n\n{{< /tabs >}}\n\n### Meson\n\n{{< tabs tabTotal=\"2\" tabID1=\"meson-pyproject\" tabName1=\"pyproject.toml\" tabID2=\"meson-build-script\" tabName2=\"build-extension.py\">}}\n\n{{< tab tabID=\"meson-pyproject\" >}}\n\n```toml\n[tool.poetry.build]\nscript = \"build-extension.py\"\n\n[build-system]\nrequires = [\"poetry-core\", \"meson\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"meson-build-script\" >}}\n\n```py\nfrom __future__ import annotations\n\nimport subprocess\n\nfrom pathlib import Path\n\n\ndef meson(*args):\n    subprocess.call([\"meson\", *args])\n\n\ndef build():\n    build_dir = Path(__file__).parent.joinpath(\"build\")\n    build_dir.mkdir(parents=True, exist_ok=True)\n\n    meson(\"setup\", build_dir.as_posix())\n    meson(\"compile\", \"-C\", build_dir.as_posix())\n    meson(\"install\", \"-C\", build_dir.as_posix())\n\n\nif __name__ == \"__main__\":\n    build()\n```\n\n{{< /tab >}}\n\n{{< /tabs >}}\n\n### Maturin\n\n{{< tabs tabTotal=\"2\" tabID1=\"maturin-pyproject\" tabName1=\"pyproject.toml\" tabID2=\"maturin-build-script\" tabName2=\"build-extension.py\">}}\n\n{{< tab tabID=\"maturin-pyproject\" >}}\n\n```toml\n[tool.poetry.build]\nscript = \"build-extension.py\"\n\n[build-system]\nrequires = [\"poetry-core\", \"maturin\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"maturin-build-script\" >}}\n\n```py\nimport os\nimport shlex\nimport shutil\nimport subprocess\nimport zipfile\n\nfrom pathlib import Path\n\n\ndef maturin(*args):\n    subprocess.call([\"maturin\", *list(args)])\n\n\ndef build():\n    build_dir = Path(__file__).parent.joinpath(\"build\")\n    build_dir.mkdir(parents=True, exist_ok=True)\n\n    wheels_dir = Path(__file__).parent.joinpath(\"target/wheels\")\n    if wheels_dir.exists():\n        shutil.rmtree(wheels_dir)\n\n    cargo_args = []\n    if os.getenv(\"MATURIN_BUILD_ARGS\"):\n        cargo_args = shlex.split(os.getenv(\"MATURIN_BUILD_ARGS\", \"\"))\n\n    maturin(\"build\", \"-r\", *cargo_args)\n\n    # We won't use the wheel built by maturin directly since\n    # we want Poetry to build it, but we need to retrieve the\n    # compiled extensions from the maturin wheel.\n    wheel = next(iter(wheels_dir.glob(\"*.whl\")))\n    with zipfile.ZipFile(wheel.as_posix()) as whl:\n        whl.extractall(wheels_dir.as_posix())\n\n        for extension in wheels_dir.rglob(\"**/*.so\"):\n            shutil.copyfile(extension, Path(__file__).parent.joinpath(extension.name))\n\n    shutil.rmtree(wheels_dir)\n\n\nif __name__ == \"__main__\":\n    build()\n```\n\n{{< /tab >}}\n\n{{< /tabs >}}\n\n## FAQ\n### When is my build script executed?\nIf your project uses a build script, it is run implicitly in the following scenarios.\n\n1. When `poetry install` is run, it is executed prior to installing the project's root package.\n2. When `poetry build` is run, it is executed prior to building distributions.\n3. When a PEP 517 build is triggered from source or sdist by another build frontend.\n\n### How does Poetry ensure my build script's dependencies are met?\nPrior to executing the build script, Poetry creates a temporary virtual environment with your project's active Python\nversion and then installs all dependencies specified under `build-system.requires` into this environment. It should be\nnoted that no packages will be present in this environment at the time of creation.\n\n### Can I store the build script in a subdirectory?\nYes you can. If storing the script in a subdirectory, your `pyproject.toml` might look something like this.\n\n```toml\n[tool.poetry]\n...\npackages = [\n    { include = \"package\", from = \"src\"}\n]\ninclude = [\n    { path = \"src/package/**/*.so\", format = \"wheel\" },\n]\n# exclude any intermediate source files\nexclude = [\n    \"**/*.c\"\n]\n\n[tool.poetry.build]\nscript = \"scripts/build-extension.py\"\n```\n"
  },
  {
    "path": "docs/cli.md",
    "content": "---\ntitle: \"Commands\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 30\n---\n\n\n# Commands\n\nYou've already learned how to use the command-line interface to do some things.\nThis chapter documents all the available commands.\n\nTo get help from the command-line, simply call `poetry` to see the complete list of commands,\nthen `--help` combined with any of those can give you more information.\n\n## Global Options\n\n* `--verbose (-v|vv|vvv)`: Increase the verbosity of messages: \"-v\" for normal output, \"-vv\" for more verbose output and \"-vvv\" for debug.\n\n{{% note %}}\nYou can also set the verbosity level using the `SHELL_VERBOSITY` environment variable.\nThis is useful in CI/CD pipelines or scripts where you cannot easily modify command-line arguments.\n\n| Value  | Equivalent | Description         |\n|--------|------------|---------------------|\n| `-1`   | `-q`       | Quiet mode          |\n| `0`    | (default)  | Normal output       |\n| `1`    | `-v`       | Verbose output      |\n| `2`    | `-vv`      | More verbose output |\n| `3`    | `-vvv`     | Debug output        |\n\n{{% /note %}}\n\n* `--help (-h)` : Display help information.\n* `--quiet (-q)` : Do not output any message.\n* `--ansi`: Force ANSI output.\n* `--no-ansi`: Disable ANSI output.\n* `--version (-V)`: Display this application version.\n* `--no-interaction (-n)`: Do not ask any interactive question.\n* `--no-plugins`: Disables plugins.\n* `--no-cache`: Disables Poetry source caches.\n* `--directory=DIRECTORY (-C)`: The working directory for the Poetry command (defaults to the current working directory). All command-line arguments will be resolved relative to the given directory.\n* `--project=PROJECT (-P)`: Specify another path as the project root. All command-line arguments will be resolved relative to the current working directory or directory specified using `--directory` option if used.\n\n## about\n\nThe `about` command displays global information about Poetry, including the current version and version of `poetry-core`.\n\n```bash\npoetry about\n```\n\n## add\n\nThe `add` command adds required packages to your `pyproject.toml` and installs them.\n\nIf you do not specify a version constraint, poetry will attempt to use the latest version.\n\n```bash\npoetry add requests pendulum\n```\n\n{{% note %}}\nA package is looked up, by default, only from [PyPI](https://pypi.org).\nYou can modify the default source (PyPI);\nor add and use [Supplemental Package Sources]({{< relref \"repositories/#supplemental-package-sources\" >}})\nor [Explicit Package Sources]({{< relref \"repositories/#explicit-package-sources\" >}}).\n\nFor more information, refer to the [Package Sources]({{< relref \"repositories/#package-sources\" >}}) documentation.\n{{% /note %}}\n\nYou can also specify a constraint when adding a package:\n\n```bash\n# Allow >=2.0.5, <3.0.0 versions\npoetry add pendulum@^2.0.5\n\n# Allow >=2.0.5, <2.1.0 versions\npoetry add pendulum@~2.0.5\n\n# Allow >=2.0.5 versions, without upper bound\npoetry add \"pendulum>=2.0.5\"\n\n# Allow only 2.0.5 version\npoetry add pendulum==2.0.5\n```\n\n{{% note %}}\nSee the [Dependency specification]({{< relref \"dependency-specification#using-the--operator\" >}}) page for more information about the `@` operator.\n{{% /note %}}\n\nIf you try to add a package that is already present, you will get an error.\nHowever, if you specify a constraint, like above, the dependency will be updated\nby using the specified constraint.\n\nIf you want to get the latest version of an already\npresent dependency, you can use the special `latest` constraint:\n\n```bash\npoetry add pendulum@latest\n```\n\n{{% note %}}\nSee the [Dependency specification]({{< relref \"dependency-specification\" >}}) for more information on setting the version constraints for a package.\n{{% /note %}}\n\nYou can also add `git` dependencies:\n\n```bash\npoetry add git+https://github.com/sdispater/pendulum.git\n```\n\nor use ssh instead of https:\n\n```bash\npoetry add git+ssh://git@github.com/sdispater/pendulum.git\n\n# or alternatively:\npoetry add git+ssh://git@github.com:sdispater/pendulum.git\n```\n\nIf you need to checkout a specific branch, tag or revision,\nyou can specify it when using `add`:\n\n```bash\npoetry add git+https://github.com/sdispater/pendulum.git#develop\npoetry add git+https://github.com/sdispater/pendulum.git#2.0.5\n\n# or using SSH instead:\npoetry add git+ssh://git@github.com:sdispater/pendulum.git#develop\npoetry add git+ssh://git@github.com:sdispater/pendulum.git#2.0.5\n```\n\nor reference a subdirectory:\n\n```bash\npoetry add git+https://github.com/myorg/mypackage_with_subdirs.git@main#subdirectory=subdir\n```\n\nYou can also add a local directory or file:\n\n```bash\npoetry add ./my-package/\npoetry add ../my-package/dist/my-package-0.1.0.tar.gz\npoetry add ../my-package/dist/my_package-0.1.0.whl\n```\n\nIf you want the dependency to be installed in editable mode you can use the `--editable` option.\n\n```bash\npoetry add --editable ./my-package/\npoetry add --editable git+ssh://github.com/sdispater/pendulum.git#develop\n```\n\nAlternatively, you can specify it in the `pyproject.toml` file. It means that changes in the local directory will be reflected directly in environment.\n\n```toml\n[tool.poetry.dependencies]\nmy-package = {path = \"../my/path\", develop = true}\n```\n\n{{% note %}}\nThe `develop` attribute is a Poetry-specific feature, so it is not included in the package distribution metadata.\nIn other words, it is only considered when using Poetry to install the project.\n{{% /note %}}\n\nIf the package(s) you want to install provide extras, you can specify them\nwhen adding the package:\n\n```bash\npoetry add \"requests[security,socks]\"\npoetry add \"requests[security,socks]~=2.22.0\"\npoetry add \"git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]\"\n```\n\n{{% warning %}}\nSome shells may treat square braces (`[` and `]`) as special characters. It is suggested to always quote arguments containing these characters to prevent unexpected shell expansion.\n{{% /warning %}}\n\nIf you want to add a package to a specific group of dependencies, you can use the `--group (-G)` option:\n\n```bash\npoetry add mkdocs --group docs\n```\n\nSee [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) for more information\nabout dependency groups.\n\n#### Options\n\n* `--group (-G)`: The group to add the dependency to.\n* `--dev (-D)`: Add package as development dependency. (shortcut for `-G dev`)\n* `--editable (-e)`: Add vcs/path dependencies as editable.\n* `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed)\n* `--optional`: Add as an optional dependency to an extra.\n* `--python`: Python version for which the dependency must be installed.\n* `--platform`: Platforms for which the dependency must be installed.\n* `--markers`: Environment markers which describe when the dependency should be installed.\n* `--source`: Name of the source to use to install the package.\n* `--allow-prereleases`: Accept prereleases.\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n* `--lock`: Do not perform install (only update the lockfile).\n\n## build\n\nThe `build` command builds the source and wheels archives.\n\n```bash\npoetry build\n```\n\nThe command will trigger the build system defined in the `pyproject.toml` file according to [PEP 517](https://peps.python.org/pep-0517/).\nIf necessary the build process happens in an isolated environment.\n\n#### Options\n\n* `--format (-f)`: Limit the format to either `wheel` or `sdist`.\n* `--clean`: Clean output directory before building.\n* `--local-version (-l)`: Add or replace a local version label to the build (deprecated).\n* `--output (-o)`: Set output directory for build artifacts. Default is `dist`.\n* `--config-settings=<key>=<value> (-c)`: Config settings to be passed to the build back-end. (multiple allowed)\n\n{{% note %}}\nWhen using `--local-version`, the identifier must be [PEP 440](https://peps.python.org/pep-0440/#local-version-identifiers)\ncompliant. This is useful for adding build numbers, platform specificities, etc. for private packages.\n\n`--local-version` is deprecated and will be removed in a future version of Poetry.\nUse `--config-settings local-version=<version>` instead.\n{{% /note %}}\n\n{{% warning %}}\nLocal version identifiers SHOULD NOT be used when publishing upstream projects to a public index server, but MAY be\nused to identify private builds created directly from the project source.\n\nSee [PEP 440](https://peps.python.org/pep-0440/#local-version-identifiers) for more information.\n{{% /warning %}}\n\n## cache\n\nThe `cache` command groups subcommands to interact with Poetry's cache.\n\n### cache clear\n\nThe `cache clear` command removes packages from cached repositories.\n\nFor example, to clear the whole cache of packages from all repositories, run:\n\n```bash\npoetry cache clear --all\n```\n\nTo only clear all packages from the `PyPI` repository, run:\n\n```bash\npoetry cache clear PyPI --all\n```\n\nTo only remove a specific package from a cache, you have to specify the cache entry in the following form `cache:package:version`:\n\n```bash\npoetry cache clear PyPI:requests:2.24.0\n```\n\n### cache list\n\nThe `cache list` command lists Poetry's available caches.\n\n```bash\npoetry cache list\n```\n\n## check\n\nThe `check` command validates the content of the `pyproject.toml` file\nand its consistency with the `poetry.lock` file.\nIt returns a detailed report if there are any errors.\n\n{{% note %}}\nThis command is also available as a pre-commit hook. See [pre-commit hooks]({{< relref \"pre-commit-hooks#poetry-check\">}}) for more information.\n{{% /note %}}\n\n```bash\npoetry check\n```\n\n#### Options\n\n* `--lock`: Verifies that `poetry.lock` exists for the current `pyproject.toml`.\n* `--strict`: Fail if check reports warnings.\n\n## config\n\nThe `config` command allows you to edit poetry config settings and repositories.\n\n```bash\npoetry config --list\n```\n\n### Usage\n\n````bash\npoetry config [options] [setting-key] [setting-value1] ... [setting-valueN]\n````\n\n`setting-key` is a configuration option name and `setting-value1` is a configuration value.\nSee [Configuration]({{< relref \"configuration\" >}}) for all available settings.\n\n{{% warning %}}\nUse `--` to terminate option parsing if your values may start with a hyphen (`-`), e.g.\n```bash\npoetry config http-basic.custom-repo gitlab-ci-token -- ${GITLAB_JOB_TOKEN}\n```\nWithout `--` this command will fail if `${GITLAB_JOB_TOKEN}` starts with a hyphen.\n{{% /warning%}}\n\n#### Options\n\n* `--unset`: Remove the configuration element named by `setting-key`.\n* `--list`: Show the list of current config variables.\n* `--local`: Set/Get settings that are specific to a project (in the local configuration file `poetry.toml`).\n* `--migrate`: Migrate outdated configuration settings.\n\n## debug\n\nThe `debug` command groups subcommands that are useful for, as the name suggests, debugging issues you might have\nwhen using Poetry with your projects.\n\n### debug info\n\nThe `debug info` command shows debug information about Poetry and your project's virtual environment.\n\n### debug resolve\n\nThe `debug resolve` command helps when debugging dependency resolution issues. The command attempts to resolve your\ndependencies and list the chosen packages and versions.\n\n### debug tags\n\nThe `debug tags` command is useful when you want to see the supported packaging tags for your project's active\nvirtual environment. This is useful when Poetry cannot install any known binary distributions for a dependency.\n\n## env\n\nThe `env` command groups subcommands to interact with the virtualenvs\nassociated with a specific project.\n\nSee [Managing environments]({{< relref \"managing-environments\" >}}) for more information about these commands.\n\n### env activate\n\nThe `env activate` command prints the command to activate a virtual environment in your current shell.\n\n{{% note %}}\nThis command does not activate the virtual environment, but only displays the activation command, for more information\non how to use this command see [here]({{< relref \"managing-environments#activating-the-environment\" >}}).\n{{% /note %}}\n\n### env info\n\nThe `env info` command displays information about the current environment.\n\n#### Options\n\n* `--path (-p)`: Only display the environment's path.\n* `--executable (-e)`: Only display the environment's python executable path.\n\n### env list\n\nThe `env list` command lists all virtualenvs associated with the current project.\n\n#### Options\n\n* `--full-path`: Output the full paths of the virtualenvs.\n\n### env remove\n\nThe `env remove` command removes virtual environments associated with the project. You can specify multiple Python\nexecutables or virtual environment names to remove all matching ones. Alternatively, you can remove all associated\nvirtual environments using the `--all` option.\n\n{{% note %}}\nIf `virtualenvs.in-project` config is set to `true`, no argument or option is required. Your in project virtual environment is removed.\n{{% /note %}}\n\n#### Arguments\n\n* `python`: The python executables associated with, or names of the virtual environments which are to be removed. Can be specified multiple times.\n\n#### Options\n\n* `--all`: Remove all managed virtual environments associated with the project.\n\n### env use\n\nThe `env use` command activates or creates a new virtualenv for the current project.\n\n#### Arguments\n\n* `python`: The python executable to use. This can be a version number (if not on Windows) or a path to the python binary.\n\n## export\n\n{{% warning %}}\nThis command is provided by the [Export Poetry Plugin](https://github.com/python-poetry/poetry-plugin-export).\nThe plugin is no longer installed by default with Poetry 2.0.\n\nSee [Using plugins]({{< relref \"plugins#using-plugins\" >}}) for information on how to install a plugin.\nAs described in [Project plugins]({{< relref \"plugins#project-plugins\" >}}),\nyou can also define in your `pyproject.toml` that the plugin is required for the development of your project:\n\n```toml\n[tool.poetry.requires-plugins]\npoetry-plugin-export = \">=1.8\"\n```\n{{% /warning %}}\n\n{{% note %}}\nThe `export` command is also available as a pre-commit hook.\nSee [pre-commit hooks]({{< relref \"pre-commit-hooks#poetry-export\" >}}) for more information.\n{{% /note %}}\n\n## help\n\nThe `help` command displays global help, or help for a specific command.\n\nTo display global help:\n\n```bash\npoetry help\n```\n\nTo display help for a specific command, for instance `show`:\n\n```bash\npoetry help show\n```\n\n{{% note %}}\nThe `--help` option can also be passed to any command to get help for a specific command.\n\nFor instance:\n\n```bash\npoetry show --help\n```\n{{% /note %}}\n\n## init\n\nThis command will help you create a `pyproject.toml` file interactively\nby prompting you to provide basic information about your package.\n\nIt will interactively ask you to fill in the fields, while using some smart defaults.\n\n```bash\npoetry init\n```\n\n#### Options\n\n* `--name`: Name of the package.\n* `--description`: Description of the package.\n* `--author`: Author of the package.\n* `--python` Compatible Python versions.\n* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`.\n* `--dev-dependency`: Development requirements, see `--dependency`.\n\n## install\n\nThe `install` command reads the `pyproject.toml` file from the current project,\nresolves the dependencies, and installs them.\n\n```bash\npoetry install\n```\n\nIf there is a `poetry.lock` file in the current directory,\nit will use the exact versions from there instead of resolving them.\nThis ensures that everyone using the library will get the same versions of the dependencies.\n\nIf there is no `poetry.lock` file, Poetry will create one after dependency resolution.\n\n{{% note %}}\n**When to use `install` vs `update`:**\n- Use `poetry install` to install dependencies as specified in `poetry.lock` (or resolve dependencies and create the lock file if it is missing).\n  This is what you run after cloning a project. For reproducible installs, prefer `poetry sync`,\n  which also removes packages that are not in the lock file.\n- Use `poetry update` when you want to update dependencies to their latest versions (within the constraints from the `pyproject.toml`)\n  and refresh `poetry.lock`.\n{{% /note %}}\n\n{{% note %}}\nNormally, you should prefer `poetry sync` to `poetry install` to avoid untracked outdated packages.\nHowever, if you have set `virtualenvs.create = false` to install dependencies into your system environment,\nwhich is discouraged, or `virtualenvs.options.system-site-packages = true` to make\nsystem site-packages available in your virtual environment, you should use `poetry install`\nbecause `poetry sync` will normally not work well in these cases.\n{{% /note %}}\n\nIf you want to exclude one or more dependency groups for the installation, you can use\nthe `--without` option.\n\n```bash\npoetry install --without test,docs\n```\n\nYou can also select optional dependency groups with the `--with` option.\n\n```bash\npoetry install --with test,docs\n```\n\nTo install all dependency groups including the optional groups, use the ``--all-groups`` flag.\n\n```bash\npoetry install --all-groups\n```\n\nIt's also possible to only install specific dependency groups by using the `only` option.\n\n```bash\npoetry install --only test,docs\n```\n\nTo only install the project itself with no dependencies, use the `--only-root` flag.\n\n```bash\npoetry install --only-root\n```\n\nSee [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) for more information\nabout dependency groups.\n\nYou can also specify the extras you want installed\nby passing the `-E|--extras` option (See [Extras]({{< relref \"pyproject#extras\" >}}) for more info).\nPass `--all-extras` to install all defined extras for a project.\n\n```bash\npoetry install --extras \"mysql pgsql\"\npoetry install -E mysql -E pgsql\npoetry install --all-extras\n```\n\nAny extras not specified will be kept but not installed:\n\n```bash\npoetry install --extras \"A B\"  # C is kept if already installed\n```\n\nIf you want to remove unspecified extras, use the `sync` command.\n\nBy default `poetry` will install your project's package every time you run `install`:\n\n```bash\n$ poetry install\nInstalling dependencies from lock file\n\nNo dependencies to install or update\n\n  - Installing <your-package-name> (x.x.x)\n```\n\nIf you want to skip this installation, use the `--no-root` option.\n\n```bash\npoetry install --no-root\n```\n\nSimilar to `--no-root` you can use `--no-directory` to skip directory path dependencies:\n\n```bash\npoetry install --no-directory\n```\n\nThis is mainly useful for caching in CI or when building Docker images. See the [FAQ entry]({{< relref \"faq#poetry-busts-my-docker-cache-because-it-requires-me-to-copy-my-source-files-in-before-installing-3rd-party-dependencies\" >}}) for more information on this option.\n\nBy default `poetry` does not compile Python source files to bytecode during installation.\nThis speeds up the installation process, but the first execution may take a little more\ntime because Python then compiles source files to bytecode automatically.\nIf you want to compile source files to bytecode during installation,\nyou can use the `--compile` option:\n\n```bash\npoetry install --compile\n```\n\n#### Options\n\n* `--without`: The dependency groups to ignore.\n* `--with`: The optional dependency groups to include.\n* `--only`: The only dependency groups to include.\n* `--only-root`: Install only the root project, exclude all dependencies.\n* `--sync`: Synchronize the environment with the locked packages and the specified groups. (**Deprecated**, use `poetry sync` instead)\n* `--no-root`: Do not install the root package (your project).\n* `--no-directory`: Skip all directory path dependencies (including transitive ones).\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n* `--extras (-E)`: Features to install (multiple values allowed).\n* `--all-extras`: Install all extra features (conflicts with `--extras`).\n* `--all-groups`: Install dependencies from all groups (conflicts with `--only`, `--with`, and `--without`).\n* `--compile`: Compile Python source files to bytecode.\n\n{{% note %}}\nWhen `--only` is specified, `--with` and `--without` options are ignored.\n{{% /note %}}\n\n## list\n\nThe `list` command displays all the available Poetry commands.\n\n```bash\npoetry list\n```\n\n## lock\n\nThis command locks (without installing) the dependencies specified in `pyproject.toml`.\n\n{{% note %}}\nBy default, packages that have already been added to the lock file before will not be updated.\nTo update all dependencies to the latest available compatible versions, use `poetry update --lock`\nor `poetry lock --regenerate`, which normally produce the same result.\nThis command is also available as a pre-commit hook. See [pre-commit hooks]({{< relref \"pre-commit-hooks#poetry-lock\">}}) for more information.\n{{% /note %}}\n\n```bash\npoetry lock\n```\n\n#### Options\n\n* `--regenerate`: Ignore existing lock file and overwrite it with a new lock file created from scratch.\n\n## new\n\nThis command will help you kickstart your new Python project by creating a new Poetry project. By default, a `src`\nlayout is chosen.\n\n```bash\npoetry new my-package\n```\n\nwill create a folder as follows:\n\n```text\nmy-package\n├── pyproject.toml\n├── README.md\n├── src\n│   └── my_package\n│       └── __init__.py\n└── tests\n    └── __init__.py\n```\n\nIf you want to name your project differently than the folder, you can pass\nthe `--name` option:\n\n```bash\npoetry new my-folder --name my-package\n```\n\nIf you want to use a `flat` project layout, you can use the `--flat` option:\n\n```bash\npoetry new --flat my-package\n```\n\nThat will create a folder structure as follows:\n\n```text\nmy-package\n├── pyproject.toml\n├── README.md\n├── my_package\n│   └── __init__.py\n└── tests\n    └── __init__.py\n```\n\n{{% note %}}\nFor an overview of the differences between `flat` and `src` layouts, please see\n[here](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).\n{{% /note %}}\n\nThe `--name` option is smart enough to detect namespace packages and create\nthe required structure for you.\n\n```bash\npoetry new --name my.package my-package\n```\n\nwill create the following structure:\n\n```text\nmy-package\n├── pyproject.toml\n├── README.md\n├── src\n│   └── my\n│       └── package\n│           └── __init__.py\n└── tests\n    └── __init__.py\n```\n\n#### Options\n\n* `--interactive (-i)`: Allow interactive specification of project configuration.\n* `--name`: Set the resulting package name.\n* `--flat`: Use the flat layout for the project.\n* `--readme`: Specify the readme file extension. Default is `md`. If you intend to publish to PyPI\n  keep the [recommendations for a PyPI-friendly README](https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/)\n  in mind.\n* `--description`: Description of the package.\n* `--author`: Author of the package.\n* `--python` Compatible Python versions.\n* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`.\n* `--dev-dependency`: Development requirements, see `--dependency`.\n\n## publish\n\nThis command publishes the package, previously built with the [`build`](#build) command, to the remote repository.\n\nIt will automatically register the package before uploading if this is the first time it is submitted.\n\n```bash\npoetry publish\n```\n\nIt can also build the package if you pass it the `--build` option.\n\n{{% note %}}\nSee [Publishable Repositories]({{< relref \"repositories/#publishable-repositories\" >}}) for more information\non how to configure and use publishable repositories.\n{{% /note %}}\n\n{{% warning %}}\nOnly artifacts of the latest version of your package in the dist directory will be uploaded.\nOlder versions from previous builds as well as artifacts of other packages are ignored.\n{{% /warning %}}\n\n#### Options\n\n* `--repository (-r)`: The repository to register the package to (default: `pypi`).\nShould match a repository name set by the [`config`](#config) command.\n* `--username (-u)`: The username to access the repository.\n* `--password (-p)`: The password to access the repository.\n* `--cert`: Certificate authority to access the repository.\n* `--client-cert`: Client certificate to access the repository.\n* `--dist-dir`: Dist directory where built artifacts are stored. Default is `dist`.\n* `--build`: Build the package before publishing.\n* `--dry-run`: Perform all actions except upload the package.\n* `--skip-existing`: Ignore errors from files already existing in the repository.\n\n{{% note %}}\nSee [Configuring Credentials]({{< relref \"repositories/#configuring-credentials\" >}}) for more information on how to configure credentials.\n{{% /note %}}\n\n## python\n\nThe `python` namespace groups subcommands to manage Python versions.\n\n{{% warning %}}\nThis is an experimental feature, and can change behaviour in upcoming releases.\n{{% /warning %}}\n\n*Introduced in 2.1.0*\n\n### python install\n\nThe `python install` command installs the specified Python version from the Python Standalone Builds project.\n\n```bash\npoetry python install <PYTHON_VERSION>\n```\n\n#### Options\n\n* `--clean (-c)`: Clean up installation if check fails.\n* `--free-threaded (-t)`: Use free-threaded version if available. (Same as requesting a version with trailing \"t\".)\n* `--implementation (-i)`: Python implementation to use. (cpython, pypy)\n* `--reinstall (-r)`: Reinstall if installation already exists.\n\n### python list\n\nThe `python list` command shows Python versions available in the environment. This includes both installed and\ndiscovered System managed and Poetry managed installations.\n\n```bash\npoetry python list\n```\n#### Options\n* `--all (-a)`: List all versions, including those available for download.\n* `--free-threaded (-t)`: List only free-threaded Python versions.\n* `--implementation (-i)`: Python implementation to search for.\n* `--managed (-m)`: List only Poetry managed Python versions.\n\n### python remove\n\nThe `python remove` command removes the specified Python version if managed by Poetry.\n\n```bash\npoetry python remove <PYTHON_VERSION>\n```\n\n#### Options\n\n* `--free-threaded (-t)`: Remove free-threaded version (Same as requesting a version with trailing \"t\".)\n* `--implementation (-i)`: Python implementation to remove. (cpython, pypy)\n\n## remove\n\nThe `remove` command removes a package from the current\nlist of installed packages.\n\n```bash\npoetry remove pendulum\n```\n\nIf you want to remove a package from a specific group of dependencies, you can use the `--group (-G)` option:\n\n```bash\npoetry remove mkdocs --group docs\n```\n\nSee [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) for more information\nabout dependency groups.\n\n#### Options\n\n* `--group (-G)`: The group to remove the dependency from.\n* `--dev (-D)`: Removes a package from the development dependencies. (shortcut for `-G dev`)\n* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables `--verbose`).\n* `--lock`: Do not perform operations (only update the lockfile).\n\n## run\n\nThe `run` command executes the given command inside the project's virtualenv.\n\n```bash\npoetry run python -V\n```\n\nIt can also execute one of the scripts defined in `pyproject.toml`.\n\nSo, if you have a script defined like this:\n\n{{< tabs tabTotal=\"2\" tabID1=\"script-project\" tabID2=script-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"script-project\" >}}\n```toml\n[project]\n# ...\n[project.scripts]\nmy-script = \"my_module:main\"\n```\n{{< /tab >}}\n\n{{< tab tabID=\"script-poetry\" >}}\n```toml\n[tool.poetry.scripts]\nmy-script = \"my_module:main\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nYou can execute it like so:\n\n```bash\npoetry run my-script\n```\n\nNote that this command has no option.\n\n## search\n\nThis command searches for packages on a remote index.\n\n```bash\npoetry search requests pendulum\n```\n\n{{% note %}}\nPyPI no longer allows for the search of packages without a browser. Please use https://pypi.org/search\n(via a browser) instead.\n\nFor more information, please see [warehouse documentation](https://warehouse.pypa.io/api-reference/xml-rpc.html#deprecated-methods)\nand this [discussion](https://discuss.python.org/t/fastly-interfering-with-pypi-search/73597/6).\n{{% /note %}}\n\n## self\n\nThe `self` namespace groups subcommands to manage the Poetry installation itself.\n\n{{% note %}}\nUse of these commands will create the required `pyproject.toml` and `poetry.lock` files in your\n[configuration directory]({{< relref \"configuration\" >}}).\n{{% /note %}}\n\n{{% warning %}}\nEspecially on Windows, `self` commands that update or remove packages may be problematic\nso that other methods for installing plugins and updating Poetry are recommended.\nSee [Using plugins]({{< relref \"plugins#using-plugins\" >}}) and\n[Installing Poetry]({{< relref \"docs#installation\" >}}) for more information.\n{{% /warning %}}\n\n### self add\n\nThe `self add` command installs Poetry plugins and make them available at runtime. Additionally, it can\nalso be used to upgrade Poetry's own dependencies or inject additional packages into the runtime\nenvironment\n\n{{% note %}}\nThe `self add` command works exactly like the [`add` command](#add). However, is different in that the packages\nmanaged are for Poetry's runtime environment.\n\nThe package specification formats supported by the `self add` command are the same as the ones supported\nby the [`add` command](#add).\n{{% /note %}}\n\nFor example, to install the `poetry-plugin-export` plugin, you can run:\n\n```bash\npoetry self add poetry-plugin-export\n```\n\nTo update to the latest `poetry-core` version, you can run:\n\n```bash\npoetry self add poetry-core@latest\n```\n\nTo add a keyring provider `artifacts-keyring`, you can run:\n\n```bash\npoetry self add artifacts-keyring\n```\n\n#### Options\n\n* `--editable (-e)`: Add vcs/path dependencies as editable.\n* `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed)\n* `--allow-prereleases`: Accept prereleases.\n* `--source`: Name of the source to use to install the package.\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n\n### self install\n\nThe `self install` command ensures all additional packages specified are installed in the current\nruntime environment.\n\n{{% note %}}\nThe `self install` command works similar to the [`install` command](#install). However,\nit is different in that the packages managed are for Poetry's runtime environment.\n{{% /note %}}\n\n```bash\npoetry self install\n```\n\n#### Options\n\n* `--sync`: Synchronize the environment with the locked packages and the specified groups. (**Deprecated**, use `poetry self sync` instead)\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n\n### self lock\n\nThe `self lock` command reads this Poetry installation's system `pyproject.toml` file. The system\ndependencies are locked in the corresponding `poetry.lock` file.\n\n```bash\npoetry self lock\n```\n\n#### Options\n\n* `--regenerate`: Ignore existing lock file and overwrite it with a new lock file created from scratch.\n\n### self remove\n\nThe `self remove` command removes an installed addon package.\n\n```bash\npoetry self remove poetry-plugin-export\n```\n\n#### Options\n\n* `--dry-run`: Outputs the operations but will not execute anything (implicitly enables `--verbose`).\n\n### self show\n\nThe `self show` command behaves similar to the show command, but\nworking within Poetry's runtime environment. This lists all packages installed within\nthe Poetry install environment.\n\nTo show only additional packages that have been added via self add and their\ndependencies use `self show --addons`.\n\n```bash\npoetry self show\n```\n\n#### Options\n\n* `--addons`: List only add-on packages installed.\n* `--tree`: List the dependencies as a tree.\n* `--latest (-l)`: Show the latest version.\n* `--outdated (-o)`: Show the latest version but only for packages that are outdated.\n* `--format (-f)`: Specify the output format (`json` or `text`). Default is `text`. `json` cannot be combined with the `--tree` option.\n\n### self show plugins\n\nThe `self show plugins` command lists all the currently installed plugins.\n\n```bash\npoetry self show plugins\n```\n\n### self sync\n\nThe `self sync` command ensures all additional (and no other) packages specified\nare installed in the current runtime environment.\n\n{{% note %}}\nThe `self sync` command works similar to the [`sync` command](#sync). However,\nit is different in that the packages managed are for Poetry's runtime environment.\n{{% /note %}}\n\n```bash\npoetry self sync\n```\n\n#### Options\n\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n\n### self update\n\nThe `self update` command updates Poetry version in its current runtime environment.\n\n{{% note %}}\nThe `self update` command works exactly like the [`update` command](#update). However,\nis different in that the packages managed are for Poetry's runtime environment.\n{{% /note %}}\n\n```bash\npoetry self update\n```\n\n#### Options\n\n* `--preview`: Allow the installation of pre-release versions.\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n\n## shell\n\nThe `shell` command was moved to a plugin: [`poetry-plugin-shell`](https://github.com/python-poetry/poetry-plugin-shell)\n\n## show\n\nTo list all the available packages, you can use the `show` command.\n\n```bash\npoetry show\n```\n\nIf you want to see the details of a certain package, you can pass the package name.\n\n```bash\npoetry show pendulum\n\nname        : pendulum\nversion     : 1.4.2\ndescription : Python datetimes made easy\n\ndependencies\n - python-dateutil >=2.6.1\n - tzlocal >=1.4\n - pytzdata >=2017.2.2\n\nrequired by\n - calendar requires >=1.4.0\n```\n\n#### Options\n\n* `--without`: The dependency groups to ignore.\n* `--why`: When showing the full list, or a `--tree` for a single package, display whether they are a direct dependency or required by other packages.\n* `--with`: The optional dependency groups to include.\n* `--only`: The only dependency groups to include.\n* `--tree`: List the dependencies as a tree.\n* `--latest (-l)`: Show the latest version.\n* `--outdated (-o)`: Show the latest version but only for packages that are outdated.\n* `--all (-a)`: Show all packages (even those not compatible with current system).\n* `--top-level (-T)`: Only show explicitly defined packages.\n* `--no-truncate`: Do not truncate the output based on the terminal width.\n* `--format (-f)`: Specify the output format (`json` or `text`). Default is `text`. `json` cannot be combined with the `--tree` option.\n\n{{% note %}}\nWhen `--only` is specified, `--with` and `--without` options are ignored.\n{{% /note %}}\n\n## source\n\nThe `source` namespace groups subcommands to manage repository sources for a Poetry project.\n\n### source add\n\nThe `source add` command adds source configuration to the project.\n\nFor example, to add the `pypi-test` source, you can run:\n\n```bash\npoetry source add --priority supplemental pypi-test https://test.pypi.org/simple/\n```\n\nYou cannot use the name `pypi` for a custom repository as it is reserved for use by\nthe default PyPI source. However, you can set the priority of PyPI:\n\n```bash\npoetry source add --priority=explicit pypi\n```\n\n#### Options\n\n* `--priority`: Set the priority of this source. Accepted values are: [`primary`]({{< relref \"repositories#primary-package-sources\" >}}), [`supplemental`]({{< relref \"repositories#supplemental-package-sources\" >}}), and [`explicit`]({{< relref \"repositories#explicit-package-sources\" >}}). Refer to the dedicated sections in [Repositories]({{< relref \"repositories\" >}}) for more information.\n\n### source show\n\nThe `source show` command displays information on all configured sources for the project.\n\n```bash\npoetry source show\n```\n\nOptionally, you can show information of one or more sources by specifying their names.\n\n```bash\npoetry source show pypi-test\n```\n\n{{% note %}}\nThis command will only show sources configured via the `pyproject.toml`\nand does not include the implicit default PyPI.\n{{% /note %}}\n\n### source remove\n\nThe `source remove` command removes a configured source from your `pyproject.toml`.\n\n```bash\npoetry source remove pypi-test\n```\n\n## sync\n\nThe `sync` command makes sure that the project's environment is in sync with the `poetry.lock` file.\nIt is similar to `poetry install` but it additionally removes packages that are not tracked in the lock file.\n\n```bash\npoetry sync\n```\n\nIf there is a `poetry.lock` file in the current directory,\nit will use the exact versions from there instead of resolving them.\nThis ensures that everyone using the library will get the same versions of the dependencies.\n\nIf there is no `poetry.lock` file, Poetry will create one after dependency resolution.\n\nIf you want to exclude one or more dependency groups for the installation, you can use\nthe `--without` option.\n\n```bash\npoetry sync --without test,docs\n```\n\nYou can also select optional dependency groups with the `--with` option.\n\n```bash\npoetry sync --with test,docs\n```\n\nTo install all dependency groups including the optional groups, use the ``--all-groups`` flag.\n\n```bash\npoetry sync --all-groups\n```\n\nIt's also possible to only install specific dependency groups by using the `only` option.\n\n```bash\npoetry sync --only test,docs\n```\n\nTo only install the project itself with no dependencies, use the `--only-root` flag.\n\n```bash\npoetry sync --only-root\n```\n\nSee [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) for more information\nabout dependency groups.\n\nYou can also specify the extras you want installed\nby passing the `-E|--extras` option (See [Extras]({{< relref \"pyproject#extras\" >}}) for more info).\nPass `--all-extras` to install all defined extras for a project.\n\n```bash\npoetry sync --extras \"mysql pgsql\"\npoetry sync -E mysql -E pgsql\npoetry sync --all-extras\n```\n\nAny extras not specified will always be removed.\n\n```bash\npoetry sync --extras \"A B\"  # C is removed\n```\n\nBy default `poetry` will install your project's package every time you run `sync`:\n\n```bash\n$ poetry sync\nInstalling dependencies from lock file\n\nNo dependencies to install or update\n\n  - Installing <your-package-name> (x.x.x)\n```\n\nIf you want to skip this installation, use the `--no-root` option.\n\n```bash\npoetry sync --no-root\n```\n\nSimilar to `--no-root` you can use `--no-directory` to skip directory path dependencies:\n\n```bash\npoetry sync --no-directory\n```\n\nThis is mainly useful for caching in CI or when building Docker images. See the [FAQ entry]({{< relref \"faq#poetry-busts-my-docker-cache-because-it-requires-me-to-copy-my-source-files-in-before-installing-3rd-party-dependencies\" >}}) for more information on this option.\n\nBy default `poetry` does not compile Python source files to bytecode during installation.\nThis speeds up the installation process, but the first execution may take a little more\ntime because Python then compiles source files to bytecode automatically.\nIf you want to compile source files to bytecode during installation,\nyou can use the `--compile` option:\n\n```bash\npoetry sync --compile\n```\n\n#### Options\n\n* `--without`: The dependency groups to ignore.\n* `--with`: The optional dependency groups to include.\n* `--only`: The only dependency groups to include.\n* `--only-root`: Install only the root project, exclude all dependencies.\n* `--no-root`: Do not install the root package (your project).\n* `--no-directory`: Skip all directory path dependencies (including transitive ones).\n* `--dry-run`: Output the operations but do not execute anything (implicitly enables `--verbose`).\n* `--extras (-E)`: Features to install (multiple values allowed).\n* `--all-extras`: Install all extra features (conflicts with `--extras`).\n* `--all-groups`: Install dependencies from all groups (conflicts with `--only`, `--with`, and `--without`).\n* `--compile`: Compile Python source files to bytecode.\n\n{{% note %}}\nWhen `--only` is specified, `--with` and `--without` options are ignored.\n{{% /note %}}\n\n## update\n\nIn order to get the latest versions of the dependencies and to update the `poetry.lock` file,\nyou should use the `update` command.\n\n```bash\npoetry update\n```\n\nThis will resolve all dependencies of the project, write the exact versions into `poetry.lock`,\nand install them into your environment.\n\n{{% note %}}\nThe `update` command **does not** modify your `pyproject.toml` file. It only updates the\n`poetry.lock` file with the latest compatible versions based on the constraints already\ndefined in `pyproject.toml`. To change version constraints, use the `add` command instead.\n{{% /note %}}\n\nIf you just want to update a few packages and not all, you can list them as such:\n\n```bash\npoetry update requests toml\n```\n\nNote that this will not update versions for dependencies outside their\n[version constraints]({{< relref \"dependency-specification#version-constraints\" >}})\nspecified in the `pyproject.toml` file.\nIn other terms, `poetry update foo` will be a no-op if the version constraint\nspecified for `foo` is `~2.3` or `2.3` and `2.4` is available.\nIn order for `foo` to be updated, you must update the constraint, for example `^2.3`.\nYou can do this using the `add` command.\n\n#### Options\n\n* `--without`: The dependency groups to ignore.\n* `--with`: The optional dependency groups to include.\n* `--only`: The only dependency groups to include.\n* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables `--verbose`).\n* `--lock` : Do not perform install (only update the lockfile).\n* `--sync`: Synchronize the environment with the locked packages and the specified groups.\n\n{{% note %}}\nWhen `--only` is specified, `--with` and `--without` options are ignored.\n{{% /note %}}\n\n## version\n\nThis command shows the current version of the project or bumps the version of\nthe project and writes the new version back to `pyproject.toml` if a valid\nbump rule is provided.\n\nThe new version should be a valid [PEP 440](https://peps.python.org/pep-0440/)\nstring or a valid bump rule: `patch`, `minor`, `major`, `prepatch`, `preminor`,\n`premajor`, `prerelease`.\n\n{{% note %}}\n\nIf you would like to use semantic versioning for your project, please see\n[here]({{< relref \"libraries#versioning\" >}}).\n\n{{% /note %}}\n\nThe table below illustrates the effect of these rules with concrete examples.\n\n| rule       | before  | after   |\n| ---------- |---------|---------|\n| major      | 1.3.0   | 2.0.0   |\n| minor      | 2.1.4   | 2.2.0   |\n| patch      | 4.1.1   | 4.1.2   |\n| premajor   | 1.0.2   | 2.0.0a0 |\n| preminor   | 1.0.2   | 1.1.0a0 |\n| prepatch   | 1.0.2   | 1.0.3a0 |\n| prerelease | 1.0.2   | 1.0.3a0 |\n| prerelease | 1.0.3a0 | 1.0.3a1 |\n| prerelease | 1.0.3b0 | 1.0.3b1 |\n\nThe option `--next-phase` allows the increment of prerelease phase versions.\n\n| rule                    | before   | after    |\n|-------------------------|----------|----------|\n| prerelease --next-phase | 1.0.3a0  | 1.0.3b0  |\n| prerelease --next-phase | 1.0.3b0  | 1.0.3rc0 |\n| prerelease --next-phase | 1.0.3rc0 | 1.0.3    |\n\n#### Options\n\n* `--next-phase`: Increment the phase of the current version.\n* `--short (-s)`: Output the version number only.\n* `--dry-run`: Do not update pyproject.toml file.\n"
  },
  {
    "path": "docs/community.md",
    "content": "---\ntitle: \"Community\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 105\n---\n\n# Community\n\n## Badge\n\nFor any projects using Poetry, you may add its official badge somewhere prominent like the README.\n\n[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)\n\n**Markdown**\n```md\n[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)\n```\n\n**reStructuredText**\n```rst\n.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json\n   :alt: Poetry\n   :target: https://python-poetry.org/\n```\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "---\ntitle: \"Configuration\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 40\n---\n\n# Configuration\n\nPoetry can be configured via the `config` command ([see more about its usage here]({{< relref \"cli#config\" >}} \"config command documentation\"))\nor directly in the `config.toml` file that will be automatically created when you first run that command.\nThis file can typically be found in one of the following directories:\n\n- macOS:   `~/Library/Application Support/pypoetry`\n- Windows: `%APPDATA%\\pypoetry`\n\nFor Unix, we follow the XDG spec and support `$XDG_CONFIG_HOME`.\nThat means, by default `~/.config/pypoetry`.\n\n## Local configuration\n\nPoetry also provides the ability to have settings that are specific to a project\nby passing the `--local` option to the `config` command.\n\n```bash\npoetry config virtualenvs.create false --local\n```\n\n{{% note %}}\nYour local configuration of Poetry application is stored in the `poetry.toml` file,\nwhich is separate from `pyproject.toml`.\n{{% /note %}}\n\n{{% note %}}\nIf a setting is defined in both `poetry.toml` (local/project) and `config.toml` (global),\nthe local/project configuration takes precedence over the global configuration.\n{{% /note %}}\n\n{{% warning %}}\nBe mindful when checking in this file into your repository since it may contain user-specific or sensitive information.\n{{% /warning %}}\n\n## Listing the current configuration\n\nTo list the current configuration you can use the `--list` option\nof the `config` command:\n\n```bash\npoetry config --list\n```\n\nwhich will give you something similar to this:\n\n```toml\ncache-dir = \"/path/to/cache/directory\"\nvirtualenvs.create = true\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = true\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = \"{cache-dir}/virtualenvs\"  # /path/to/cache/directory/virtualenvs\nvirtualenvs.prompt = \"{project_name}-py{python_version}\"\nvirtualenvs.use-poetry-python = false\n```\n\n## Displaying a single configuration setting\n\nIf you want to see the value of a specific setting, you can\ngive its name to the `config` command\n\n```bash\npoetry config virtualenvs.path\n```\n\nFor a full list of the supported settings see [Available settings](#available-settings).\n\n## Adding or updating a configuration setting\n\nTo change or otherwise add a new configuration setting, you can pass\na value after the setting's name:\n\n```bash\npoetry config virtualenvs.path /path/to/cache/directory/virtualenvs\n```\n\nFor a full list of the supported settings see [Available settings](#available-settings).\n\n## Removing a specific setting\n\nIf you want to remove a previously set setting, you can use the `--unset` option:\n\n```bash\npoetry config virtualenvs.path --unset\n```\n\nThe setting will then retrieve its default value.\n\n## Using environment variables\n\nSometimes, in particular when using Poetry with CI tools, it's easier\nto use environment variables and not have to execute configuration commands.\n\nPoetry supports this and any setting can be set by using environment variables.\n\nThe environment variables must be prefixed by `POETRY_` and are comprised of the uppercase\nname of the setting and with dots and dashes replaced by underscore, here is an example:\n\n```bash\nexport POETRY_VIRTUALENVS_PATH=/path/to/virtualenvs/directory\n```\n\nThis also works for secret settings, like credentials:\n\n```bash\nexport POETRY_HTTP_BASIC_MY_REPOSITORY_PASSWORD=secret\n```\n\n## Migrate outdated configs\n\nIf Poetry renames or remove config options it might be necessary to migrate explicit set options. This is possible\nby running:\n\n```bash\npoetry config --migrate\n```\n\nIf you need to migrate a local config run:\n\n```bash\npoetry config --migrate --local\n```\n\n## Default Directories\n\nPoetry uses the following default directories:\n\n### Config Directory\n\n- Linux: `$XDG_CONFIG_HOME/pypoetry` or `~/.config/pypoetry`\n- Windows: `%APPDATA%\\pypoetry`\n- macOS: `~/Library/Application Support/pypoetry`\n\nYou can override the config directory by setting the `POETRY_CONFIG_DIR` environment variable.\n\n### Data Directory\n\n- Linux: `$XDG_DATA_HOME/pypoetry` or `~/.local/share/pypoetry`\n- Windows: `%APPDATA%\\pypoetry`\n- macOS: `~/Library/Application Support/pypoetry`\n\nYou can override the data directory by setting the `POETRY_DATA_DIR` or `POETRY_HOME` environment variables. If `POETRY_HOME` is set, it will be given higher priority.\n\n### Cache Directory\n\n- Linux: `$XDG_CACHE_HOME/pypoetry` or `~/.cache/pypoetry`\n- Windows: `%LOCALAPPDATA%\\pypoetry`\n- macOS: `~/Library/Caches/pypoetry`\n\nYou can override the cache directory by setting the `POETRY_CACHE_DIR` environment variable.\n\n## Available settings\n\n### `cache-dir`\n\n**Type**: `string`\n\n**Environment Variable**: `POETRY_CACHE_DIR`\n\nThe path to the cache directory used by Poetry.\n\nDefaults to one of the following directories:\n\n- macOS:   `~/Library/Caches/pypoetry`\n- Windows: `C:\\Users\\<username>\\AppData\\Local\\pypoetry\\Cache`\n- Unix:    `~/.cache/pypoetry`\n\n### `data-dir`\n\n**Type**: `string`\n\n**Environment Variable**: `POETRY_DATA_DIR`\n\nThe path to the data directory used by Poetry.\n\n- Linux: `$XDG_DATA_HOME/pypoetry` or `~/.local/share/pypoetry`\n- Windows: `%APPDATA%\\pypoetry`\n- macOS: `~/Library/Application Support/pypoetry`\n\nYou can override the data directory by setting the `POETRY_DATA_DIR` or `POETRY_HOME` environment variables. If\n`POETRY_HOME` is set, it will be given higher priority.\n\n### `installer.max-workers`\n\n**Type**: `int`\n\n**Default**: `number_of_cores + 4`\n\n**Environment Variable**: `POETRY_INSTALLER_MAX_WORKERS`\n\n*Introduced in 1.2.0*\n\nSet the maximum number of workers while using the parallel installer.\nThe `number_of_cores` is determined by `os.cpu_count()`.\nIf this raises a `NotImplementedError` exception, `number_of_cores` is assumed to be 1.\n\nIf this configuration parameter is set to a value greater than `number_of_cores + 4`,\nthe number of maximum workers is still limited at `number_of_cores + 4`.\n\n{{% note %}}\nThis configuration is ignored when `installer.parallel` is set to `false`.\n{{% /note %}}\n\n### `installer.no-binary`\n\n**Type**: `string | boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_INSTALLER_NO_BINARY`\n\n*Introduced in 1.2.0*\n\nWhen set, this configuration allows users to disallow the use of binary distribution format for all, none or specific packages.\n\n| Configuration          | Description                                                |\n|------------------------|------------------------------------------------------------|\n| `:all:` or `true`      | Disallow binary distributions for all packages.            |\n| `:none:` or `false`    | Allow binary distributions for all packages.               |\n| `package[,package,..]` | Disallow binary distributions for specified packages only. |\n\nIf both `installer.no-binary` and `installer.only-binary` are set, explicit package names will take precedence over `:all:`.\n\n{{% note %}}\nAs with all configurations described here, this is a user specific configuration. This means that this\nis not taken into consideration when a lockfile is generated or dependencies are resolved. This is\napplied only when selecting which distribution for dependency should be installed into a Poetry managed\nenvironment.\n{{% /note %}}\n\n{{% note %}}\nFor project specific usage, it is recommended that this be configured with the `--local`.\n\n```bash\npoetry config --local installer.no-binary :all:\n```\n{{% /note %}}\n\n{{% note %}}\nFor CI or container environments using [environment variable](#using-environment-variables)\nto configure this might be useful.\n\n```bash\nexport POETRY_INSTALLER_NO_BINARY=:all:\n```\n{{% /note %}}\n\n{{% warning %}}\nUnless this is required system-wide, if configured globally, you could encounter slower install times\nacross all your projects if incorrectly set.\n{{% /warning %}}\n\n### `installer.only-binary`\n\n**Type**: `string | boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_INSTALLER_ONLY_BINARY`\n\n*Introduced in 2.0.0*\n\nWhen set, this configuration allows users to enforce the use of binary distribution format for all, none or\nspecific packages.\n\n{{% note %}}\nPlease refer to [`installer.no-binary`]({{< relref \"configuration#installerno-binary\" >}}) for information on allowed\nvalues, usage instructions and warnings.\n{{% /note %}}\n\n### `installer.parallel`\n\n**Type**: `boolean`\n\n**Default**: `true`\n\n**Environment Variable**: `POETRY_INSTALLER_PARALLEL`\n\n*Introduced in 1.1.4*\n\nUse parallel execution when using the new (`>=1.1.0`) installer.\n\n### `installer.build-config-settings.<package-name>`\n\n**Type**: `Serialised JSON with string or list of string properties`\n\n**Default**: `None`\n\n**Environment Variable**: `POETRY_INSTALLER_BUILD_CONFIG_SETTINGS_<package-name>`\n\n*Introduced in 2.1.0*\n\n{{% warning %}}\nThis is an **experimental** configuration and can be subject to changes in upcoming releases until it is considered\nstable.\n{{% /warning %}}\n\nConfigure [PEP 517 config settings](https://peps.python.org/pep-0517/#config-settings) to be passed to a package's\nbuild backend if it has to be built from a directory or vcs source; or a source distribution during installation.\n\nThis is only used when a compatible binary distribution (wheel) is not available for a package. This can be used along\nwith [`installer.no-binary`]({{< relref \"configuration#installerno-binary\" >}}) option to force a build with these\nconfigurations when a dependency of your project with the specified name is being installed.\n\n{{% note %}}\nPoetry does not offer a similar option in the `pyproject.toml` file as these are, in majority of cases, not universal\nand vary depending on the target installation environment.\n\nIf you want to use a project specific configuration it is recommended that this configuration be set locally, in your\nproject's `poetry.toml` file.\n\n```bash\npoetry config --local installer.build-config-settings.grpcio \\\n  '{\"CC\": \"gcc\", \"--global-option\": [\"--some-global-option\"], \"--build-option\": [\"--build-option1\", \"--build-option2\"]}'\n```\n\nIf you want to modify a single key, you can do, by setting the same key again.\n\n```bash\npoetry config --local installer.build-config-settings.grpcio \\\n  '{\"CC\": \"g++\"}'\n```\n\n{{% /note %}}\n\n### `requests.max-retries`\n\n**Type**: `int`\n\n**Default**: `0`\n\n**Environment Variable**: `POETRY_REQUESTS_MAX_RETRIES`\n\n*Introduced in 2.0.0*\n\nSet the maximum number of retries in an unstable network.\nThis setting has no effect if the server does not support HTTP range requests.\n\n### `installer.re-resolve`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_INSTALLER_RE_RESOLVE`\n\n*Changed default from `true` to `false` in 2.3.0*\n\n*Introduced in 2.0.0*\n\nIf the config option is _not_ set and the lock file is at least version 2.1\n(created by Poetry 2.0 or above), the installer will not re-resolve dependencies\nbut evaluate the locked markers to decide which of the locked dependencies have to\nbe installed into the target environment.\n\n### `python.installation-dir`\n\n**Type**: `string`\n\n**Default**: `{data-dir}/python`\n\n**Environment Variable**: `POETRY_PYTHON_INSTALLATION_DIR`\n\n*Introduced in 2.1.0*\n\nThe directory in which Poetry managed Python versions are installed to.\n\n### `solver.lazy-wheel`\n\n**Type**: `boolean`\n\n**Default**: `true`\n\n**Environment Variable**: `POETRY_SOLVER_LAZY_WHEEL`\n\n*Introduced in 1.8.0*\n\nDo not download entire wheels to extract metadata but use\n[HTTP range requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests)\nto only download the METADATA files of wheels.\nEspecially with slow network connections, this setting can speed up dependency resolution significantly.\nIf the cache has already been filled or the server does not support HTTP range requests,\nthis setting makes no difference.\n\n### `system-git-client`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_SYSTEM_GIT_CLIENT`\n\n*Renamed to `system-git-client` in 2.0.0*\n\n*Introduced in 1.2.0 as `experimental.system-git-client`*\n\nUse system git client backend for git related tasks.\n\nPoetry uses `dulwich` by default for git related tasks to not rely on the availability of a git client.\n\nIf you encounter any problems with it, set to `true` to use the system git backend.\n\n### `virtualenvs.create`\n\n**Type**: `boolean`\n\n**Default**: `true`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_CREATE`\n\nCreate a new virtual environment if one doesn't already exist.\n\nIf set to `false`, Poetry will not create a new virtual environment. If it detects an already enabled virtual\nenvironment or an existing one in `{cache-dir}/virtualenvs` or `{project-dir}/.venv` it will\ninstall dependencies into them, otherwise it will install dependencies into the systems python environment.\n\n{{% note %}}\nIf Poetry detects it's running within an activated virtual environment, it will never create a new virtual environment,\nregardless of the value set for `virtualenvs.create`.\n{{% /note %}}\n\n{{% note %}}\nBe aware that installing dependencies into the system environment likely upgrade or uninstall existing packages and thus\nbreak other applications. Installing additional Python packages after installing the project might break the Poetry\nproject in return.\n\nThis is why it is recommended to always create a virtual environment. This is also true in Docker containers, as they\nmight contain additional Python packages as well.\n{{% /note %}}\n\n### `virtualenvs.in-project`\n\n**Type**: `boolean`\n\n**Default**: `None`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_IN_PROJECT`\n\nCreate the virtualenv inside the project's root directory.\n\nIf not set explicitly, `poetry` by default will create a virtual environment under\n`{cache-dir}/virtualenvs` or use the `{project-dir}/.venv` directory if one already exists.\n\nIf set to `true`, the virtualenv will be created and expected in a folder named\n`.venv` within the root directory of the project.\n\n{{% note %}}\nIf a virtual environment has already been created for the project under `{cache-dir}/virtualenvs`, setting this variable to `true` will not cause `poetry` to create or use a local virtual environment.\n\nIn order for this setting to take effect for a project already in that state, you must delete the virtual environment folder located in `{cache-dir}/virtualenvs`.\n\nYou can find out where the current project's virtual environment (if there is one) is stored\nwith the command `poetry env info --path`.\n{{% /note %}}\n\nIf set to `false`, `poetry` will ignore any existing `.venv` directory.\n\n### `virtualenvs.options.always-copy`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_ALWAYS_COPY`\n\n*Introduced in 1.2.0*\n\nIf set to `true` the `--always-copy` parameter is passed to `virtualenv` on creation of the virtual environment, so that\nall needed files are copied into it instead of symlinked.\n\n### `virtualenvs.options.no-pip`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_NO_PIP`\n\n*Introduced in 1.2.0*\n\nIf set to `true` the `--no-pip` parameter is passed to `virtualenv` on creation of the virtual environment. This means\nwhen a new virtual environment is created, `pip` will not be installed in the environment.\n\n{{% note %}}\nPoetry, for its internal operations, uses the `pip` wheel embedded in the `virtualenv` package installed as a dependency\nin Poetry's runtime environment. If a user runs `poetry run pip` when this option is set to `true`, the `pip` the\nembedded instance of `pip` is used.\n\nYou can safely set this to `true`, if you desire a virtual environment with no additional packages.\nThis is desirable for production environments.\n{{% /note %}}\n\n### `virtualenvs.options.system-site-packages`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_OPTIONS_SYSTEM_SITE_PACKAGES`\n\nGive the virtual environment access to the system site-packages directory.\nApplies on virtualenv creation.\n\n### `virtualenvs.path`\n\n**Type**: `string`\n\n**Default**: `{cache-dir}/virtualenvs`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_PATH`\n\nDirectory where virtual environments will be created.\n\n{{% note %}}\nThis setting controls the global virtual environment storage path. It most likely will not be useful at the local level. To store virtual environments in the project root, see `virtualenvs.in-project`.\n{{% /note %}}\n\n### `virtualenvs.prompt`\n\n**Type**: `string`\n\n**Default**: `{project_name}-py{python_version}`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_PROMPT`\n\n*Introduced in 1.2.0*\n\nFormat string defining the prompt to be displayed when the virtual environment is activated.\nThe variables `project_name` and `python_version` are available for formatting.\n\n### `virtualenvs.use-poetry-python`\n\n**Type**: `boolean`\n\n**Default**: `false`\n\n**Environment Variable**: `POETRY_VIRTUALENVS_USE_POETRY_PYTHON`\n\n*Introduced in 2.0.0*\n\nBy default, Poetry will use the activated Python version to create a new virtual environment.\nIf set to `true`, the Python version used during Poetry installation is used.\n\n### `repositories.<name>.url`\n\n**Type**: `string`\n\n**Environment Variable**: `POETRY_REPOSITORIES_<NAME>_URL`\n\nSet the repository URL for `<name>`.\n\nSee [Publishable Repositories]({{< relref \"repositories#publishable-repositories\" >}}) for more information.\n\n### `http-basic.<name>.[username|password]`\n\n**Type**: `string`\n\n**Environment Variables**: `POETRY_HTTP_BASIC_<NAME>_USERNAME`, `POETRY_HTTP_BASIC_<NAME>_PASSWORD`\n\nSet repository credentials (`username` and `password`) for `<name>`.\nSee [Repositories - Configuring credentials]({{< relref \"repositories#configuring-credentials\" >}})\nfor more information.\n\n### `pypi-token.<name>`\n\n**Type**: `string`\n\n**Environment Variable**: `POETRY_PYPI_TOKEN_<NAME>`\n\nSet repository credentials (using an API token) for `<name>`.\nSee [Repositories - Configuring credentials]({{< relref \"repositories#configuring-credentials\" >}})\nfor more information.\n\n### `certificates.<name>.cert`\n\n**Type**: `string | boolean`\n\n**Environment Variable**: `POETRY_CERTIFICATES_<NAME>_CERT`\n\nSet custom certificate authority for repository `<name>`.\nSee [Repositories - Configuring credentials - Custom certificate authority]({{< relref \"repositories#custom-certificate-authority-and-mutual-tls-authentication\" >}})\nfor more information.\n\nThis configuration can be set to `false`, if TLS certificate verification should be skipped for this\nrepository.\n\n### `certificates.<name>.client-cert`\n\n**Type**: `string`\n\n**Environment Variable**: `POETRY_CERTIFICATES_<NAME>_CLIENT_CERT`\n\nSet client certificate for repository `<name>`.\nSee [Repositories - Configuring credentials - Custom certificate authority]({{< relref \"repositories#custom-certificate-authority-and-mutual-tls-authentication\" >}})\nfor more information.\n\n### `keyring.enabled`\n\n**Type**: `boolean`\n\n**Default**: `true`\n\n**Environment Variable**: `POETRY_KEYRING_ENABLED`\n\nEnable the system keyring for storing credentials.\nSee [Repositories - Configuring credentials]({{< relref \"repositories#configuring-credentials\" >}})\nfor more information.\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "---\ntitle: \"Contributing to Poetry\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 100\n\nnote: \"Are you viewing this document on GitHub? For the best experience, view it on the website https://python-poetry.org/docs/contributing.\"\n---\n\n# Contributing to Poetry\n\nFirst off, thanks for taking the time to contribute!\n\nThe following is a set of guidelines for contributing to Poetry on GitHub. These are mostly guidelines, not rules. Use\nyour best judgement, and feel free to propose changes to this document in a pull request.\n\n## How to contribute\n\n### Reporting bugs\n\nThis section guides you through submitting a bug report for Poetry.\nFollowing these guidelines helps maintainers and the community understands your report, reproduces the behavior, and finds\nrelated reports.\n\n#### Before submitting a bug report\n\n* **Check the [FAQ]** for a list of common questions and problems.\n* **Check the [blog]** for release notes from recent releases, including steps for upgrading and known issues.\n* **Check that your issue does not already exist** in the [issue tracker].\n* **Make sure your issue is really a bug, and is not a support request or question** better suited for [Discussions]\nor [Discord].\n* **Try running your commands with the** `--no-cache` **flag**.\n* **Try clearing your cache with** `poetry cache clear --all PyPI` **and rerunning your commands**.\n\n{{% note %}}\nIf you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and\ninclude a link to the original issue in the body of your new one.\n{{% /note %}}\n\n#### How do I submit a bug report?\n\nBugs concerning Poetry and poetry-core should be submitted to the main [issue tracker], using the correct\n[issue template].\n\nExplain the problem and make it easy for others to search for and understand:\n\n* **Use a clear and descriptive title** for the issue to identify the problem.\n* **Describe the exact steps which reproduce the problem** in as many details as possible.\n* **Describe the behavior you observed after following the steps** and point out how this is a bug.\n* **Explain which behavior you expected to see instead and why.**\n* **If the problem involves an unexpected error being raised**, execute the problematic command in **debug** mode\n(with `-vvv` flag).\n\nProvide detailed steps for reproduction of your issue:\n\n* **Provide your pyproject.toml file** in a [Gist](https://gist.github.com), pastebin, or example repository after\nremoving potential private information like private package repositories or names.\n* **Provide specific examples to demonstrate the steps to reproduce the issue**. This could be an example repository, a\nsequence of steps run in a container, or just a pyproject.toml for very simple cases.\n* **Are you unable to reliably reproduce the issue?** If so, provide details about how often the problem happens\nand under which conditions it normally happens.\n\nProvide more context by answering these questions:\n\n* **Did the problem start happening recently** (e.g., after updating to a new version of Poetry) or was this always a\nproblem?\n* If the problem started happening recently, **can you reproduce the problem in an older version of Poetry?** What's the\nmost recent version in which the problem does not happen?\n* **Is there anything exotic or unusual about your environment?** This could include use of special container images,\nnewer CPU architectures like Apple Silicon, or corporate proxies that intercept or modify your network traffic.\n\nInclude details about your configuration and environment:\n\n* **Which version of Poetry are you using?** You can get the exact version by running `poetry --version`.\n* **What version of Python is being used to run Poetry?** Execute the `poetry debug info` to get this information.\n* **What's the name and version of the OS you're using?** Examples include Ubuntu 22.04 or macOS 12.6.\n\nTo give others the best chance to understand and reproduce your issue, please be sure to put extra effort into your\nreproduction steps. You can both rule out local configuration issues on your end, and ensure others can cleanly\nreproduce your issue if attempt all reproductions in a pristine container (or VM), and provide the steps you performed\ninside that container/VM in your issue report.\n\n### Suggesting enhancements\n\nThis section guides you through submitting an enhancement suggestion for Poetry, including completely new features as\nwell as improvements to existing functionality. Following these guidelines helps maintainers and the community\nunderstand your suggestion and find related suggestions.\n\n#### Before submitting a suggested enhancement\n\n* **Check the [FAQ]** for a list of common questions and problems.\n* **Check that your issue does not already exist** in the [issue tracker].\n\n#### How do I submit a suggested enhancement?\n\nSuggested enhancements concerning Poetry and poetry-core should be submitted to the main [issue tracker], using the\ncorrect [issue template].\n\n* **Use a clear and descriptive title** for the issue to identify the suggestion.\n* **Provide a detailed description of the proposed enhancement**, with specific steps or examples when possible.\n* **Describe the current behavior** and **explain which behavior you would like to see instead**, and why.\n\n### Documentation contributions\n\nOne of the simplest ways to get started contributing to a project is through improving documentation. Poetry is\nconstantly evolving, and this means that sometimes our documentation has gaps. You can help by adding missing sections,\nediting the existing content to be more accessible, or creating new content such as tutorials, FAQs, etc.\n\n{{% note %}}\nGitHub [Discussions](https://github.com/python-poetry/poetry/discussions) and the\n[kind/question label](https://github.com/python-poetry/poetry/labels/kind/question) are excellent sources for FAQ\ncandidates.\n{{% /note %}}\n\nIssues pertaining to the documentation are usually marked with the [area/docs label], which will also trigger a preview\nof the changes as rendered by this website.\n\n### Code contributions\n\n#### Picking an issue\n\n{{% note %}}\nIf you are a first time contributor, and are looking for an issue to take on, you might want to look for\nat the [contributing page](https://github.com/python-poetry/poetry/contribute) for candidates. We do our best to curate\ngood issues for first-time contributors there, but do fall behind -- so if you don't see anything good, feel free to\nask.\n{{% /note %}}\n\nIf you would like to take on an issue, feel free to comment on the issue tagging `@python-poetry/triage`.\nWe are more than happy to discuss solutions on the issue. If you would like help with navigating the code base, are\nlooking for something to work on, or want feedback on a design or change, join us on our [Discord server][Discord] or\nstart a [Discussion][Discussions].\n\n#### Local development\n\nPoetry is developed using Poetry. Refer to the [documentation] to install Poetry in your local environment.\n\n{{% note %}}\nPoetry's development toolchain requires Python 3.9 or newer.\n{{% /note %}}\n\nYou should first fork the Poetry repository and then clone it locally, so that you can make pull requests against the\nproject. If you are new to Git and pull request-based development, GitHub provides a\n[guide](https://docs.github.com/en/get-started/quickstart/contributing-to-projects) you will find helpful.\n\nNext, you should install Poetry's dependencies, and run the test suite to make sure everything is working as expected:\n\n```bash\npoetry install\npoetry run pytest\n```\n\n{{% note %}}\nIf you want to see the coverage stats after the tests are complete, use:\n\n```bash\npoetry run pytest --cov=src/poetry --cov-report term\n```\n{{% /note %}}\n\nWhen you contribute to Poetry, automated tools will be run to make sure your code is suitable to be merged. Besides\npytest, you will need to make sure your code typechecks properly using [mypy](http://mypy-lang.org/):\n\n```bash\npoetry run mypy\n```\n\nFinally, a great deal of linting tools are run on your code, to try and ensure consistent code style and root out common\nmistakes. The [pre-commit](https://pre-commit.com/) tool is used to install and run these tools, and requires one-time\nsetup:\n\n```bash\npoetry run pre-commit install\n```\n\npre-commit will now run and check your code every time you make a commit. By default, it will only run on changed files,\nbut you can run it on all files manually (this may be useful if you altered the pre-commit config):\n\n```bash\npoetry run pre-commit run --all-files\n```\n\n#### Pull requests\n\n* Fill out the pull request body completely and describe your changes as accurately as possible. The pull request body\nshould be kept up to date as it will usually form the base for the final merge commit and the changelog entry.\n* Be sure that your pull request contains tests that cover the changed or added code. Tests are generally required for\ncode be to be considered mergeable, and code without passing tests will not be merged.\n* Ensure your pull request passes the mypy and pre-commit checks. Remember that you can run these tools locally\ninstead of relying on remote CI.\n* If your changes warrant a documentation change, the pull request must also update the documentation. Make sure to\nreview the documentation preview generated by CI for any rendering issues.\n\n{{% note %}}\nMake sure your branch is [rebased](https://docs.github.com/en/get-started/using-git/about-git-rebase) against the latest\nbase branch. A maintainer might ask you to ensure the branch is up-to-date prior to merging your pull request\n(especially if there have been CI changes on the base branch), and will also ask you to fix any conflicts.\n{{% /note %}}\n\nAll pull requests, unless otherwise instructed, need to be first accepted into the `main` branch. Maintainers will\ngenerally decide if any backports to other branches are required, and carry them out as needed.\n\n### Issue triage\n\n{{% note %}}\nIf you have an issue that hasn't had any attention, you can ping us `@python-poetry/triage` on the issue. Please give us\nreasonable time to get to your issue first, and avoid pinging any individuals directly, especially if they are not part\nof the Poetry team.\n{{% /note %}}\n\nIf you are helping with the triage of reported issues, this section provides some useful information to assist you in\nyour contribution.\n\n#### Triage steps\n\n1. Determine what area and versions of Poetry the issue is related to, and set the appropriate labels (e.g.\n`version/x.x.x`, `area/docs`, `area/venv`), and remove the `status/triage` label.\n2. If requested information (such as debug logs, pyproject.toml, etc.) is not provided and is relevant, request it from\nthe author.\n   1. Set the `status/waiting-on-response` label while waiting to hear back from the author.\n3. Attempt to reproduce the issue with the reported Poetry version or request further clarification from the author.\n4. Ensure the issue is not already resolved. Try reproducing it on the latest stable release, the latest prerelease (if\npresent), and the development branch.\n5. If the issue cannot be reproduced,\n   1. request more reproduction steps and clarification from the issue's author,\n   2. set the `status/needs-reproduction` label,\n   3. close the issue if there is no reproduction forthcoming.\n6. If the issue can be reproduced,\n   1. comment on the issue confirming so,\n   2. set the `status/confirmed` label,\n   3. if possible, identify the root cause of the issue,\n   4. if interested, attempt to fix it via a pull request.\n\n#### Multiple versions\n\nWhen trying to reproduce issues, you often want to use multiple versions of Poetry at the same time.\n[pipx](https://pypa.github.io/pipx/) makes this easy to do:\n\n```sh\npipx install --suffix @1.2.1 'poetry==1.2.1'\npipx install --suffix @1.3.0rc1 'poetry==1.3.0rc1'\npipx install --suffix @main 'poetry @ git+https://github.com/python-poetry/poetry'\npipx install --suffix @local '/path/to/local/clone/of/poetry'\n\n# now you can use any of the chosen versions of Poetry with their configured suffix, e.g.\npoetry@main --version\n```\n\n{{% note %}}\nDo not forget to `pipx upgrade poetry@main` before using it, to make sure you have the latest changes.\n{{% /note %}}\n\n{{% note %}}\nThis mechanism can also be used to test pull requests by using GitHub's pull request remote refs:\n```sh\npipx install --suffix @pr1234 git+https://github.com/python-poetry/poetry.git@refs/pull/1234/head\n```\n{{% /note %}}\n\n  [Blog]: {{< ref \"/blog\" >}}\n  [Documentation]: {{< ref \"/docs\" >}}\n  [FAQ]: {{< relref \"faq\" >}}\n  [Issue Tracker]: https://github.com/python-poetry/poetry/issues\n  [area/docs label]: https://github.com/python-poetry/poetry/labels/area/docs\n  [kind/question label]: https://github.com/python-poetry/poetry/labels/kind/question\n  [Issue Template]: https://github.com/python-poetry/poetry/issues/new/choose\n  [Discussions]: https://github.com/python-poetry/poetry/discussions\n  [Discord]: https://discord.com/invite/awxPgve\n"
  },
  {
    "path": "docs/dependency-specification.md",
    "content": "---\ntitle: \"Dependency specification\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 70\n---\n\n# Dependency specification\n\nDependencies for a project can be specified in various forms, which depend on the type\nof the dependency and on the optional constraints that might be needed for it to be installed.\n\n## `project.dependencies` and `tool.poetry.dependencies`\n\nPrior Poetry 2.0, dependencies had to be declared in the `tool.poetry.dependencies`\nsection of the `pyproject.toml` file.\n\n```toml\n[tool.poetry.dependencies]\nrequests = \"^2.13.0\"\n```\n\nWith Poetry 2.0, you should consider using the `project.dependencies` section instead.\n\n```toml\n[project]\n# ...\ndependencies = [\n    \"requests (>=2.23.0,<3.0.0)\"\n]\n```\n\nWhile dependencies in `tool.poetry.dependencies` are specified using toml tables,\ndependencies in `project.dependencies` are specified as strings according\nto [PEP 508](https://peps.python.org/pep-0508/).\n\nIn many cases, `tool.poetry.dependencies` can be replaced with `project.dependencies`.\nHowever, there are some cases where you might still need to use `tool.poetry.dependencies`.\nFor example, if you want to define additional information that is not required for building\nbut only for locking (for example, an explicit source), you can enrich dependency\ninformation in the `tool.poetry` section.\n\n```toml\n[project]\n# ...\ndependencies = [\n    \"requests>=2.13.0\",\n]\n\n[tool.poetry.dependencies]\nrequests = { source = \"private-source\" }\n```\n\nWhen both are specified, `project.dependencies` are used for metadata when building the project,\n`tool.poetry.dependencies` is only used to enrich `project.dependencies` for locking.\n\nAlternatively, you can add `dependencies` to `dynamic` and define your dependencies\ncompletely in the `tool.poetry` section. Using only the `tool.poetry` section might\nmake sense in non-package mode when you will not build an sdist or a wheel.\n\n```toml\n[project]\n# ...\ndynamic = [ \"dependencies\" ]\n\n[tool.poetry.dependencies]\nrequests = { version = \">=2.13.0\", source = \"private-source\" }\n```\n\n{{% note %}}\nAnother use case for `tool.poetry.dependencies` are relative path dependencies\nsince `project.dependencies` only support absolute paths.\n{{% /note %}}\n\n{{% note %}}\nOnly main dependencies can be specified in the `project` section.\nOther [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}})\nmust still be specified in the `tool.poetry` section.\n{{% /note %}}\n\n## Version constraints\n\n### Compatible release requirements\n\n**Compatible release requirements** specify a minimal version with the ability to update to later versions of the same level.\nFor example, if you specify a major, minor, and patch version, only patch-level changes are allowed.\nIf you only specify a major, and minor version, then minor- and patch-level changes are allowed.\n\n`~=1.2.3` is an example of a compatible release requirement.\n\n| Requirement | Versions allowed |\n| ----------- | ---------------- |\n| ~=1.2.3     | >=1.2.3 <1.3.0   |\n| ~=1.2       | >=1.2.0 <2.0.0   |\n\n### Wildcard requirements\n\n**Wildcard requirements** allow for the latest (dependency-dependent) version where the wildcard is positioned.\n\n`*`, `1.*` and `1.2.*` are examples of wildcard requirements.\n\n| Requirement | Versions allowed |\n| ----------- | ---------------- |\n| *           | >=0.0.0          |\n| 1.*         | >=1.0.0 <2.0.0   |\n| 1.2.*       | >=1.2.0 <1.3.0   |\n\n### Inequality requirements\n\n**Inequality requirements** allow manually specifying a version range or an exact version to depend on.\n\nHere are some examples of inequality requirements:\n\n```text\n>= 1.2.0\n> 1\n< 2\n!= 1.2.3\n```\n\n#### Multiple requirements\n\nMultiple version requirements can also be separated with a comma, e.g. `>= 1.2, < 1.5`.\n\n### Exact requirements\n\nYou can specify the exact version of a package.\n\n`1.2.3` is an example of an exact version specification.\n\nThis will tell Poetry to install this version and this version only.\nIf other dependencies require a different version, the solver will ultimately fail and abort any installation or update procedures.\n\nExact versions can also be specified with `==` according to [PEP 440](https://peps.python.org/pep-0440/).\n\n`==1.2.3` is an example of this.\n\n### Caret requirements\n\n{{% warning %}}\nNot supported in `project.dependencies`.\n\nWhen using `poetry add` such constraints are automatically converted into an equivalent constraint.\n{{% /warning %}}\n\n**Caret requirements** allow [SemVer](https://semver.org/) compatible updates to a specified version. An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping. For instance, if we previously ran `poetry add requests@^2.13.0` and wanted to update the library and ran `poetry update requests`, poetry would update us to version `2.14.0` if it was available, but would not update us to `3.0.0`. If instead, we had specified the version string as `^0.1.13`, poetry would update to `0.1.14` but not `0.2.0`. `0.0.x` is not considered compatible with any other version.\n\nHere are some more examples of caret requirements and the versions that would be allowed with them:\n\n| Requirement | Versions allowed |\n| ----------- | ---------------- |\n| ^1.2.3      | >=1.2.3 <2.0.0   |\n| ^1.2        | >=1.2.0 <2.0.0   |\n| ^1          | >=1.0.0 <2.0.0   |\n| ^0.2.3      | >=0.2.3 <0.3.0   |\n| ^0.0.3      | >=0.0.3 <0.0.4   |\n| ^0.0        | >=0.0.0 <0.1.0   |\n| ^0          | >=0.0.0 <1.0.0   |\n\n### Tilde requirements\n\n{{% warning %}}\nNot supported in `project.dependencies`.\n\nWhen using `poetry add` such constraints are automatically converted into an equivalent constraint.\n{{% /warning %}}\n\n**Tilde requirements** specify a minimal version with some ability to update.\nIf you specify a major, minor, and patch version or only a major and minor version, only patch-level changes are allowed.\nIf you only specify a major version, then minor- and patch-level changes are allowed.\n\n`~1.2.3` is an example of a tilde requirement.\n\n| Requirement | Versions allowed |\n| ----------- | ---------------- |\n| ~1.2.3      | >=1.2.3 <1.3.0   |\n| ~1.2        | >=1.2.0 <1.3.0   |\n| ~1          | >=1.0.0 <2.0.0   |\n\n### Using the `@` operator\n\nWhen adding dependencies via `poetry add`, you can use the `@` operator.\nThis is understood similarly to the `==` syntax, but also allows prefixing any\nspecifiers that are valid in `pyproject.toml`. For example:\n\n```shell\npoetry add \"django@^4.0.0\"\n```\n\nThe above would translate to the following entry in `pyproject.toml`:\n\n{{< tabs tabTotal=\"2\" tabID1=\"at-project\" tabID2=\"at-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"at-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"django (>=4.0.0,<5.0.0)\",\n]\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"at-poetry\" >}}\n```toml\n[tool.poetry.dependencies]\ndjango = \"^4.0.0\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nThe special keyword `latest` is also understood by the `@` operator:\n```shell\npoetry add django@latest\n```\n\nThe above would translate to the following entry in `pyproject.toml`, assuming the latest release of `django` is `5.1.3`:\n\n{{< tabs tabTotal=\"2\" tabID1=\"at-latest-project\" tabID2=\"at-latest-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"at-latest-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"django (>=5.1.3,<6.0.0)\",\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"at-latest-poetry\" >}}\n```toml\n[tool.poetry.dependencies]\ndjango = \"^5.1.3\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n#### Extras\n\nExtras and `@` can be combined as one might expect (`package[extra]@version`):\n\n```shell\npoetry add django[bcrypt]@^4.0.0\n```\n\n## `git` dependencies\n\nTo depend on a library located in a `git` repository,\nthe minimum information you need to specify is the location of the repository:\n\n{{< tabs tabTotal=\"2\" tabID1=\"git-project\" tabID2=\"git-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"git-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"requests @ git+https://github.com/requests/requests.git\",\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"git-poetry\" >}}\nIn the `tool.poetry` section you use the `git` key:\n\n```toml\n[tool.poetry.dependencies]\nrequests = { git = \"https://github.com/requests/requests.git\" }\n```\n\n{{< /tab >}}\n{{< /tabs >}}\n\nSince we haven’t specified any other information,\nPoetry assumes that we intend to use the latest commit on the `main` branch\nto build our project.\n\nYou can explicitly specify which branch, commit hash or tagged ref should be used:\n\n{{< tabs tabTotal=\"2\" tabID1=\"git-rev-project\" tabID2=\"git-rev-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"git-rev-project\" >}}\nAppend the information to the git url.\n\n```toml\n[project]\n# ...\ndependencies = [\n    \"requests @ git+https://github.com/requests/requests.git@next\",\n    \"flask @ git+https://github.com/pallets/flask.git@38eb5d3b\",\n    \"numpy @ git+https://github.com/numpy/numpy.git@v0.13.2\",\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"git-rev-poetry\" >}}\nCombine the `git` key with the `branch`, `rev` or `tag` key respectively.\n\n```toml\n[tool.poetry.dependencies]\n# Get the latest revision on the branch named \"next\"\nrequests = { git = \"https://github.com/kennethreitz/requests.git\", branch = \"next\" }\n# Get a revision by its commit hash\nflask = { git = \"https://github.com/pallets/flask.git\", rev = \"38eb5d3b\" }\n# Get a revision by its tag\nnumpy = { git = \"https://github.com/numpy/numpy.git\", tag = \"v0.13.2\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nIt's possible to add a package that is located in a subdirectory of the VCS repository.\n\n{{< tabs tabTotal=\"2\" tabID1=\"git-subdir-project\" tabID2=\"git-subdir-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"git-subdir-project\" >}}\nProvide the subdirectory as a URL fragment similarly to what [pip](https://pip.pypa.io/en/stable/topics/vcs-support/#url-fragments) provides.\n```toml\n[project]\n# ...\ndependencies = [\n    \"subdir_package @ git+https://github.com/myorg/mypackage_with_subdirs.git#subdirectory=subdir\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"git-subdir-poetry\" >}}\nUse the `subdirectory` key in the `tool.poetry` section:\n\n```toml\n[tool.poetry.dependencies]\n# Install a package named `subdir_package` from a folder called `subdir` within the repository\nsubdir_package = { git = \"https://github.com/myorg/mypackage_with_subdirs.git\", subdirectory = \"subdir\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nThe corresponding `add` call looks like this:\n\n```bash\npoetry add \"git+https://github.com/myorg/mypackage_with_subdirs.git#subdirectory=subdir\"\n```\n\nTo use an SSH connection, for example, in the case of private repositories, use the following example syntax:\n\n{{< tabs tabTotal=\"2\" tabID1=\"git-ssh-project\" tabID2=\"git-ssh-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"git-ssh-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"pendulum @ git+ssh://git@github.com/sdispater/pendulum.git\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"git-ssh-poetry\" >}}\n```toml\n[tool.poetry.dependencies]\npendulum = { git = \"git@github.com/sdispater/pendulum.git\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n### Credentials for git dependencies\n\nTo use HTTP basic authentication with your git repositories, you can configure credentials similar to\nhow [repository credentials]({{< relref \"repositories#configuring-credentials\" >}}) are configured.\n\n```bash\npoetry config repositories.git-org-project https://github.com/org/project.git\npoetry config http-basic.git-org-project username token\npoetry add git+https://github.com/org/project.git\n```\n\n{{% note %}}\nThe default git client used is [Dulwich](https://www.dulwich.io/).\n\nWe fall back to legacy system git client implementation in cases where\n[gitcredentials](https://git-scm.com/docs/gitcredentials) is used. This fallback will be removed in\na future release where `gitcredentials` helpers can be better supported natively.\n\nIn cases where you encounter issues with the default implementation, you may wish to\nexplicitly configure the use of the system git client via a shell subprocess call.\n\n```bash\npoetry config system-git-client true\n```\n{{% /note %}}\n\n## `path` dependencies\n\n{{< tabs tabTotal=\"2\" tabID1=\"path-project\" tabID2=\"path-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"path-project\" >}}\nIn the `project` section, you can only use absolute paths:\n\n```toml\n[project]\n# directory\ndependencies = [\n    \"my-package @ file:///absolute/path/to/my-package\"\n]\n# file\ndependencies = [\n    \"my-package @ file:///absolute/path/to/my-package/dist/my-package-0.1.0.tar.gz\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"path-poetry\" >}}\nTo depend on a library located in a local directory or file,\nyou can use the `path` property:\n\n```toml\n[tool.poetry.dependencies]\n# directory\nmy-package = { path = \"../my-package/\", develop = true }\n\n# file\nmy-package = { path = \"../my-package/dist/my-package-0.1.0.tar.gz\" }\n```\n\nTo install directory path dependencies in editable mode, use the `develop` keyword and set it to `true`.\n{{< /tab >}}\n{{< /tabs >}}\n\n## `url` dependencies\n\n`url` dependencies are libraries located on a remote archive.\n\n{{< tabs tabTotal=\"2\" tabID1=\"url-project\" tabID2=\"url-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"url-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"my-package @ https://example.com/my-package-0.1.0.tar.gz\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"url-poetry\" >}}\nUse the `url` property.\n\n```toml\n[tool.poetry.dependencies]\n# directory\nmy-package = { url = \"https://example.com/my-package-0.1.0.tar.gz\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nThe corresponding `add` call is:\n\n```bash\npoetry add https://example.com/my-package-0.1.0.tar.gz\n```\n\n## Dependency `extras`\n\nYou can specify [PEP-508 Extras](https://www.python.org/dev/peps/pep-0508/#extras)\nfor a dependency as shown here.\n\n{{< tabs tabTotal=\"2\" tabID1=\"extras-project\" tabID2=\"extras-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"extras-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"gunicorn[gevent] (>=20.1,<21.0)\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"extras-poetry\" >}}\n```toml\n[tool.poetry.dependencies]\ngunicorn = { version = \"^20.1\", extras = [\"gevent\"] }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n{{% note %}}\nThese activate extra defined for the dependency, to configure an optional dependency\nfor extras in your project refer to [`extras`]({{< relref \"pyproject#extras\" >}}).\n{{% /note %}}\n\n## `source` dependencies\n{{% note %}}\nIt is not possible to define source dependencies in the `project` section.\n{{% /note %}}\n\nTo depend on a package from an [alternate repository]({{< relref \"repositories#installing-from-private-package-sources\" >}}),\nyou can use the `source` property:\n\n```toml\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"supplemental\"\n\n[tool.poetry.dependencies]\nmy-cool-package = { version = \"*\", source = \"foo\" }\n```\n\nwith the corresponding `add` call:\n\n```sh\npoetry add my-cool-package --source foo\n```\n\n{{% note %}}\nIn this example, we expect `foo` to be configured correctly. See [using a private repository]({{< relref \"repositories#installing-from-private-package-sources\" >}})\nfor further information.\n{{% /note %}}\n\n## Python restricted dependencies\n\nYou can also specify that a dependency should be installed only for specific Python versions:\n\n{{< tabs tabTotal=\"2\" tabID1=\"python-restriction-project\" tabID2=\"python-restriction-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"python-restriction-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"tomli (>=2.0.1,<3.0) ; python_version < '3.11'\",\n    \"pathlib2 (>=2.2,<3.0) ; python_version >= '3.9' and python_version < '4.0'\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"python-restriction-poetry\" >}}\n\n```toml\n[tool.poetry.dependencies]\ntomli = { version = \"^2.0.1\", python = \"<3.11\" }\npathlib2 = { version = \"^2.2\", python = \"^3.9\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n## Using environment markers\n\nIf you need more complex install conditions for your dependencies,\nPoetry supports [environment markers](https://www.python.org/dev/peps/pep-0508/#environment-markers):\n\n{{< tabs tabTotal=\"2\" tabID1=\"markers-project\" tabID2=\"markers-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"markers-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"pathlib2 (>=2.2,<3.0) ; python_version <= '3.4' or sys_platform == 'win32'\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"markers-poetry\" >}}\nUse the `markers` property:\n\n```toml\n[tool.poetry.dependencies]\npathlib2 = { version = \"^2.2\", markers = \"python_version <= '3.4' or sys_platform == 'win32'\" }\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n### `extra` environment marker\n\nPoetry populates the `extra` marker with each of the selected extras of the root package.\nFor example, consider the following dependency:\n```toml\n[project.optional-dependencies]\npaths = [\n    \"pathlib2 (>=2.2,<3.0) ; sys_platform == 'win32'\"\n]\n```\n\n`pathlib2` will be installed when you install your package with `--extras paths` on a `win32` machine.\n\n#### Exclusive extras\n\n{{% warning %}}\nThe first example will only work completely if you configure Poetry to not re-resolve for installation:\n\n```bash\npoetry config installer.re-resolve false\n```\n\nThis was a new feature of Poetry 2.0 and became the default behavior in Poetry 2.3.\n\n{{% /warning %}}\n\nKeep in mind that all combinations of possible extras available in your project need to be compatible with each other.\nThis means that in order to use differing or incompatible versions across different combinations, you need to make your\nextra markers *exclusive*. For example, the following installs PyTorch from one source repository with CPU versions\nwhen the `cuda` extra is *not* specified, while the other installs from another repository with a separate version set\nfor GPUs when the `cuda` extra *is* specified:\n\n```toml\n[project]\nname = \"torch-example\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"torch (==2.3.1+cpu) ; extra != 'cuda'\",\n]\n\n[project.optional-dependencies]\ncuda = [\n    \"torch (==2.3.1+cu118)\",\n]\n\n[tool.poetry]\npackage-mode = false\n\n[tool.poetry.dependencies]\ntorch = [\n    { markers = \"extra != 'cuda'\", source = \"pytorch-cpu\"},\n    { markers = \"extra == 'cuda'\", source = \"pytorch-cuda\"},\n ]\n\n[[tool.poetry.source]]\nname = \"pytorch-cpu\"\nurl = \"https://download.pytorch.org/whl/cpu\"\npriority = \"explicit\"\n\n[[tool.poetry.source]]\nname = \"pytorch-cuda\"\nurl = \"https://download.pytorch.org/whl/cu118\"\npriority = \"explicit\"\n```\n\nFor the CPU case, we have to specify `\"extra != 'cuda'\"` because the version specified is not compatible with the\nGPU (`cuda`) version.\n\nThis same logic applies when you want either-or extras:\n\n```toml\n[project]\nname = \"torch-example\"\nrequires-python = \">=3.10\"\n\n[project.optional-dependencies]\ncpu = [\n    \"torch (==2.3.1+cpu)\",\n]\ncuda = [\n    \"torch (==2.3.1+cu118)\",\n]\n\n[tool.poetry]\npackage-mode = false\n\n[tool.poetry.dependencies]\ntorch = [\n    { markers = \"extra == 'cpu' and extra != 'cuda'\", source = \"pytorch-cpu\"},\n    { markers = \"extra == 'cuda' and extra != 'cpu'\", source = \"pytorch-cuda\"},\n ]\n\n[[tool.poetry.source]]\nname = \"pytorch-cpu\"\nurl = \"https://download.pytorch.org/whl/cpu\"\npriority = \"explicit\"\n\n[[tool.poetry.source]]\nname = \"pytorch-cuda\"\nurl = \"https://download.pytorch.org/whl/cu118\"\npriority = \"explicit\"\n```\n\n## Multiple constraints dependencies\n\nSometimes, one of your dependencies may have different version ranges depending\non the target Python versions.\n\nLet's say you have a dependency on the package `foo` which is only compatible\nwith Python 3.6–3.7 up to version 1.9, and compatible with Python 3.8+ from version 2.0:\nyou would declare it like so:\n\n{{< tabs tabTotal=\"2\" tabID1=\"multiple-constraints-project\" tabID2=\"multiple-constraints-poetry\" tabName1=\"[project]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"multiple-constraints-project\" >}}\n```toml\n[project]\n# ...\ndependencies = [\n    \"foo (<=1.9) ; python_version >= '3.6' and python_version < '3.8'\",\n    \"foo (>=2.0,<3.0) ; python_version >= '3.8'\"\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"multiple-constraints-poetry\" >}}\n```toml\n[tool.poetry.dependencies]\nfoo = [\n    {version = \"<=1.9\", python = \">=3.6,<3.8\"},\n    {version = \"^2.0\", python = \">=3.8\"}\n]\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n{{% note %}}\nThe constraints **must** have different requirements (like `python`)\notherwise it will cause an error when resolving dependencies.\n{{% /note %}}\n\n### Combining git / url / path dependencies with source repositories\n\nDirect origin (`git`/ `url`/ `path`) dependencies can satisfy the requirement of a dependency that\ndoesn't explicitly specify a source, even when mutually exclusive markers are used. For instance,\nin the following example, the url package will also be a valid solution for the second requirement:\n```toml\nfoo = [\n    { platform = \"darwin\", url = \"https://example.com/example-1.0-py3-none-any.whl\" },\n    { platform = \"linux\", version = \"^1.0\" },\n]\n```\n\nSometimes you may instead want to use a direct origin dependency for specific conditions\n(i.e., a compiled package that is not available on PyPI for a certain platform/architecture) while\nfalling back on source repositories in other cases. In this case you should explicitly ask for your\ndependency to be satisfied by another `source`. For example:\n```toml\nfoo = [\n    { platform = \"darwin\", url = \"https://example.com/foo-1.0.0-py3-none-macosx_11_0_arm64.whl\" },\n    { platform = \"linux\", version = \"^1.0\", source = \"pypi\" },\n]\n```\n\n## Expanded dependency specification syntax\n\nIn the case of more complex dependency specifications, you may find that you\nend up with lines which are very long and difficult to read. In these cases,\nyou can shift from using \"inline table\" syntax to the \"standard table\" syntax.\n\nAn example where this might be useful is the following:\n\n```toml\n[tool.poetry.group.dev.dependencies]\nblack = {version = \"19.10b0\", allow-prereleases = true, python = \"^3.7\", markers = \"platform_python_implementation == 'CPython'\"}\n```\n\nAs a single line, this is a lot to digest. To make this a bit easier to\nwork with, you can do the following:\n\n```toml\n[tool.poetry.group.dev.dependencies.black]\nversion = \"19.10b0\"\nallow-prereleases = true\npython = \"^3.7\"\nmarkers = \"platform_python_implementation == 'CPython'\"\n```\n\nThe same information is still present, and ends up providing the exact\nsame specification. It's simply split into multiple, slightly more readable,\nlines.\n\n### Handling of pre-releases\n\nPer default, Poetry will prefer stable releases and only choose a pre-release\nif no stable release satisfies a version constraint. In some cases, this may result in\na solution containing pre-releases even if another solution without pre-releases exists.\n\nIf you want to disallow pre-releases for a specific dependency,\nyou can set `allow-prereleases` to `false`. In this case, dependency resolution will\nfail if there is no solution without choosing a pre-release.\n\nIf you want to prefer the latest version of a dependency even if it is a pre-release,\nyou can set `allow-prereleases` to `true` so that Poetry makes no distinction\nbetween stable and pre-release versions during dependency resolution.\n"
  },
  {
    "path": "docs/faq.md",
    "content": "---\ntitle: \"FAQ\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 110\n---\n\n# FAQ\n\n### Why is the dependency resolution process slow?\n\nWhile the dependency resolver at the heart of Poetry is highly optimized and\nshould be fast enough for most cases, with certain sets of dependencies,\nit can take time to find a valid solution.\n\nThis is due to the fact that not all libraries on PyPI have properly declared their metadata\nand, as such, they are not available via the PyPI JSON API. At this point, Poetry has no choice\nbut to download the packages and inspect them to get the necessary information. This is an expensive\noperation, both in bandwidth and time, which is why it seems this is a long process.\n\nAt the moment, there is no way around it. However, if you notice that Poetry\nis downloading many versions of a single package, you can lessen the workload\nby constraining that one package in your pyproject.toml more narrowly. That way,\nPoetry does not have to sift through so many versions of it, which may speed up\nthe locking process considerably in some cases.\n\n{{% note %}}\nOnce Poetry has cached the releases' information on your machine, the dependency resolution process\nwill be much faster.\n{{% /note %}}\n\n### What kind of versioning scheme does Poetry use for itself?\n\nPoetry uses \"major.minor.micro\" version identifiers as mentioned in\n[PEP 440](https://peps.python.org/pep-0440/#final-releases).\n\nVersion bumps are done similar to Python's versioning:\n* A major version bump (incrementing the first number) is only done for breaking changes\n  if a deprecation cycle is not possible, and many users have to perform some manual steps\n  to migrate from one version to the next one.\n* A minor version bump (incrementing the second number) may include new features as well\n  as new deprecations and drop features deprecated in an earlier minor release.\n* A micro version bump (incrementing the third number) usually only includes bug fixes.\n  Deprecated features will not be dropped in a micro release.\n\n### Why does Poetry not adhere to semantic versioning?\n\nBecause of its large user base, even small changes not considered relevant by most users\ncan turn out to be a breaking change for some users in hindsight.\nSticking to strict [semantic versioning](https://semver.org) and (almost) always bumping\nthe major version instead of the minor version does not seem desirable\nsince the minor version will not carry any meaning anymore.\n\n### Are unbound version constraints a bad idea?\n\nA version constraint without an upper bound such as `*` or `>=3.4` will allow updates to any future version of the dependency.\nThis includes major versions breaking backward compatibility.\n\nOnce a release of your package is published, you cannot tweak its dependencies anymore in case a dependency breaks BC\n– you have to do a new release but the previous one stays broken.\n(Users can still work around the broken dependency by restricting it by themselves.)\n\nTo avoid such issues, you can define an upper bound on your constraints,\nwhich you can increase in a new release after testing that your package is compatible\nwith the new major version of your dependency.\n\nFor example, instead of using `>=3.4` you can use `^3.4` which allows all versions `<4.0`.\nThe `^` operator works very well with libraries following [semantic versioning](https://semver.org).\n\nHowever, when defining an upper bound, users of your package are not able to update\na dependency beyond the upper bound even if it does not break anything\nand is fully compatible with your package.\nYou have to release a new version of your package with an increased upper-bound first.\n\nIf your package is used as a library in other packages, it might be better to avoid\nupper bounds and thus unnecessary dependency conflicts (unless you already know for sure\nthat the next release of the dependency will break your package).\nIf your package is used as an application, it might be worth defining an upper bound.\n\n### Is tox supported?\n\n**Yes**. Provided that you are using `tox` >= 4, you can use it in combination with\nthe PEP 517 compliant build system provided by Poetry. (With tox 3, you have to set the\n[isolated build](https://tox.wiki/en/3.27.1/config.html#conf-isolated_build) option.)\n\nSo, in your `pyproject.toml` file, add this section if it does not already exist:\n\n```toml\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n`tox` can be configured in multiple ways. It depends on what should be the code under test and which dependencies\nshould be installed.\n\n#### Use case #1\n```ini\n[tox]\n\n[testenv]\ndeps =\n    pytest\ncommands =\n    pytest tests/ --import-mode importlib\n```\n\n`tox` will create an `sdist` package of the project and uses `pip` to install it in a fresh environment.\nThus, dependencies are resolved by `pip`.\n\n#### Use case #2\n```ini\n[tox]\n\n[testenv]\nallowlist_externals = poetry\ncommands_pre =\n    poetry install --no-root --sync\ncommands =\n    poetry run pytest tests/ --import-mode importlib\n```\n\n`tox` will create an `sdist` package of the project and uses `pip` to install it in a fresh environment.\nThus, dependencies are resolved by `pip` in the first place. But afterward, we run Poetry,\n which will install the locked dependencies into the environment.\n\n#### Use case #3\n```ini\n[tox]\n\n[testenv]\nskip_install = true\nallowlist_externals = poetry\ncommands_pre =\n    poetry install\ncommands =\n    poetry run pytest tests/ --import-mode importlib\n```\n\n`tox` will not do any install. Poetry installs all the dependencies and the current package in editable mode.\nThus, tests are running against the local files and not the built and installed package.\n\n#### Note about credentials\n\nNote that `tox` does not forward the environment variables of your current shell session by default.\nThis may cause Poetry to not be able to install dependencies in the `tox` environments if you have configured\ncredentials using the system keyring on Linux systems or using environment variables in general.\nYou can use the `passenv` [configuration option](https://tox.wiki/en/latest/config.html#passenv) to forward the\nrequired variables explicitly or `passenv = \"*\"` to forward all of them.\nLinux systems may require forwarding the `DBUS_SESSION_BUS_ADDRESS` variable to allow access to the system keyring,\nthough this may vary between desktop environments.\n\nAlternatively, you can disable the keyring completely:\n\n```bash\npoetry config keyring.enabled false\n```\n\nBe aware that this will cause Poetry to write passwords to plaintext config files.\nYou will need to set the credentials again after changing this setting.\n\n### Is Nox supported?\n\nUse the [`nox-poetry`](https://github.com/cjolowicz/nox-poetry) package to install locked versions of\ndependencies specified in `poetry.lock` into [Nox](https://nox.thea.codes/en/stable/) sessions.\n\n### I don't want Poetry to manage my virtual environments. Can I disable it?\n\nWhile Poetry automatically creates virtual environments to always work isolated\nfrom the global Python installation, there are rare scenarios where the use of a Poetry managed\nvirtual environment is not possible or preferred.\n\nIn this case, you can disable this feature by setting the `virtualenvs.create` setting to `false`:\n\n```bash\npoetry config virtualenvs.create false\n```\n\n{{% warning %}}\nThe recommended best practice, including when installing an application within a container, is to make\nuse of a virtual environment. This can also be managed by another tool.\n\nThe Poetry team strongly encourages the use of a virtual environment.\n{{% /warning %}}\n\n### Why is Poetry telling me that the current project's supported Python range is not compatible with one or more packages' Python requirements?\n\nUnlike `pip`, Poetry doesn't resolve for just the Python in the current environment. Instead, it makes sure that a dependency\nis resolvable within the given Python version range in `pyproject.toml`.\n\nAssume you have the following `pyproject.toml`:\n\n```toml\n[tool.poetry.dependencies]\npython = \"^3.7\"\n```\n\nThis means your project aims to be compatible with any Python version >=3.7,<4.0. Whenever you try to add a dependency\nwhose Python requirement doesn't match the whole range, Poetry will tell you this, e.g.:\n\n```\nThe current project's supported Python range (>=3.7.0,<4.0.0) is not compatible with some of the required packages Python requirement:\n    - scipy requires Python >=3.7,<3.11, so it will not be installable for Python >=3.11,<4.0.0\n```\n\nUsually you will want to match the supported Python range of your project with the upper bound of the failing dependency.\nAlternatively, you can tell Poetry to install this dependency [only for a specific range of Python versions]({{< relref \"dependency-specification#multiple-constraints-dependencies\" >}}),\nif you know that it's not needed in all versions.\n\nIf you do not want to set an upper bound in the metadata when building your project,\nyou can omit it in the `project` section and only set it in `tool.poetry.dependencies`:\n\n```toml\n[project]\n# ...\nrequires-python = \">=3.7\"  # used for metadata when building the project\n\n[tool.poetry.dependencies]\npython = \">=3.7,<3.11\"  # used for locking dependencies\n```\n\n\n### Why does Poetry enforce PEP 440 versions?\n\nThis is done to be compliant with the broader Python ecosystem.\n\nFor example, if Poetry builds a distribution for a project that uses a version that is not valid, according to\n[PEP 440](https://peps.python.org/pep-0440), third party tools will be unable to parse the version correctly.\n\n\n### Poetry busts my Docker cache because it requires me to COPY my source files in before installing 3rd party dependencies\n\nBy default, running `poetry install ...` requires you to have your source files present (both the \"root\" package and any directory path dependencies you might have).\nThis interacts poorly with Docker's caching mechanisms because any change to a source file will make any layers (subsequent commands in your Dockerfile) re-run.\nFor example, you might have a Dockerfile that looks something like this:\n\n```text\nFROM python\nCOPY pyproject.toml poetry.lock .\nCOPY src/ ./src\nRUN pip install poetry && poetry install --only main\n```\n\nAs soon as *any* source file changes, the cache for the `RUN` layer will be invalidated, which forces all 3rd party dependencies (likely the slowest step out of these) to be installed again if you changed any files in `src/`.\n\nTo avoid this cache busting you can split this into two steps:\n\n1. Install 3rd party dependencies.\n2. Copy over your source code and install just the source code.\n\nThis might look something like this:\n\n```text\nFROM python\nCOPY pyproject.toml poetry.lock .\nRUN pip install poetry && poetry install --only main --no-root --no-directory\nCOPY src/ ./src\nRUN poetry install --only main\n```\n\nThe two key options we are using here are `--no-root` (skips installing the project source) and `--no-directory` (skips installing any local directory path dependencies, you can omit this if you don't have any).\n[More information on the options available for `poetry install`]({{< relref \"cli#install\" >}}).\n\n\n### My requests are timing out!\n\nPoetry's default HTTP request timeout is 15 seconds, the same as `pip`.\nSimilar to `PIP_REQUESTS_TIMEOUT`, the **experimental** environment variable `POETRY_REQUESTS_TIMEOUT`\ncan be set to alter this value.\n\n\n### How do I migrate an existing Poetry project using `tools.poetry` section to use the new `project` section (PEP 621)?\n\n{{% note %}}\nPoetry `>=2.0.0` should seamlessly support both `tools.poetry` section only configuration as well using the `project` section. This\nlets you decide when and if you would like to migrate to using the `project` section as [described by PyPA](https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table).\n\nSee documentation on [the `pyproject.toml` file]({{< relref \"pyproject\" >}}), for information specific to Poetry.\n{{% /note %}}\n\nDue to the nature of this change some manual changes to your `pyproject.toml` file is unavoidable in order start using the `project` section. The following tabs\nshow a transition example. If you wish to retain Poetry's richer [dependency specification]({{< relref \"dependency-specification\" >}}) syntax it is recommended that\nyou use dynamic dependencies as described in the second tab below.\n\n{{< tabs tabTotal=\"3\" tabID1=\"migrate-pep621-old\" tabName1=\"Original\" tabID2=\"migrate-pep621-new-dynamic\" tabName2=\"Using Dynamic Dependencies\" tabID3=\"migrate-pep621-new-static\" tabName3=\"Using Static Dependencies\">}}\n\n{{< tab tabID=\"migrate-pep621-old\" >}}\n\n```toml\n[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Baz Qux <baz.qux@example.com>\"]\nreadme = \"README.md\"\npackages = [{ include = \"awesome\", from = \"src\" }]\ninclude = [{ path = \"tests\", format = \"sdist\" }]\nhomepage = \"https://python-foobar.org/\"\nrepository = \"https://github.com/python-foobar/foobar\"\ndocumentation = \"https://python-foobar.org/docs\"\nkeywords = [\"packaging\", \"dependency\", \"foobar\"]\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\n\n[tool.poetry.scripts]\nfoobar = \"foobar.console.application:main\"\n\n[tool.poetry.dependencies]\npython = \"^3.13\"\nhttpx = \"^0.28.1\"\n\n[tool.poetry.group.dev.dependencies]\npre-commit = \">=2.10\"\n\n[tool.poetry.group.test.dependencies]\npytest = \">=8.0\"\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"migrate-pep621-new-static\" >}}\n\n```toml\n[project]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\n    { name = \"Baz Qux\", email = \"baz.qux@example.com\" }\n]\nreadme = \"README.md\"\nrequires-python = \">=3.13\"\nkeywords = [\"packaging\", \"dependency\", \"foobar\"]\n# classifiers property is dynamic because we want to create Python classifiers automatically\n# dependencies are dynamic because we want to keep Poetry's rich dependency definition format\ndynamic = [\"classifiers\", \"dependencies\"]\n\n[project.urls]\nhomepage = \"https://python-foobar.org/\"\nrepository = \"https://github.com/python-foobar/foobar\"\ndocumentation = \"https://python-foobar.org/docs\"\n\n[project.scripts]\nfoobar = \"foobar.console.application:main\"\n\n[tool.poetry]\nrequires-poetry = \">=2.0\"\npackages = [{ include = \"foobar\", from = \"src\" }]\ninclude = [{ path = \"tests\", format = \"sdist\" }]\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\n\n[tool.poetry.dependencies]\nhttpx = \"^0.28.1\"\n\n[tool.poetry.group.dev.dependencies]\npre-commit = \">=2.10\"\n\n[tool.poetry.group.test.dependencies]\npytest = \">=8.0\"\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{< /tab >}}\n\n{{< tab tabID=\"migrate-pep621-new-static\" >}}\n\n```toml\n[project]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\n    { name = \"Baz Qux\", email = \"baz.qux@example.com\" }\n]\nreadme = \"README.md\"\nrequires-python = \">=3.13\"\nkeywords = [\"packaging\", \"dependency\", \"foobar\"]\n# classifiers property is dynamic because we want to create Python classifiers automatically\ndynamic = [\"classifiers\"]\ndependencies = [\n    \"httpx (>=0.28.1,<0.29.0)\"\n]\n\n[project.urls]\nhomepage = \"https://python-foobar.org/\"\nrepository = \"https://github.com/python-foobar/foobar\"\ndocumentation = \"https://python-foobar.org/docs\"\n\n[project.scripts]\nfoobar = \"foobar.console.application:main\"\n\n[tool.poetry]\nrequires-poetry = \">=2.0\"\npackages = [{ include = \"foobar\", from = \"src\" }]\ninclude = [{ path = \"tests\", format = \"sdist\" }]\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\n\n[tool.poetry.group.dev.dependencies]\npre-commit = \">=2.10\"\n\n[tool.poetry.group.test.dependencies]\npytest = \">=8.0\"\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{< /tab >}}\n\n{{< /tabs >}}\n\n{{% note %}}\n- The `classifiers` property is dynamic to allow Poetry to create and manage Python classifiers in accordance with supported Python version.\n- The `python` dependency, in this example was replaced with `project.requires-python`. However, note that if you need an upper bound on supported Python versions refer to the documentation [here]({{< relref \"pyproject#requires-python\" >}}).\n- The [`requires-poetry`]({{< relref \"pyproject#requires-poetry\" >}}) is added to the `tools.poetry` section.\n{{% /note %}}\n"
  },
  {
    "path": "docs/libraries.md",
    "content": "---\ntitle: \"Libraries\"\ndraft: false\ntype: docs\nlayout: \"docs\"\n\nmenu:\n  docs:\n    weight: 20\n---\n\n\n# Libraries\n\nThis chapter will tell you how to make your library installable through Poetry.\n\n\n## Versioning\n\nPoetry requires [PEP 440](https://peps.python.org/pep-0440)-compliant versions for all projects.\n\nWhile Poetry does not enforce any release convention, it used to encourage the use of\n[semantic versioning](https://semver.org/) within the scope of\n[PEP 440](https://peps.python.org/pep-0440/#semantic-versioning) and supports\n[version constraints]({{< relref \"dependency-specification/#caret-requirements\" >}})\nthat are especially suitable for semver.\n\n{{% note %}}\n\nAs an example, `1.0.0-hotfix.1` is not compatible with [PEP 440](https://peps.python.org/pep-0440). You can instead\nchoose to use `1.0.0-post1` or `1.0.0.post1`.\n\n{{% /note %}}\n\n## Lock file\n\nFor your library, you may commit the `poetry.lock` file if you want to.\nThis can help your team to always test against the same dependency versions.\nHowever, this lock file will not have any effect on other projects that depend on it.\nIt only has an effect on the main project.\n\nIf you do not want to commit the lock file and you are using git, add it to the `.gitignore`.\n\n## Packaging\n\nBefore you can actually publish your library, you will need to package it.\n\nYou need to define a build-system according to [PEP 517](https://peps.python.org/pep-0517/) in the `pyproject.toml` file:\n\n```toml\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\nThen you can package your library by running:\n\n```bash\npoetry build\n```\n\nThis command will package your library in two different formats: `sdist` which is\nthe source format, and `wheel` which is a `compiled` package.\n\nPoetry will automatically include some license-related files when building a package -\nin the `.dist-info/licenses` directory when building a `wheel`,\nand in the root folder when building an `sdist`:\n- `LICENSE*`\n- `LICENCE*`\n- `COPYING*`\n- `AUTHORS*`\n- `NOTICE*`\n- `LICENSES/**/*`\n\nYou can override this behavior by specifying\n[`license-files`]({{< relref \"pyproject/#license-files\" >}})\nin the `pyproject.toml` file.\n\n### Alternative build backends\n\nIf you want to use a different build backend, you can specify it in the `pyproject.toml` file:\n\n```toml\n[build-system]\nrequires = [\"maturin>=0.8.1,<0.9\"]\nbuild-backend = \"maturin\"\n```\n\nThe `poetry build` command will then use the specified build backend to build your package in\nan isolated environment. Ensure you have specified any additional settings according to the\ndocumentation of the build backend you are using.\n\n\nOnce building is done, you are ready to publish your library.\n\n## Publishing to PyPI\n\nAlright, so now you can publish packages.\n\nPoetry will publish to [PyPI](https://pypi.org) by default. Anything that is published to PyPI\nis available automatically through Poetry. Since [pendulum](https://pypi.org/project/pendulum/)\nis on PyPI we can depend on it without having to specify any additional repositories.\n\nIf we wanted to share `poetry-demo` with the Python community, we would publish on PyPI as well.\nDoing so is really easy.\n\n```bash\npoetry publish\n```\n\nThis will package and publish the library to PyPI, on the condition that you are a registered user\nand you have [configured your credentials]({{< relref \"repositories#configuring-credentials\" >}}) properly.\n\n{{% note %}}\nThe `publish` command does not execute `build` by default.\n\nIf you want to build and publish your packages together,\njust pass the `--build` option.\n{{% /note %}}\n\nOnce this is done, your library will be available to anyone.\n\n\n## Publishing to a private repository\n\nSometimes, you may want to keep your library private but also be accessible to your team.\n\nIn this case, you will need to use a private repository.\n\nIn order to publish to a private repository, you will need to add it to your\nglobal list of repositories. See [Adding a repository]({{< relref \"repositories#adding-a-repository\" >}})\nfor more information.\n\nOnce this is done, you can publish your package to the repository like so:\n\n```bash\npoetry publish -r my-repository\n```\n"
  },
  {
    "path": "docs/managing-dependencies.md",
    "content": "---\ndraft: false\nlayout: single\nmenu:\n  docs:\n    weight: 11\ntitle: Managing dependencies\ntype: docs\n---\n\n\n# Managing dependencies\n\nPoetry supports specifying main dependencies in the [`project.dependencies`]({{< relref \"pyproject#dependencies\" >}}) section of your `pyproject.toml`\naccording to PEP 621. For legacy reasons and to define additional information that are only used by Poetry\nthe [`tool.poetry.dependencies`]({{< relref \"pyproject#dependencies-and-dependency-groups\" >}}) sections can be used.\n\nSee [Dependency specification]({{< relref \"dependency-specification\" >}}) for more information.\n\n## Dependency groups\n\nPoetry provides a way to **organize** your dependencies by **groups**.\n\nThe dependencies declared in `project.dependencies` respectively `tool.poetry.dependencies`\nare part of an implicit `main` group. Those dependencies are required by your project during runtime.\n\nBesides the `main` dependencies, you might have dependencies that are only needed to test your project\nor to build the documentation.\n\nTo declare a new dependency group, use a `dependency-groups` section according to PEP 735 or\na `tool.poetry.group.<group>` section where `<group>` is the name of your dependency group (for instance, `test`):\n\n{{< tabs tabTotal=\"2\" tabID1=\"group-pep735\" tabID2=\"group-poetry\" tabName1=\"[dependency-groups]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"group-pep735\" >}}\n```toml\n[dependency-groups]\ntest = [\n    \"pytest (>=6.0.0,<7.0.0)\",\n    \"pytest-mock\",\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"group-poetry\" >}}\n```toml\n[tool.poetry.group.test.dependencies]\npytest = \"^6.0.0\"\npytest-mock = \"*\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n\n{{% note %}}\nAll dependencies **must be compatible with each other** across groups since they will\nbe resolved regardless of whether they are required for installation or not (see [Installing group dependencies]({{< relref \"#installing-group-dependencies\" >}})).\n\nThink of dependency groups as **labels** associated with your dependencies: they don't have any bearings\non whether their dependencies will be resolved and installed **by default**, they are simply a way to organize\nthe dependencies logically.\n{{% /note %}}\n\n{{% note %}}\nDependency groups, other than the implicit `main` group,\nmust only contain dependencies you need in your development process.\nTo declare a set of dependencies, which add additional functionality to the project\nduring runtime, use [extras]({{< relref \"pyproject#extras\" >}}) instead.\n{{% /note %}}\n\n\n### Optional groups\n\nA dependency group can be declared as optional. This makes sense when you have\na group of dependencies that are only required in a particular environment or for\na specific purpose.\n\n{{< tabs tabTotal=\"2\" tabID1=\"group-optional-pep735\" tabID2=\"group-optional-poetry\" tabName1=\"[dependency-groups]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"group-optional-pep735\" >}}\n```toml\n[dependency-groups]\ndocs = [\n    \"mkdocs\",\n]\n\n[tool.poetry.group.docs]\noptional = true\n```\n{{< /tab >}}\n\n{{< tab tabID=\"group-optional-poetry\" >}}\n```toml\n[tool.poetry.group.docs]\noptional = true\n\n[tool.poetry.group.docs.dependencies]\nmkdocs = \"*\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\n\n\nOptional groups can be installed in addition to the **default** dependencies by using the `--with`\noption of the [`install`]({{< relref \"cli#install\" >}}) command.\n\n```bash\npoetry install --with docs\n```\n\n{{% warning %}}\nOptional group dependencies will **still** be resolved alongside other dependencies, so\nspecial care should be taken to ensure they are compatible with each other.\n{{% /warning %}}\n\n### Including dependencies from other groups\n\nYou can include dependencies from one group in another group.\nThis is useful when you want to aggregate dependencies from multiple groups into a single group.\n\n{{< tabs tabTotal=\"2\" tabID1=\"group-include-pep735\" tabID2=\"group-include-poetry\" tabName1=\"[dependency-groups]\" tabName2=\"[tool.poetry]\">}}\n\n{{< tab tabID=\"group-include-pep735\" >}}\n```toml\n[dependency-groups]\ntest = [\n    \"pytest (>=8.0.0,<9.0.0)\",\n]\nlint = [\n    \"ruff (>=0.11.0,<0.12.0)\",\n]\ndev = [\n    { include-group = \"test\" },\n    { include-group = \"lint\" },\n    \"tox\",\n]\n```\n{{< /tab >}}\n\n{{< tab tabID=\"group-include-poetry\" >}}\n```toml\n[tool.poetry.group.test.dependencies]\npytest = \"^8.0.0\"\n\n[tool.poetry.group.lint.dependencies]\nruff = \"^0.11\"\n\n[tool.poetry.group.dev]\ninclude-groups = [\n    \"test\",\n    \"lint\",\n]\n\n[tool.poetry.group.dev.dependencies]\ntox = \"*\"\n```\n{{< /tab >}}\n{{< /tabs >}}\n\nIn this example, the `dev` group includes all dependencies from the `test` and `lint` groups.\n\n### Adding a dependency to a group\n\nThe [`add`]({{< relref \"cli#add\" >}}) command is the preferred way to add dependencies\nto a group. This is done by using the `--group (-G)` option.\n\n```bash\npoetry add pytest --group test\n```\n\nIf the group does not already exist, it will be created automatically.\n\n### Installing group dependencies\n\n**By default**, dependencies across **all non-optional groups** will be installed when executing\n`poetry install`.\n\n{{% note %}}\nThe default set of dependencies for a project includes the implicit `main` group as well as all\ngroups that are not explicitly marked as an [optional group]({{< relref \"#optional-groups\" >}}).\n{{% /note %}}\n\nYou can **exclude** one or more groups with the `--without` option:\n\n```bash\npoetry install --without test,docs\n```\n\nYou can also opt in [optional groups]({{< relref \"#optional-groups\" >}}) by using the `--with` option:\n\n```bash\npoetry install --with docs\n```\n\n{{% warning %}}\nWhen used together, `--without` takes precedence over `--with`. For example, the following command\nwill only install the dependencies specified in the optional `test` group.\n\n```bash\npoetry install --with test,docs --without docs\n```\n{{% /warning %}}\n\nFinally, in some case you might want to install **only specific groups** of dependencies\nwithout installing the default set of dependencies. For that purpose, you can use\nthe `--only` option.\n\n```bash\npoetry install --only docs\n```\n\n{{% note %}}\nIf you only want to install the project's runtime dependencies, you can do so with the\n`--only main` notation:\n\n```bash\npoetry install --only main\n```\n{{% /note %}}\n\n{{% note %}}\nIf you want to install the project root, and no other dependencies, you can use\nthe `--only-root` option.\n\n```bash\npoetry install --only-root\n```\n{{% /note %}}\n\n### Removing dependencies from a group\n\nThe [`remove`]({{< relref \"cli#remove\" >}}) command supports a `--group` option\nto remove packages from a specific group:\n\n```bash\npoetry remove mkdocs --group docs\n```\n\n## Synchronizing dependencies\n\nPoetry supports what's called dependency synchronization. Dependency synchronization ensures\nthat the locked dependencies in the `poetry.lock` file are the only ones present\nin the environment, removing anything that's not necessary.\n\nThis is done by using the `sync` command:\n\n```bash\npoetry sync\n```\n\nThe `sync` command can be combined with any [dependency groups]({{< relref \"#dependency-groups\" >}}) related options\nto synchronize the environment with specific groups. Note that extras are separate.\nAny extras not selected for install are always removed.\n\n```bash\npoetry sync --without dev\npoetry sync --with docs\npoetry sync --only dev\n```\n\n## Layering optional groups\n\nWhen using the `install` command without the `--sync` option, you can install any subset of optional groups without removing\nthose that are already installed.  This is very useful, for example, in multi-stage\nDocker builds, where you run `poetry install` multiple times in different build stages.\n"
  },
  {
    "path": "docs/managing-environments.md",
    "content": "---\ntitle: \"Managing environments\"\ndraft: false\ntype: docs\nlayout: \"docs\"\n\nmenu:\n  docs:\n    weight: 60\n---\n\n# Managing environments\n\nPoetry makes project environment isolation one of its core features.\n\nWhat this means is that it will always work isolated from your global Python installation.\nTo achieve this, it will first check if it's currently running inside a virtual environment.\nIf it is, it will use it directly without creating a new one. But if it's not, it will use\none that it has already created or create a brand new one for you.\n\nBy default, Poetry will try to use the Python version used during Poetry's installation\nto create the virtual environment for the current project.\n\nHowever, for various reasons, this Python version might not be compatible\nwith the `python` range supported by the project. In this case, Poetry will try\nto find one that is and use it. If it's unable to do so then you will be prompted\nto activate one explicitly, see [Switching environments](#switching-between-environments).\n\n{{% note %}}\nIf you use a tool like [pyenv](https://github.com/pyenv/pyenv) to manage different Python versions,\nyou can switch the current `python` of your shell and Poetry will use it to create\nthe new environment.\n\nFor instance, if your project requires a newer Python than is available with\nyour system, a standard workflow would be:\n\n```bash\npyenv install 3.9.8\npyenv local 3.9.8  # Activate Python 3.9 for the current project\npoetry install\n```\n\n{{% /note %}}\n\n{{% note %}}\nSince version 1.2, Poetry no longer supports managing environments for Python 2.7.\n{{% /note %}}\n\n## Switching between environments\n\nSometimes this might not be feasible for your system, especially Windows where `pyenv`\nis not available, or you simply prefer to have a more explicit control over your environment.\nFor this specific purpose, you can use the `env use` command to tell Poetry\nwhich Python version to use for the current project.\n\n```bash\npoetry env use /full/path/to/python\n```\n\nIf you have the python executable in your `PATH` you can use it:\n\n```bash\npoetry env use python3.7\n```\n\nYou can even just use the minor Python version in this case:\n\n```bash\npoetry env use 3.7\n```\n\nIf you want to disable the explicitly activated virtual environment, you can use the\nspecial `system` Python version to retrieve the default behavior:\n\n```bash\npoetry env use system\n```\n\n## Activating the environment\n\n{{% note %}}\nLooking for `poetry shell`? It was moved to a plugin: [`poetry-plugin-shell`](https://github.com/python-poetry/poetry-plugin-shell)\n{{% /note %}}\n\nThe `poetry env activate` command prints the activate command of the virtual environment to the console.\nYou can run the output command manually or feed it to the eval command of your shell to activate the environment.\nThis way you won't leave the current shell.\n\n{{< tabs tabTotal=\"3\" tabID1=\"bash-csh-zsh\" tabID2=\"fish\" tabID3=\"powershell\" tabName1=\"Bash/Zsh/Csh\" tabName2=\"Fish\" tabName3=\"Powershell\" >}}\n\n{{< tab tabID=\"bash-csh-zsh\" >}}\n\n```bash\n$ eval $(poetry env activate)\n(test-project-for-test) $  # Virtualenv entered\n```\n\n{{< /tab >}}\n{{< tab tabID=\"fish\" >}}\n\n```bash\n$ eval (poetry env activate)\n(test-project-for-test) $  # Virtualenv entered\n```\n\n{{< /tab >}}\n{{< tab tabID=\"powershell\" >}}\n\n```ps1\nPS1> Invoke-Expression (poetry env activate)\n(test-project-for-test) PS1>  # Virtualenv entered\n```\n\n{{< /tab >}}\n{{< /tabs >}}\n\n## Displaying the environment information\n\nIf you want to get basic information about the currently activated virtual environment,\nyou can use the `env info` command:\n\n```bash\npoetry env info\n```\n\nwill output something similar to this:\n\n```text\nVirtualenv\nPython:         3.7.1\nImplementation: CPython\nPath:           /path/to/poetry/cache/virtualenvs/test-O3eWbxRl-py3.7\nValid:          True\n\nBase\nPlatform: darwin\nOS:       posix\nPython:   /path/to/main/python\n```\n\nIf you only want to know the path to the virtual environment, you can pass the `--path` option\nto `env info`:\n\n```bash\npoetry env info --path\n```\n\nIf you only want to know the path to the python executable (useful for running mypy from a global environment without installing it in the virtual environment), you can pass the `--executable` option\nto `env info`:\n\n```bash\npoetry env info --executable\n```\n\n## Listing the environments associated with the project\n\nYou can also list all the virtual environments associated with the current project\nwith the `env list` command:\n\n```bash\npoetry env list\n```\n\nwill output something like the following:\n\n```text\ntest-O3eWbxRl-py3.6\ntest-O3eWbxRl-py3.7 (Activated)\n```\n\nYou can pass the option `--full-path` to display the full path to the environments:\n\n```bash\npoetry env list --full-path\n```\n\n## Deleting the environments\n\nFinally, you can delete existing virtual environments by using `env remove`:\n\n```bash\npoetry env remove /full/path/to/python\npoetry env remove python3.7\npoetry env remove 3.7\npoetry env remove test-O3eWbxRl-py3.7\n```\n\nYou can delete more than one environment at a time.\n\n```bash\npoetry env remove python3.6 python3.7 python3.8\n```\n\nUse the `--all` option to delete all virtual environments at once.\n\n```bash\npoetry env remove --all\n```\n\nIf you remove the currently activated virtual environment, it will be automatically deactivated.\n\n{{% note %}}\nIf you use the [`virtualenvs.in-project`]({{< relref \"configuration#virtualenvsin-project\" >}}) configuration, you\ncan simply use the command as shown below.\n\n```bash\npoetry env remove\n```\n{{% /note %}}\n"
  },
  {
    "path": "docs/plugins.md",
    "content": "---\ntitle: \"Plugins\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 80\n---\n\n# Plugins\n\nPoetry supports using and building plugins if you wish to\nalter or expand Poetry's functionality with your own.\n\nFor example if your environment poses special requirements\non the behaviour of Poetry which do not apply to the majority of its users\nor if you wish to accomplish something with Poetry in a way that is not desired by most users.\n\nIn these cases you could consider creating a plugin to handle your specific logic.\n\n\n## Creating a plugin\n\nA plugin is a regular Python package which ships its code as part of the package\nand may also depend on further packages.\n\n### Plugin package\n\nThe plugin package must depend on Poetry\nand declare a proper [plugin]({{< relref \"pyproject#plugins\" >}}) in the `pyproject.toml` file.\n\n```toml\n[project]\nname = \"my-poetry-plugin\"\nversion = \"1.0.0\"\n# ...\nrequires-python = \">=3.7\"\ndependencies = [\n    \"poetry (>=1.2,<2.0)\",\n]\n\n[project.entry-points.\"poetry.plugin\"]\ndemo = \"poetry_demo_plugin.plugin:MyPlugin\"\n```\n\n### Generic plugins\n\nEvery plugin has to supply a class which implements the `poetry.plugins.Plugin` interface.\n\nThe `activate()` method of the plugin is called after the plugin is loaded\nand receives an instance of `Poetry` as well as an instance of `cleo.io.io.IO`.\n\nUsing these two objects all configuration can be read\nand all public internal objects and state can be manipulated as desired.\n\nExample:\n\n```python\nfrom cleo.io.io import IO\n\nfrom poetry.plugins.plugin import Plugin\nfrom poetry.poetry import Poetry\n\n\nclass MyPlugin(Plugin):\n\n    def activate(self, poetry: Poetry, io: IO):\n        io.write_line(\"Setting readme\")\n        poetry.package.readme = \"README.md\"\n        ...\n```\n\n### Application plugins\n\nIf you want to add commands or options to the `poetry` script you need\nto create an application plugin which implements the `poetry.plugins.ApplicationPlugin` interface.\n\nThe `activate()` method of the application plugin is called after the plugin is loaded\nand receives an instance of `poetry.console.Application`.\n\n```python\nfrom cleo.commands.command import Command\nfrom poetry.plugins.application_plugin import ApplicationPlugin\n\n\nclass CustomCommand(Command):\n\n    name = \"my-command\"\n\n    def handle(self) -> int:\n        self.line(\"My command\")\n\n        return 0\n\n\ndef factory():\n    return CustomCommand()\n\n\nclass MyApplicationPlugin(ApplicationPlugin):\n    def activate(self, application):\n        application.command_loader.register_factory(\"my-command\", factory)\n```\n\n{{% note %}}\nIt's possible to do the following to register the command:\n\n```python\napplication.add(MyCommand())\n```\n\nHowever, it is **strongly** recommended to register a new factory\nin the command loader to defer the loading of the command when it's actually\ncalled.\n\nThis will help keep the performances of Poetry good.\n{{% /note %}}\n\nThe plugin also must be declared in the `pyproject.toml` file of the plugin package\nas a `poetry.application.plugin` plugin:\n\n```toml\n[tool.poetry.plugins.\"poetry.application.plugin\"]\nfoo-command = \"poetry_demo_plugin.plugin:MyApplicationPlugin\"\n```\n\n{{% warning %}}\nA plugin **must not** remove or modify in any way the core commands of Poetry.\n{{% /warning %}}\n\n\n### Event handler\n\nPlugins can also listen to specific events and act on them if necessary.\n\nThese events are fired by [Cleo](https://github.com/python-poetry/cleo)\nand are accessible from the `cleo.events.console_events` module.\n\n- `COMMAND`: this event allows attaching listeners before any command is executed.\n- `SIGNAL`: this event allows some actions to be performed after the command execution is interrupted.\n- `TERMINATE`: this event allows listeners to be attached after the command.\n- `ERROR`: this event occurs when an uncaught exception is raised.\n\nLet's see how to implement an application event handler. For this example\nwe will see how to load environment variables from a `.env` file before executing\na command.\n\n\n```python\nfrom cleo.events.console_events import COMMAND\nfrom cleo.events.console_command_event import ConsoleCommandEvent\nfrom cleo.events.event_dispatcher import EventDispatcher\nfrom dotenv import load_dotenv\nfrom poetry.console.application import Application\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.plugins.application_plugin import ApplicationPlugin\n\n\nclass MyApplicationPlugin(ApplicationPlugin):\n    def activate(self, application: Application):\n        application.event_dispatcher.add_listener(\n            COMMAND, self.load_dotenv\n        )\n\n    def load_dotenv(\n        self,\n        event: ConsoleCommandEvent,\n        event_name: str,\n        dispatcher: EventDispatcher\n    ) -> None:\n        command = event.command\n        if not isinstance(command, EnvCommand):\n            return\n\n        io = event.io\n\n        if io.is_debug():\n            io.write_line(\n                \"<debug>Loading environment variables.</debug>\"\n            )\n\n        load_dotenv()\n```\n\n\n## Using plugins\n\nInstalled plugin packages are automatically loaded when Poetry starts up.\n\nYou have multiple ways to install plugins for Poetry\n\n### With `pipx inject`\n\nIf you used `pipx` to install Poetry you can add the plugin packages via the `pipx inject` command.\n\n```shell\npipx inject poetry poetry-plugin\n```\n\nIf you want to uninstall a plugin, you can run:\n\n```shell\npipx uninject poetry poetry-plugin          # For pipx versions >= 1.2.0\n\npipx runpip poetry uninstall poetry-plugin  # For pipx versions  < 1.2.0\n```\n\n### With `pip`\n\nThe `pip` binary in Poetry's virtual environment can also be used to install and remove plugins.\nThe environment variable `$POETRY_HOME` here is used to represent the path to the virtual environment.\nThe [installation instructions](/docs/) can be referenced if you are not\nsure where Poetry has been installed.\n\nTo add a plugin, you can use `pip install`:\n\n```shell\n$POETRY_HOME/bin/pip install --user poetry-plugin\n```\n\nIf you want to uninstall a plugin, you can run:\n\n```shell\n$POETRY_HOME/bin/pip uninstall poetry-plugin\n```\n\n### The `self add` command\n\n{{% warning %}}\nEspecially on Windows, `self add` and `self remove` may be problematic\nso that other methods should be preferred.\n{{% /warning %}}\n\n```bash\npoetry self add poetry-plugin\n```\n\nThe `self add` command will ensure that the plugin is compatible with the current version of Poetry\nand install the needed packages for the plugin to work.\n\nThe package specification formats supported by the `self add` command are the same as the ones supported\nby the [`add` command]({{< relref \"cli#add\" >}}).\n\nIf you no longer need a plugin and want to uninstall it, you can use the `self remove` command.\n\n```shell\npoetry self remove poetry-plugin\n```\n\nYou can also list all currently installed plugins by running:\n\n```shell\npoetry self show plugins\n```\n\n### Project plugins\n\nYou can also specify that a plugin is required for your project\nin the `tool.poetry.requires-plugins` section of the pyproject.toml file:\n\n```toml\n[tool.poetry.requires-plugins]\nmy-application-plugin = \">1.0\"\ncustom-plugin = {path = \"custom_plugin\", develop = true}\n```\n\nIf the plugin is not installed in Poetry's own environment when running `poetry install`,\nit will be installed only for the current project under `.poetry/plugins`\nin the project's directory.\n\nThe syntax to specify `plugins` is the same as for [dependencies]({{< relref \"managing-dependencies\" >}}).\nPlugins can be installed in editable mode using path dependencies with `develop = true`,\nwhich is useful for plugin development.\n\n{{% warning %}}\nYou can even overwrite a plugin in Poetry's own environment with another version.\nHowever, if a plugin's dependencies are not compatible with packages in Poetry's own\nenvironment, installation will fail.\n{{% /warning %}}\n\n\n## Maintaining a plugin\n\nWhen writing a plugin, you will probably access internals of Poetry, since there is no\nstable public API. Although we try our best to deprecate methods first, before\nremoving them, sometimes the signature of an internal method has to be changed.\n\nAs the author of a plugin, you are probably testing your plugin\nagainst the latest release of Poetry.\nAdditionally, you should consider testing against the latest release branch and the\nmain branch of Poetry and schedule a CI job that runs regularly even if you did not\nmake any changes to your plugin.\nThis way, you will notice internal changes that break your plugin immediately\nand can prepare for the next Poetry release.\n"
  },
  {
    "path": "docs/pre-commit-hooks.md",
    "content": "---\ntitle: \"pre-commit hooks\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 120\n---\n\n# pre-commit hooks\n\npre-commit is a framework for building and running\n[git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).\nSee the official documentation for more information: [pre-commit.com](https://pre-commit.com/)\n\nThis document provides a list of available pre-commit hooks provided by Poetry.\n\n\n{{% note %}}\nIf you specify the `args:` for a hook in your `.pre-commit-config.yaml`,\nthe defaults are overwritten. You must fully specify all arguments for\nyour hook if you make use of `args:`.\n{{% /note %}}\n\n{{% note %}}\nIf the `pyproject.toml` file is not in the root directory, you can specify `args: [\"-C\", \"./subdirectory\"]`.\n{{% /note %}}\n\n## poetry-check\n\nThe `poetry-check` hook calls the `poetry check` command\nto make sure the poetry configuration does not get committed in a broken state.\n\n### Arguments\n\nThe hook takes the same arguments as the poetry command.\nFor more information see the [check command]({{< relref \"cli#check\" >}}).\n\n## poetry-lock\n\nThe `poetry-lock` hook calls the `poetry lock` command\nto make sure the lock file is up-to-date when committing changes.\n\n### Arguments\n\nThe hook takes the same arguments as the poetry command.\nFor more information see the [lock command]({{< relref \"cli#lock\" >}}).\n\n## poetry-export\n\nThe `poetry-export` hook calls the `poetry export` command\nto sync your `requirements.txt` file with your current dependencies.\n\n{{% warning %}}\nThis hook is provided by the [Export Poetry Plugin](https://github.com/python-poetry/poetry-plugin-export).\n{{% /warning %}}\n\n{{% note %}}\nIt is recommended to run the [`poetry-lock`](#poetry-lock) hook or [`poetry-check`](#poetry-check) with argument `--lock` prior to this one.\n{{% /note %}}\n\n### Arguments\n\nThe hook takes the same arguments as the poetry command.\nFor more information, see the [export command]({{< relref \"cli#export\" >}}).\n\nThe default arguments are `args: [\"-f\", \"requirements.txt\", \"-o\", \"requirements.txt\"]`,\nwhich will create/update the `requirements.txt` file in the current working directory.\n\nYou may add `verbose: true` in your `.pre-commit-config.yaml` in order to output to the\nconsole:\n\n```yaml\nhooks:\n-   id: poetry-export\n    args: [\"-f\", \"requirements.txt\"]\n    verbose: true\n```\n\nAlso, `--dev` can be added to `args` to write dev-dependencies to `requirements.txt`:\n\n```yaml\nhooks:\n-   id: poetry-export\n    args: [\"--dev\", \"-f\", \"requirements.txt\", \"-o\", \"requirements.txt\"]\n```\n\n## poetry-install\n\nThe `poetry-install` hook calls the `poetry install` command to make sure all locked packages are installed.\nIn order to install this hook, you either need to specify `default_install_hook_types`, or you have\nto install it via `pre-commit install --install-hooks -t post-checkout -t post-merge`.\n\n### Arguments\n\nThe hook takes the same arguments as the poetry command.\nFor more information, see the [install command]({{< relref \"cli#install\" >}}).\n\n## Usage\n\nFor more information on how to use pre-commit, please see the [official documentation](https://pre-commit.com/).\n\nA minimalistic `.pre-commit-config.yaml` example:\n\n```yaml\nrepos:\n-   repo: https://github.com/python-poetry/poetry\n    rev: ''  # add version here\n    hooks:\n    -   id: poetry-check\n    -   id: poetry-lock\n    -   id: poetry-export\n    -   id: poetry-install\n```\n\nA `.pre-commit-config.yaml` example for a monorepo setup or if the `pyproject.toml` file is not in the root directory:\n\n```yaml\nrepos:\n-   repo: https://github.com/python-poetry/poetry\n    rev: ''  # add version here\n    hooks:\n    -   id: poetry-check\n        args: [\"-C\", \"./subdirectory\"]\n    -   id: poetry-lock\n        args: [\"-C\", \"./subdirectory\"]\n    -   id: poetry-export\n        args: [\"-C\", \"./subdirectory\", \"-f\", \"requirements.txt\", \"-o\", \"./subdirectory/requirements.txt\"]\n    -   id: poetry-install\n        args: [\"-C\", \"./subdirectory\"]\n```\n\n## FAQ\n\n### Why does `pre-commit autoupdate` not update to the latest version?\n\n`pre-commit autoupdate` updates the `rev` for each repository defined in your `.pre-commit-config.yaml`\nto the latest available tag in the default branch.\n\nPoetry follows a branching strategy where the default branch is the active development branch,\nand fixes get backported to stable branches. New tags are assigned in these stable branches.\n\n`pre-commit` does not support such a branching strategy and has decided to not implement\nan option, either on the [user's side](https://github.com/pre-commit/pre-commit/issues/2512)\nor the [hook author's side](https://github.com/pre-commit/pre-commit/issues/2508), to define a branch for looking\nup the latest available tag.\n\nThus, `pre-commit autoupdate` is not usable for the hooks described here.\n\nYou can avoid changing the `rev` to an unexpected value by using the `--repo` parameter (may be specified multiple\ntimes), to explicitly list repositories that should be updated. An option to explicitly exclude\nrepositories [will not be implemented](https://github.com/pre-commit/pre-commit/issues/1959) into `pre-commit`.\n\n### Are there any alternatives to `pre-commit autoupdate`?\n\nYou may use [pre-commit-update](https://pypi.org/project/pre-commit-update/) as an alternative to\n`pre-commit autoupdate`.\n\nSince `pre-commit-update` can be used as a pre-commit hook itself, the easiest way\nto make use of it would be to include it inside `.pre-commit-config.yaml`:\n\n```yaml\nrepos:\n-   repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update\n    rev: v0.5.1post1\n    hooks:\n    -   id: pre-commit-update\n-   repo: https://github.com/python-poetry/poetry\n    rev: 1.8.3\n    hooks:\n    -   id: poetry-check\n    -   id: poetry-lock\n    -   id: poetry-export\n    -   id: poetry-install\n```\n\nYour `.pre-commit-config.yaml` repos will be checked and updated every time pre-commit hooks run.\n\nFor more advanced configuration, please check the `pre-commit-update` documentation.\n"
  },
  {
    "path": "docs/pyproject.md",
    "content": "---\ntitle: \"The pyproject.toml file\"\ndraft: false\ntype: docs\nlayout: single\n\nmenu:\n  docs:\n    weight: 90\n---\n\n# The `pyproject.toml` file\n\nIn package mode, the only required fields are `name` and `version`\n(either in the `project` section or in the `tool.poetry` section).\nOther fields are optional.\nIn non-package mode, the `name` and `version` fields are required\nif using the `project` section.\n\n{{% note %}}\nRun `poetry check` to print warnings about deprecated fields.\n{{% /note %}}\n\n\n## The `project` section\n\nThe `project` section of the `pyproject.toml` file according to the\n[specification of the PyPA](https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table).\n\n### name\n\nThe name of the package. **Always required when the `project` section is specified**\n\nThis should be a valid name as defined by [PEP 508](https://peps.python.org/pep-0508/#names).\n\n\n```toml\n[project]\nname = \"my-package\"\n```\n\n### version\n\nThe version of the package. **Always required when the `project` section is specified**\n\nThis should be a valid [PEP 440](https://peps.python.org/pep-0440/) string.\n\n```toml\n[project]\n# ...\nversion = \"0.1.0\"\n```\n\nIf you want to set the version dynamically via `poetry build --local-version`\nor you are using a plugin, which sets the version dynamically, you should add `version`\nto dynamic and define the base version in the `tool.poetry` section, for example:\n\n```toml\n[project]\nname = \"my-package\"\ndynamic = [ \"version\" ]\n\n[tool.poetry]\nversion = \"1.0\"  # base version\n```\n\n### description\n\nA short description of the package.\n\n```toml\n[project]\n# ...\ndescription = \"A short description of the package.\"\n```\n\n### license\n\nAn [SPDX expression](https://packaging.python.org/en/latest/glossary/#term-License-Expression)\nrepresenting the license of the package.\n\nThe recommended notation for the most common licenses is (alphabetical):\n\n* Apache-2.0\n* BSD-2-Clause\n* BSD-3-Clause\n* BSD-4-Clause\n* GPL-2.0-only\n* GPL-2.0-or-later\n* GPL-3.0-only\n* GPL-3.0-or-later\n* LGPL-2.1-only\n* LGPL-2.1-or-later\n* LGPL-3.0-only\n* LGPL-3.0-or-later\n* MIT\n\nOptional, but it is highly recommended to supply this.\nMore identifiers are listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/).\n\n```toml\n[project]\n# ...\nlicense = \"MIT\"\n```\n\n{{% warning %}}\nSpecifying license as a table, e.g. `{ text = \"MIT\" }` is deprecated.\nIf you used to specify a license file, e.g. `{ file = \"LICENSE\" }`,\nuse `license-files` instead.\n{{% /warning %}}\n\n### license-files\n\nA list of glob patterns that match the license files of the package\nrelative to the root of the project source tree.\n\n```toml\n[project]\n# ...\nlicense-files = [\n    \"*-LICENSE\",\n    \"CONTRIBUTORS\",\n    \"MY-SPECIAL-LICENSE-DIR/**/*\"\n]\n```\n\nBy default, Poetry will include the following files:\n- `LICENSE*`\n- `LICENCE*`\n- `COPYING*`\n- `AUTHORS*`\n- `NOTICE*`\n- `LICENSES/**/*`\n\n{{% note %}}\nThe default applies only if the `license-files` field is not specified.\nSpecifying an empty list results in no license files being included.\n{{% /note %}}\n\n### readme\n\nA path to the README file or the content.\n\n```toml\n[project]\n# ...\nreadme = \"README.md\"\n```\n\n{{% note %}}\nIf you want to define multiple README files, you have to add `readme` to `dynamic`\nand define them in the `tool.poetry` section.\n{{% /note %}}\n\n```toml\n[project]\n# ...\ndynamic = [ \"readme\" ]\n\n[tool.poetry]\n# ...\nreadme = [\"docs/README1.md\", \"docs/README2.md\"]\n```\n\n### requires-python\n\nThe Python version requirements of the project.\n\n```toml\n[project]\n# ...\nrequires-python = \">=3.8\"\n```\n\n{{% note %}}\nIf you need an upper bound for locking, but do not want to define an upper bound\nin your package metadata, you can omit the upper bound in the `requires-python` field\nand add it in the `tool.poetry.dependencies` section.\n{{% /note %}}\n\n```toml\n[project]\n# ...\nrequires-python = \">=3.8\"\n\n[tool.poetry.dependencies]\npython = \">=3.8,<4.0\"\n```\n\n### authors\n\nThe authors of the package.\n\nThis is a list of authors and should contain at least one author.\n\n```toml\n[project]\n# ...\nauthors = [\n    { name = \"Sébastien Eustace\", email = \"sebastien@eustace.io\" },\n]\n```\n\n### maintainers\n\nThe maintainers of the package.\n\nThis is a list of maintainers and should be distinct from authors.\n\n```toml\n[project]\n# ...\nmaintainers = [\n    { name = \"John Smith\", email = \"johnsmith@example.org\" },\n    { name = \"Jane Smith\", email = \"janesmith@example.org\" },\n]\n```\n\n### keywords\n\nA list of keywords that the package is related to.\n\n```toml\n[project]\n# ...\nkeywords = [ \"packaging\", \"poetry\" ]\n```\n\n### classifiers\n\nA list of PyPI [trove classifiers](https://pypi.org/classifiers/) that describe the project.\n\n```toml\n[project]\n# ...\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n```\n\n{{% warning %}}\nNote that suitable classifiers based on your `python` requirement\nare **not** automatically added for you if you define classifiers statically\nin the `project` section.\n\nIf you want to enrich classifiers automatically, you should add `classifiers` to `dynamic`\nand use the `tool.poetry` section instead.\n{{% /warning %}}\n\n```toml\n[project]\n# ...\ndynamic = [ \"classifiers\" ]\n\n[tool.poetry]\n# ...\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n```\n\n### urls\n\nThe URLs of the project.\n\n```toml\n[project.urls]\nhomepage = \"https://python-poetry.org/\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs/\"\n\"Bug Tracker\" = \"https://github.com/python-poetry/poetry/issues\"\n```\n\nIf you publish your package on PyPI, they will appear in the `Project Links` section.\n\n### scripts\n\nThis section describes the console scripts that will be installed when installing the package.\n\n```toml\n[project.scripts]\nmy_package_cli = 'my_package.console:run'\n```\n\nHere, we will have the `my_package_cli` script installed which will execute the `run` function in the `console` module in the `my_package` package.\n\n{{% note %}}\nWhen a script is added or updated, run `poetry install` to make them available in the project's virtualenv.\n{{% /note %}}\n\n{{% note %}}\nTo include a file as a script, use [`tool.poetry.scripts`]({{< relref \"#scripts-1\" >}}) instead.\n{{% /note %}}\n\n### gui-scripts\n\nThis section describes the GUI scripts that will be installed when installing the package.\n\n```toml\n[project.gui-scripts]\nmy_package_gui = 'my_package.gui:run'\n```\n\nHere, we will have the `my_package_gui` script installed which will execute the `run` function in the `gui` module in the `my_package` package.\n\n{{% note %}}\nWhen a script is added or updated, run `poetry install` to make them available in the project's virtualenv.\n{{% /note %}}\n\n### entry-points\n\nEntry points can be used to define plugins for your package.\n\nPoetry supports arbitrary plugins, which are exposed as the ecosystem-standard\n[entry points](https://packaging.python.org/en/latest/specifications/entry-points/)\nand discoverable using `importlib.metadata`.\nThis is similar to (and compatible with) the entry points feature of `setuptools`.\nThe syntax for registering a plugin is:\n\n```toml\n[project.entry-points] # Optional super table\n\n[project.entry-points.\"A\"]\nB = \"C:D\"\n```\nWhich are:\n\n- `A` - type of the plugin, for example `poetry.plugin` or `flake8.extension`\n- `B` - name of the plugin\n- `C` - python module import path\n- `D` - the entry point of the plugin (a function or class)\n\nExample (from [`poetry-plugin-export`](http://github.com/python-poetry/poetry-plugin-export)):\n\n```toml\n[project.entry-points.\"poetry.application.plugin\"]\nexport = \"poetry_plugin_export.plugins:ExportApplicationPlugin\"\n```\n\n### dependencies\n\nThe `dependencies` of the project.\n\n```toml\n[project]\n# ...\ndependencies = [\n    \"requests>=2.13.0\",\n]\n```\n\nThese are the dependencies that will be declared when building an sdist or a wheel.\n\nSee [Dependency specification]({{< relref \"dependency-specification\" >}}) for more information\nabout the relation between `project.dependencies` and `tool.poetry.dependencies`.\n\n### optional-dependencies\n\nThe optional dependencies of the project (also known as extras).\n\n```toml\n[project.optional-dependencies]\nmysql = [ \"mysqlclient>=1.3,<2.0\" ]\npgsql = [ \"psycopg2>=2.9,<3.0\" ]\ndatabases = [ \"mysqlclient>=1.3,<2.0\", \"psycopg2>=2.9,<3.0\" ]\n```\n\n{{% note %}}\n\nYou can enrich optional dependencies for locking in the `tool.poetry` section\nanalogous to `dependencies`.\n\n{{% /note %}}\n\n\n## The `tool.poetry` section\n\nThe `tool.poetry` section of the `pyproject.toml` file is composed of multiple sections.\n\n### package-mode\n\nWhether Poetry operates in package mode (default) or not.\n\nSee [basic usage]({{< relref \"basic-usage#operating-modes\" >}}) for more information.\n\n```toml\n[tool.poetry]\n# ...\npackage-mode = false\n```\n\n### name\n\n**Deprecated**: Use `project.name` instead.\n\nThe name of the package. **Required in package mode if not defined in the project section**\n\nThis should be a valid name as defined by [PEP 508](https://peps.python.org/pep-0508/#names).\n\n\n```toml\n[tool.poetry]\nname = \"my-package\"\n```\n\n### version\n\n{{% note %}}\nIf you do not want to set the version dynamically via `poetry build --local-version`\nand you are not using a plugin, which sets the version dynamically,\nprefer `project.version` over this setting.\n{{% /note %}}\n\nThe version of the package. **Required in package mode if not defined in the project section**\n\nThis should be a valid [PEP 440](https://peps.python.org/pep-0440/) string.\n\n```toml\n[tool.poetry]\n# ...\nversion = \"0.1.0\"\n```\n\n{{% note %}}\n\nIf you would like to use semantic versioning for your project, please see\n[here]({{< relref \"libraries#versioning\" >}}).\n\n{{% /note %}}\n\n### description\n\n**Deprecated**: Use `project.description` instead.\n\nA short description of the package.\n\n```toml\n[tool.poetry]\n# ...\ndescription = \"A short description of the package.\"\n```\n\n### license\n\n**Deprecated**: Use `project.license` instead.\n\nThe license of the package.\n\nThe recommended notation for the most common licenses is (alphabetical):\n\n* Apache-2.0\n* BSD-2-Clause\n* BSD-3-Clause\n* BSD-4-Clause\n* GPL-2.0-only\n* GPL-2.0-or-later\n* GPL-3.0-only\n* GPL-3.0-or-later\n* LGPL-2.1-only\n* LGPL-2.1-or-later\n* LGPL-3.0-only\n* LGPL-3.0-or-later\n* MIT\n\nOptional, but it is highly recommended to supply this.\nMore identifiers are listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/).\n\n```toml\n[tool.poetry]\n# ...\nlicense = \"MIT\"\n```\n\n### authors\n\n**Deprecated**: Use `project.authors` instead.\n\nThe authors of the package.\n\nThis is a list of authors and should contain at least one author. Authors must be in the form `name <email>`.\n\n```toml\n[tool.poetry]\n# ...\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\",\n]\n```\n\n### maintainers\n\n**Deprecated**: Use `project.maintainers` instead.\n\nThe maintainers of the package.\n\nThis is a list of maintainers and should be distinct from authors. Maintainers may contain an email and be in the form `name <email>`.\n\n```toml\n[tool.poetry]\n# ...\nmaintainers = [\n    \"John Smith <johnsmith@example.org>\",\n    \"Jane Smith <janesmith@example.org>\",\n]\n```\n\n### readme\n\n{{% note %}}\nIf you do not want to set multiple README files, prefer `project.readme` over this setting.\n{{% /note %}}\n\nA path, or list of paths corresponding to the README file(s) of the package.\n\nThe file(s) can be of any format, but if you intend to publish to PyPI keep the\n[recommendations for a PyPI-friendly README](\nhttps://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/) in\nmind. README paths are implicitly relative to `pyproject.toml`.\n\n{{% note %}}\nWhether paths are case-sensitive follows platform defaults, but it is recommended to keep cases.\n\nTo be specific, you can set `readme = \"rEaDmE.mD\"` for `README.md` on macOS and Windows, but Linux users can't `poetry install` after cloning your repo. This is because macOS and Windows are case-insensitive and case-preserving.\n{{% /note %}}\n\nThe contents of the README file(s) are used to populate the [Description\nfield](https://packaging.python.org/en/latest/specifications/core-metadata/#description-optional)\nof your distribution's metadata (similar to `long_description` in setuptools).\nWhen multiple files are specified they are concatenated with newlines.\n\n```toml\n[tool.poetry]\n# ...\nreadme = \"README.md\"\n```\n\n```toml\n[tool.poetry]\n# ...\nreadme = [\"docs/README1.md\", \"docs/README2.md\"]\n```\n\n### homepage\n\n**Deprecated**: Use `project.urls` instead.\n\nA URL to the website of the project.\n\n```toml\n[tool.poetry]\n# ...\nhomepage = \"https://python-poetry.org/\"\n```\n\n### repository\n\n**Deprecated**: Use `project.urls` instead.\n\nA URL to the repository of the project.\n\n```toml\n[tool.poetry]\n# ...\nrepository = \"https://github.com/python-poetry/poetry\"\n```\n\n### documentation\n\n**Deprecated**: Use `project.urls` instead.\n\nA URL to the documentation of the project.\n\n```toml\n[tool.poetry]\n# ...\ndocumentation = \"https://python-poetry.org/docs/\"\n```\n\n### keywords\n\n**Deprecated**: Use `project.keywords` instead.\n\nA list of keywords that the package is related to.\n\n```toml\n[tool.poetry]\n# ...\nkeywords = [\"packaging\", \"poetry\"]\n```\n\n### classifiers\n\nA list of PyPI [trove classifiers](https://pypi.org/classifiers/) that describe the project.\n\n```toml\n[tool.poetry]\n# ...\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n```\n\n{{% note %}}\nNote that Python classifiers are automatically added for you\nand are determined by your `python` requirement.\n\nIf you do not want Poetry to automatically add suitable classifiers\nbased on the `python` requirement, use `project.classifiers` instead of this setting.\n{{% /note %}}\n\n### packages\n\nA list of packages and modules to include in the final distribution.\n\nIf packages are not automatically detected, you can specify the packages you want\nto include in the final distribution.\n\n{{% note %}}\nPoetry automatically detects a single **module** or **package** whose name matches the\n[normalized](https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization)\nproject name with `-` replaced with `_`.\n\nThe detected module or package must be located either:\n\n- at the same level as the `pyproject.toml` file (flat layout), or\n- inside a `src/` directory (src layout).\n\n{{% /note %}}\n\n```toml\n[tool.poetry]\n# ...\npackages = [\n    { include = \"my_package\" },\n    { include = \"extra_package/**/*.py\" },\n]\n```\n\nIf your package is stored inside a \"lib\" directory, you must specify it:\n\n```toml\n[tool.poetry]\n# ...\npackages = [\n    { include = \"my_package\", from = \"lib\" },\n]\n```\n\nThe `to` parameter is designed to specify the relative destination path\nwhere the package will be located upon installation. This allows for\ngreater control over the organization of packages within your project's structure.\n\n```toml\n[tool.poetry]\n# ...\npackages = [\n    { include = \"my_package\", from = \"lib\", to = \"target_package\" },\n]\n```\n\nIf you want to restrict a package to a specific build format, you can specify\nit by using `format`:\n\n```toml\n[tool.poetry]\n# ...\npackages = [\n    { include = \"my_package\" },\n    { include = \"my_other_package\", format = \"sdist\" },\n]\n```\n\nFrom now on, only the `sdist` build archive will include the `my_other_package` package.\n\n{{% note %}}\nUsing `packages` disables the package auto-detection feature meaning you have to\n**explicitly** specify the \"default\" package.\n\nFor instance, if you have a package named `my_package` and you want to also include\nanother package named `extra_package`, you will need to specify `my_package` explicitly:\n\n```toml\n[tool.poetry]\n# ...\npackages = [\n    { include = \"my_package\" },\n    { include = \"extra_package\" },\n]\n```\n{{% /note %}}\n\n{{% note %}}\nPoetry is clever enough to detect Python subpackages.\n\nThus, you only have to specify the directory where your root package resides.\n{{% /note %}}\n\n### exclude and include\n\n{{% note %}}\nIf you just want to include a package or module, which is not picked up automatically,\nuse [packages]({{< relref \"#packages\" >}}) instead of `include`.\n{{% /note %}}\n\nA list of patterns that will be excluded or included in the final package.\n\n```toml\n[tool.poetry]\n# ...\nexclude = [\"my_package/excluded.py\"]\ninclude = [\"CHANGELOG.md\"]\n```\n\nYou can explicitly specify to Poetry that a set of globs should be ignored or included for the purposes of packaging.\nThe globs specified in the exclude field identify a set of files that are not included when a package is built.\n`include` has priority over `exclude`.\n\nYou can also specify the formats for which these patterns have to be included, as shown here:\n\n```toml\n[tool.poetry]\n# ...\ninclude = [\n    { path = \"tests\", format = \"sdist\" },\n    { path = \"my_package/for_sdist_and_wheel.txt\", format = [\"sdist\", \"wheel\"] }\n]\n```\n\nIf no format is specified, `include` defaults to only `sdist`.\n\nIn contrast, `exclude` defaults to both `sdist` and `wheel`.\n\n{{% warning %}}\nWhen a wheel is installed, its includes are unpacked straight into the `site-packages` directory.\nPay attention to include top level files and directories with common names like\n`CHANGELOG.md`, `LICENSE`, `tests` or `docs` only in sdists and **not** in wheels.\n{{% /warning %}}\n\nIf a VCS is being used for a package, the exclude field will be seeded with the VCS’ ignore settings (`.gitignore` for git, for example).\n\n{{% note %}}\nVCS ignore settings can be negated by adding entries in `include`; be sure to explicitly set the `format` as above.\n{{% /note %}}\n\n### dependencies and dependency groups\n\nPoetry is configured to look for dependencies on [PyPI](https://pypi.org) by default.\nOnly the name and a version string are required in this case.\n\n```toml\n[tool.poetry.dependencies]\nrequests = \"^2.13.0\"\n```\n\nIf you want to use a [private repository]({{< relref \"repositories#using-a-private-repository\" >}}),\nyou can add it to your `pyproject.toml` file, like so:\n\n```toml\n[[tool.poetry.source]]\nname = \"private\"\nurl = \"http://example.com/simple\"\n```\n\nIf you have multiple repositories configured, you can explicitly tell poetry where to look for a specific package:\n\n```toml\n[tool.poetry.dependencies]\nrequests = { version = \"^2.13.0\", source = \"private\" }\n```\n\nYou may also specify your project's compatible python versions in this section, instead of or in addition to `project.requires-python`.\n\n```toml\n[tool.poetry.dependencies]\npython = \"^3.7\"\n```\n\n{{% note %}}\nIf you specify the compatible python versions in both `tool.poetry.dependencies` and in `project.requires-python`, then Poetry will use the information in `tool.poetry.dependencies` for locking, but the python versions must be a subset of those allowed by `project.requires-python`.\n\nFor example, the following is invalid and will result in an error, because versions `4.0` and greater are allowed by `tool.poetry.dependencies`, but not by `project.requires-python`.\n\n```toml\n[project]\n# ...\nrequires-python = \">=3.8,<4.0\"\n\n[tool.poetry.dependencies]\npython = \">=3.8\" # not valid!\n```\n{{% /note %}}\n\nYou can organize your dependencies in [groups]({{< relref \"managing-dependencies#dependency-groups\" >}})\nto manage them in a more granular way.\n\n```toml\n[tool.poetry.group.test.dependencies]\npytest = \"*\"\n\n[tool.poetry.group.docs.dependencies]\nmkdocs = \"*\"\n```\n\nSee [Dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) for a more in-depth look\nat how to manage dependency groups and [Dependency specification]({{< relref \"dependency-specification\" >}})\nfor more information on other keys and specifying version ranges.\n\n### scripts\n\n{{% note %}}\n**Deprecated**: Use [`project.scripts`]({{< relref \"#scripts\" >}}) instead for `console` and `gui` scripts. Use\n`[tool.poetry.scripts]` only for scripts of type `file`.\n{{% /note %}}\n\nThis section describes the scripts or executables that will be installed when installing the package\n\n```toml\n[tool.poetry.scripts]\nmy_package_cli = 'my_package.console:run'\n```\n\nHere, we will have the `my_package_cli` script installed which will execute the `run` function in the `console` module in the `my_package` package.\n\n{{% note %}}\nWhen a script is added or updated, run `poetry install` to make them available in the project's virtualenv.\n{{% /note %}}\n\n```toml\n[tool.poetry.scripts]\nmy_executable = { reference = \"some_binary.exe\", type = \"file\" }\n```\n\nThis tells Poetry to include the specified file, relative to your project directory, in distribution builds. It will then be copied to the appropriate installation directory for your operating system when your package is installed.\n\n* On Windows the file is placed in the `Scripts/` directory.\n* On *nix system the file is placed in the `bin/` directory.\n\nIn its table form, the value of each script can contain a `reference` and `type`. The supported types are\n`console` and `file`. When the value is a string, it is inferred to be a `console` script.\n\n### extras\n\n**Deprecated**: Use `project.optional-dependencies` instead.\n\nPoetry supports extras to allow expression of:\n\n* optional dependencies, which enhance a package, but are not required; and\n* clusters of optional dependencies.\n\n```toml\n[tool.poetry]\nname = \"awesome\"\n\n[tool.poetry.dependencies]\n# These packages are mandatory and form the core of this package’s distribution.\nmandatory = \"^1.0\"\n\n# A list of all of the optional dependencies, some of which are included in the\n# below `extras`. They can be opted into by apps.\npsycopg2 = { version = \"^2.9\", optional = true }\nmysqlclient = { version = \"^1.3\", optional = true }\n\n[tool.poetry.extras]\nmysql = [\"mysqlclient\"]\npgsql = [\"psycopg2\"]\ndatabases = [\"mysqlclient\", \"psycopg2\"]\n```\n\nWhen installing packages with Poetry, you can specify extras by using the `-E|--extras` option:\n\n```bash\npoetry install --extras \"mysql pgsql\"\npoetry install -E mysql -E pgsql\n```\n\nAny extras you don't specify will be removed. Note this behavior is different from\n[optional dependency groups]({{< relref \"managing-dependencies#optional-groups\" >}}) not\nselected for installation, e.g., those not specified via `install --with`.\n\nYou can install all extras with the `--all-extras` option:\n\n```bash\npoetry install --all-extras\n```\n\n{{% note %}}\nNote that `install --extras` and the variations mentioned above (`--all-extras`, `--extras foo`, etc.) only work on dependencies defined in the current project. If you want to install extras defined by dependencies, you'll have to express that in the dependency itself:\n```toml\n[tool.poetry.dependencies]\npandas = {version=\"^2.2.1\", extras=[\"computation\", \"performance\"]}\n```\n```toml\n[tool.poetry.group.dev.dependencies]\nfastapi = {version=\"^0.92.0\", extras=[\"all\"]}\n```\n{{% /note %}}\n\nWhen installing or specifying Poetry-built packages, the extras defined in this section can be activated\nas described in [PEP 508](https://www.python.org/dev/peps/pep-0508/#extras).\n\nFor example, when installing the package using `pip`, the dependencies required by\nthe `databases` extra can be installed as shown below.\n\n```bash\npip install awesome[databases]\n```\n\n{{% note %}}\nThe dependencies specified for each `extra` must already be defined as project dependencies.\n\nDependencies listed in [dependency groups]({{< relref \"managing-dependencies#dependency-groups\" >}}) cannot be specified as extras.\n{{% /note %}}\n\n\n### plugins\n\n**Deprecated**: Use `project.entry-points` instead.\n\nPoetry supports arbitrary plugins, which are exposed as the ecosystem-standard [entry points](https://packaging.python.org/en/latest/specifications/entry-points/) and discoverable using `importlib.metadata`. This is similar to (and compatible with) the entry points feature of `setuptools`.\nThe syntax for registering a plugin is:\n\n```toml\n[tool.poetry.plugins] # Optional super table\n\n[tool.poetry.plugins.\"A\"]\nB = \"C:D\"\n```\nWhich are:\n\n- `A` - type of the plugin, for example `poetry.plugin` or `flake8.extension`\n- `B` - name of the plugin\n- `C` - python module import path\n- `D` - the entry point of the plugin (a function or class)\n\nExample (from [`poetry-plugin-export`](http://github.com/python-poetry/poetry-plugin-export)):\n\n```toml\n[tool.poetry.plugins.\"poetry.application.plugin\"]\nexport = \"poetry_plugin_export.plugins:ExportApplicationPlugin\"\n```\n\n### urls\n\n**Deprecated**: Use `project.urls` instead.\n\nIn addition to the basic urls (`homepage`, `repository` and `documentation`), you can specify\nany custom url in the `urls` section.\n\n```toml\n[tool.poetry.urls]\n\"Bug Tracker\" = \"https://github.com/python-poetry/poetry/issues\"\n```\n\nIf you publish your package on PyPI, they will appear in the `Project Links` section.\n\n### requires-poetry\n\nA constraint for the Poetry version that is required for this project.\nIf you are using a Poetry version that is not allowed by this constraint,\nan error will be raised.\n\n```toml\n[tool.poetry]\n# ...\nrequires-poetry = \">=2.0\"\n```\n\n### requires-plugins\n\nIn this section, you can specify that certain plugins are required for your project:\n\n```toml\n[tool.poetry.requires-plugins]\nmy-application-plugin = \">=1.0\"\nmy-plugin = \">=1.0,<2.0\"\n```\n\nSee [Project plugins]({{< relref \"plugins#project-plugins\" >}}) for more information.\n\n### build-constraints\n\nIn this section, you can specify additional constraints to apply when creating the build\nenvironment for a dependency. This is useful if a package does not provide wheels\n(or shall be built from source for other reasons)\nand specifies too loose build requirements (without an upper bound)\nand is not compatible with current versions of one of its build requirements.\n\nFor example, if your project depends on `some-package`, which only provides an sdist\nand defines its build requirements as `build-requires = [\"setuptools\"]`,\nbut is incompatible with `setuptools >= 78`, building the package will probably fail\nbecause per default the latest setuptools will be chosen. In this case, you can\nwork around this issue of `some-package` as follows:\n\n```toml\n[tool.poetry.build-constraints]\nsome-package = { setuptools = \"<78\" }\n```\n\nThe syntax for specifying constraints is the same as for specifying dependencies\nin the `tool.poetry` section.\n\n## Poetry and PEP-517\n\n[PEP-517](https://www.python.org/dev/peps/pep-0517/) introduces a standard way\nto define alternative build systems to build a Python project.\n\nPoetry is compliant with PEP-517, by providing a lightweight core library,\nso if you use Poetry to manage your Python project, you should reference\nit in the `build-system` section of the `pyproject.toml` file like so:\n\n```toml\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\n{{% note %}}\nWhen using the `new` or `init` command this section will be automatically added.\n{{% /note %}}\n\n{{% note %}}\nIf your `pyproject.toml` file still references `poetry` directly as a build backend,\nyou should update it to reference `poetry-core` instead.\n{{% /note %}}\n"
  },
  {
    "path": "docs/repositories.md",
    "content": "---\ntitle: \"Repositories\"\ndraft: false\ntype: docs\nlayout: \"docs\"\n\nmenu:\n  docs:\n    weight: 50\n---\n\n# Repositories\n\nPoetry supports the use of [PyPI](https://pypi.org) and private repositories for discovery of\npackages as well as for publishing your projects.\n\nBy default, Poetry is configured to use the [PyPI](https://pypi.org) repository,\nfor package installation and publishing.\n\nSo, when you add dependencies to your project, Poetry will assume they are available\non PyPI.\n\nThis represents most cases and will likely be enough for most users.\n\n### Private Repository Example\n\n#### Installing from private package sources\nBy default, Poetry discovers and installs packages from [PyPI](https://pypi.org). But, you want to\ninstall a dependency to your project for a [simple API repository](#simple-api-repository)? Let's\ndo it.\n\nFirst, [configure](#project-configuration) the [package source](#package-sources) as a [supplemental](#supplemental-package-sources) (or [explicit](#explicit-package-sources)) package source to your\nproject.\n\n```bash\npoetry source add --priority=supplemental foo https://pypi.example.org/simple/\n```\n\nThen, assuming the repository requires authentication, configure credentials for it.\n\n```bash\npoetry config http-basic.foo <username> <password>\n```\n{{% warning %}}\nIf you have completed configuring credentials and are receiving authorization failures, check for the presence of `~/.netrc`, which has been known to conflict with Poetry's configured authentication.\n{{% /warning %}}\n\n{{% warning %}}\nDepending on your system configuration, credentials might be saved in your command line history.\nMany shells do not save commands to history when they are prefixed by a space character. For more information, please refer to your shell's documentation.\n{{% /warning %}}\n\n{{% note %}}\nIf you would like to provide the password interactively, you can simply omit `<password>` in your command. And\nPoetry will prompt you to enter the credential manually.\n\n```bash\npoetry config http-basic.foo <username>\n```\n{{% /note %}}\n\nOnce this is done, you can add dependencies to your project from this source.\n\n```bash\npoetry add --source foo private-package\n```\n\n#### Publishing to a private repository\n\nGreat, now all that is left is to publish your package. Assuming you'd want to share it privately\nwith your team, you can configure the\n[Upload API](https://warehouse.pypa.io/api-reference/legacy.html#upload-api) endpoint for your\n[publishable repository](#publishable-repositories).\n\n```bash\npoetry config repositories.foo https://pypi.example.org/legacy/\n```\n\n{{% note %}}\n\nIf you need to use a different credential for your [package source](#package-sources), then it is\nrecommended to use a different name for your publishing repository.\n\n```bash\npoetry config repositories.foo-pub https://pypi.example.org/legacy/\npoetry config http-basic.foo-pub <username> <password>\n```\n\n{{% /note %}}\n\n{{% note %}}\nWhen configuring a repository using environment variables, note that correct suffixes need to be used.\n\n```bash\nexport POETRY_REPOSITORIES_FOO_URL=https://pypi.example.org/legacy/\nexport POETRY_HTTP_BASIC_FOO_USERNAME=<username>\nexport POETRY_HTTP_BASIC_FOO_PASSWORD=<password>\n```\n{{% /note %}}\n\nNow, all that is left is to build and publish your project using the\n[`publish`]({{< relref \"cli#publish\" >}}).\n\n```bash\npoetry publish --build --repository foo-pub\n```\n\n## Package Sources\n\nBy default, if you have not configured any primary source,\nPoetry is configured to use the Python ecosystem's canonical package index\n[PyPI](https://pypi.org).\nYou can alter this behavior and exclusively look up packages only from the configured\npackage sources by adding at least one primary source.\n\n{{% note %}}\n\nExcept for the implicitly configured source for [PyPI](https://pypi.org) named `PyPI`,\npackage sources are local to a project and must be configured within the project's\n[`pyproject.toml`]({{< relref \"pyproject\" >}}) file. This is **not** the same configuration used\nwhen publishing a package.\n\n{{% /note %}}\n\n{{% warning %}}\n\nPackage sources are a Poetry-specific feature and **not** included in\n[core metadata](https://packaging.python.org/en/latest/specifications/core-metadata/) produced by\nthe poetry-core build backend.\n\nConsequently, when a Poetry project is e.g., installed using Pip (as a normal package or in editable\nmode), package sources will be ignored and the dependencies in question downloaded from PyPI by\ndefault.\n\n{{% /warning %}}\n\n### Project Configuration\n\nThese package sources may be managed using the [`source`]({{< relref \"cli#source\" >}}) command for\nyour project.\n\n```bash\npoetry source add foo https://foo.bar/simple/\n```\n\n{{% note %}}\n\nIf your package source requires [credentials](#configuring-credentials) or\n[certificates](#certificates), please refer to the relevant sections below.\n\n{{% /note %}}\n\nThis will generate the following configuration snippet in your\n[`pyproject.toml`]({{< relref \"pyproject\" >}}) file.\n\n```toml\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"primary\"\n```\n\nIf `priority` is undefined, the source is considered a primary source,\nwhich disables the implicit PyPI source and takes precedence over supplemental sources.\n\nPackage sources are considered in the following order:\n1. [primary sources](#primary-package-sources) or implicit PyPI (if there are no primary sources),\n2. [supplemental sources](#supplemental-package-sources).\n\n[Explicit sources](#explicit-package-sources) are considered only for packages that explicitly [indicate their source](#package-source-constraint).\n\nWithin each priority class, package sources are considered in order of appearance in `pyproject.toml`.\n\n\n#### Primary Package Sources\n\nAll primary package sources are searched for each dependency without a [source constraint](#package-source-constraint).\nIf you configure at least one primary source, the implicit PyPI source is disabled.\n\n```bash\npoetry source add --priority=primary foo https://foo.bar/simple/\n```\n\nSources without a priority are considered primary sources, too.\n\n```bash\npoetry source add foo https://foo.bar/simple/\n```\n\n{{% warning %}}\n\nThe implicit PyPI source is disabled automatically if at least one primary source is configured.\nIf you want to use PyPI in addition to a primary source, configure it explicitly\nwith a certain priority, e.g.\n\n```bash\npoetry source add --priority=primary PyPI\n```\n\nThis way, the priority of PyPI can be set in a fine-granular way.\n\nThe equivalent specification in `pyproject.toml` is:\n\n```toml\n[[tool.poetry.source]]\nname = \"pypi\"\npriority = \"primary\"\n```\n\n**Omit the `url` when specifying PyPI explicitly.** Because PyPI is internally configured\nwith Poetry, the PyPI repository cannot be configured with a given URL. Remember, you can always use\n`poetry check` to ensure the validity of the `pyproject.toml` file.\n\n{{% /warning %}}\n\n\n#### Supplemental Package Sources\n\n*Introduced in 1.5.0*\n\nPackage sources configured as supplemental are only searched if no other (higher-priority) source yields a compatible package distribution. This is particularly convenient if the response time of the source is high and relatively few package distributions are to be fetched from this source.\n\nYou can configure a package source as a supplemental source with `priority = \"supplemental\"` in your package\nsource configuration.\n\n```bash\npoetry source add --priority=supplemental foo https://foo.bar/simple/\n```\n\nThere can be more than one supplemental package source.\n\n{{% warning %}}\n\nTake into account that someone could publish a new package to a primary source\nwhich matches a package in your supplemental source. They could coincidentally\nor intentionally replace your dependency with something you did not expect.\n\n{{% /warning %}}\n\n\n#### Explicit Package Sources\n\n*Introduced in 1.5.0*\n\nIf package sources are configured as explicit, these sources are only searched when a package configuration [explicitly indicates](#package-source-constraint) that it should be found on this package source.\n\nYou can configure a package source as an explicit source with `priority = \"explicit\"` in your package source configuration.\n\n```bash\npoetry source add --priority=explicit foo https://foo.bar/simple/\n```\n\nThere can be more than one explicit package source.\n\n{{% note %}}\nA real-world example where an explicit package source is useful, is for PyTorch GPU packages.\n\n```bash\npoetry source add --priority=explicit pytorch-gpu-src https://download.pytorch.org/whl/cu118\npoetry add --source pytorch-gpu-src torch torchvision torchaudio\n```\n{{% /note %}}\n\n#### Package Source Constraint\n\nAll package sources (including possibly supplemental sources) will be searched\nduring the package lookup process.\nThese network requests will occur for all primary sources, regardless of if the package is\nfound at one or more sources, and all supplemental sources until the package is found.\n\nIn order to limit the search for a specific package to a particular package repository, you can specify the source explicitly.\n\n```bash\npoetry add --source internal-pypi httpx\n```\n\nThis results in the following configuration in `pyproject.toml`:\n\n```toml\n[tool.poetry.dependencies]\n...\nhttpx = { version = \"^0.22\", source = \"internal-pypi\" }\n\n[[tool.poetry.source]]\nname = \"internal-pypi\"\nurl = ...\npriority = ...\n```\n\n{{% note %}}\n\nA repository that is configured to be the only source for retrieving a certain package can itself have any priority.\nIn particular, it does not need to have priority `\"explicit\"`.\nIf a repository is configured to be the source of a package, it will be the only source that is considered for that package\nand the repository priority will have no effect on the resolution.\n\n{{% /note %}}\n\n{{% note %}}\n\nPackage `source` keys are not inherited by their dependencies.\nIn particular, if `package-A` is configured to be found in `source = internal-pypi`,\nand `package-A` depends on `package-B` that is also to be found on `internal-pypi`,\nthen `package-B` needs to be configured as such in `pyproject.toml`.\nThe easiest way to achieve this is to add `package-B` with a wildcard constraint:\n\n```bash\npoetry add --source internal-pypi package-B@*\n```\n\nThis will ensure that `package-B` is searched only in the `internal-pypi` package source.\nThe version constraints on `package-B` are derived from `package-A` (and other client packages), as usual.\n\nIf you want to avoid additional main dependencies,\nyou can add `package-B` to a dedicated [dependency group]({{< relref \"managing-dependencies#dependency-groups\" >}}):\n\n```bash\npoetry add --group explicit --source internal-pypi package-B@*\n```\n\n{{% /note %}}\n\n{{% note %}}\n\nPackage source constraints are strongly suggested for all packages that are expected\nto be provided only by one specific source to avoid dependency confusion attacks.\n\n{{% /note %}}\n\n### Supported Package Sources\n\n#### Python Package Index (PyPI)\n\nPoetry interacts with [PyPI](https://pypi.org) via its\n[JSON API](https://warehouse.pypa.io/api-reference/json.html). This is used to retrieve a requested\npackage's versions, metadata, files, etc.\n\n{{% note %}}\n\nIf the package's published metadata is invalid, Poetry will download the available bdist/sdist to\ninspect it locally to identify the relevant metadata.\n\n{{% /note %}}\n\nIf you want to explicitly select a package from [PyPI](https://pypi.org) you can use the `--source`\noption with the [`add`]({{< relref \"cli#add\" >}}) command, like shown below.\n\n```bash\npoetry add --source pypi httpx@^0.22.0\n```\n\nThis will generate the following configuration snippet in your `pyproject.toml` file.\n\n```toml\nhttpx = {version = \"^0.22.0\", source = \"pypi\"}\n```\n\n{{% warning %}}\n\nThe implicit `PyPI` source will be disabled and not used for any packages\nif at least one [primary source](#primary-package-sources) is configured.\n\n{{% /warning %}}\n\n#### Simple API Repository\n\nPoetry can fetch and install package dependencies from public or private custom repositories that\nimplement the simple repository API as described in [PEP 503](https://peps.python.org/pep-0503/).\n\n{{% warning %}}\n\nWhen using sources that distribute large wheels without providing file checksum in file URLs,\nPoetry will download each candidate wheel at least once in order to generate the checksum. This can\nmanifest as long dependency resolution times when adding packages from this source.\n\n{{% /warning %}}\n\nThese package sources may be configured via the following command in your project.\n\n```bash\npoetry source add testpypi https://test.pypi.org/simple/\n```\n\n{{% note %}}\n\nNote the trailing `/simple/`. This is important when configuring\n[PEP 503](https://peps.python.org/pep-0503/) compliant package sources.\n\n{{% /note %}}\n\nIn addition to [PEP 503](https://peps.python.org/pep-0503/), Poetry can also handle simple API\nrepositories that implement [PEP 658](https://peps.python.org/pep-0658/) (*Introduced in 1.2.0*).\nThis is helpful in reducing dependency resolution time for packages from these sources as Poetry can\navoid having to download each candidate distribution, in order to determine associated metadata.\n\n{{% note %}}\n\n*Why does Poetry insist on downloading all candidate distributions for all platforms when metadata\nis not available?*\n\nThe need for this stems from the fact that Poetry's lock file is platform-agnostic. This means, in\norder to resolve dependencies for a project, Poetry needs metadata for all platform-specific\ndistributions. And when this metadata is not readily available, downloading the distribution and\ninspecting it locally is the only remaining option.\n\n{{% /note %}}\n\n#### Single Page Link Source\n\n*Introduced in 1.2.0*\n\nSome projects choose to release their binary distributions via a single page link source that\npartially follows the structure of a package page in [PEP 503](https://peps.python.org/pep-0503/).\n\nThese package sources may be configured via the following command in your project.\n\n```bash\npoetry source add jax https://storage.googleapis.com/jax-releases/jax_releases.html\n```\n\n{{% note %}}\n\nAll caveats regarding slower resolution times described for simple API repositories do apply here as\nwell.\n\n{{% /note %}}\n\n\n## Publishable Repositories\n\nPoetry treats repositories to which you publish packages as user-specific and not project-specific\nconfiguration unlike [package sources](#package-sources). Poetry, today, only supports the\n[Legacy Upload API](https://warehouse.pypa.io/api-reference/legacy.html#upload-api) when publishing\nyour project.\n\nThese are configured using the [`config`]({{< relref \"cli#config\" >}}) command, under the\n`repositories` key.\n\n```bash\npoetry config repositories.testpypi https://test.pypi.org/legacy/\n```\n\n{{% note %}}\n\n[Legacy Upload API](https://warehouse.pypa.io/api-reference/legacy.html#upload-api) URLs are\ntypically different to the same one provided by the repository for the simple API. You'll note that\nin the example of [Test PyPI](https://test.pypi.org/), both the host (`test.pypi.org`) as\nwell as the path (`/legacy`) are different to its simple API (`https://test.pypi.org/simple`).\n\n{{% /note %}}\n\n## Configuring Credentials\n\nIf you want to store your credentials for a specific repository, you can do so easily:\n\n```bash\npoetry config http-basic.foo <username> <password>\n```\n\nIf you do not specify the password, you will be prompted to write it.\n\n{{% note %}}\n\nTo publish to PyPI, you can set your credentials for the repository named `pypi`.\n\nNote that it is recommended to use [API tokens](https://pypi.org/help/#apitoken)\nwhen uploading packages to PyPI.\nOnce you have created a new token, you can tell Poetry to use it:\n\n```bash\npoetry config pypi-token.pypi <my-token>\n```\nIf you have configured **testpypi** as a [Publishable Repository](#publishable-repositories), the token can be set using\n```bash\npoetry config pypi-token.testpypi <your-token>\n```\n\nIf you still want to use your username and password, you can do so with the following\ncall to `config`.\n\n```bash\npoetry config http-basic.pypi <username> <password>\n```\n\n{{% /note %}}\n\nYou can also specify the username and password when using the `publish` command\nwith the `--username` and `--password` options.\n\nIf a system keyring is available and supported, the password is stored to and retrieved from the keyring. In the above example, the credential will be stored using the name `poetry-repository-pypi`. If access to keyring fails or is unsupported, this will fall back to writing the password to the `auth.toml` file along with the username.\n\nKeyring support is enabled using the [keyring library](https://pypi.org/project/keyring/). For more information on supported backends refer to the [library documentation](https://keyring.readthedocs.io/en/latest/?badge=latest).\n\nIf you do not want to use the keyring, you can tell Poetry to disable it and store the credentials in plaintext config files:\n\n```bash\npoetry config keyring.enabled false\n```\n\n{{% note %}}\n\nPoetry will fall back to Pip style use of keyring so that backends like\nMicrosoft's [artifacts-keyring](https://pypi.org/project/artifacts-keyring/) get a chance to retrieve\nvalid credentials. It will need to be properly installed into Poetry's virtualenv,\npreferably by installing a plugin.\n\n{{% /note %}}\n\nAlternatively, you can use environment variables to provide the credentials:\n\n```bash\nexport POETRY_PYPI_TOKEN_FOO=my-token\nexport POETRY_HTTP_BASIC_FOO_USERNAME=<username>\nexport POETRY_HTTP_BASIC_FOO_PASSWORD=<password>\n```\n\nwhere `FOO` is the name of the repository in uppercase (e.g. `PYPI`).\nSee [Using environment variables]({{< relref \"configuration#using-environment-variables\" >}}) for more information\non how to configure Poetry with environment variables.\n\nIf your password starts with a dash (e.g., randomly generated tokens in a CI environment), it will be parsed as a\ncommand line option instead of a password.\nYou can prevent this by adding double dashes to prevent any following argument from being parsed as an option.\n\n```bash\npoetry config -- http-basic.pypi myUsername -myPasswordStartingWithDash\n```\n\n{{% note %}}\nIn some cases like that of [Gemfury](https://gemfury.com/help/errors/repo-url-password/) repositories, it might be\nrequired to set an empty password. This is supported by Poetry.\n\n```bash\npoetry config http-basic.foo <TOKEN> \"\"\n```\n\n**Note:** Empty usernames are discouraged. However, Poetry will honor them if a password is configured without it. This\nis unfortunately commonplace practice, while not best practice, for private indices that use tokens. When a password is\nstored into the system keyring with an empty username, Poetry will use a literal `__poetry_source_empty_username__` as\nthe username to circumvent [keyring#687](https://github.com/jaraco/keyring/pull/687).\n{{% /note %}}\n\n## Certificates\n\n### Custom certificate authority and mutual TLS authentication\n\nPoetry supports repositories that are secured by a custom certificate authority as well as those that require\ncertificate-based client authentication.  The following will configure the \"foo\" repository to validate the repository's\ncertificate using a custom certificate authority and use a client certificate (note that these config variables do not\nboth need to be set):\n\n```bash\npoetry config certificates.foo.cert /path/to/ca.pem\npoetry config certificates.foo.client-cert /path/to/client.pem\n```\n\n{{% note %}}\nThe value of `certificates.<repository>.cert` can be set to `false` if certificate verification is\nrequired to be skipped. This is useful for cases where a package source with self-signed certificates\nis used.\n\n```bash\npoetry config certificates.foo.cert false\n```\n\n{{% warning %}}\nDisabling certificate verification is not recommended as it does not conform to security\nbest practices.\n{{% /warning %}}\n{{% /note %}}\n\n## Caches\n\nPoetry employs multiple caches for package sources in order to improve user experience and avoid duplicate network\nrequests.\n\nThe first level cache is a [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)\nheader-based cache for almost all HTTP requests.\n\nFurther, every HTTP backed package source caches metadata associated with a package once it is fetched or generated.\nAdditionally, downloaded files (package distributions) are also cached.\n\n## Debugging Issues\nIf you encounter issues with package sources, one of the simplest steps you might take to debug an issue is rerunning\nyour command with the `--no-cache` flag.\n\n```bash\npoetry --no-cache add pycowsay\n```\n\nIf this solves your issue, you can consider clearing your cache using the [`cache`]({{< relref \"cli#cache-clear\" >}})\ncommand.\n\nAlternatively, you could also consider enabling very verbose logging `-vvv` along with the `--no-cache` to see network\nrequests being made in the logs.\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"poetry\"\nversion = \"2.3.2\"\ndescription = \"Python dependency management and packaging made easy.\"\nrequires-python = \">=3.10,<4.0\"\ndependencies = [\n    \"poetry-core (==2.3.1)\",\n    \"build (>=1.2.1,<2.0.0)\",\n    \"cachecontrol[filecache] (>=0.14.0,<0.15.0)\",\n    \"cleo (>=2.1.0,<3.0.0)\",\n    \"dulwich (>=0.25.0,<2)\",\n    \"fastjsonschema (>=2.18.0,<3.0.0)\",\n    \"installer (>=0.7.0,<0.8.0)\",\n    \"keyring (>=25.1.0,<26.0.0)\",\n    # packaging uses calver, so version is unclamped\n    \"packaging (>=24.2)\",  # PEP 639 support was added in 24.2\n    \"pkginfo (>=1.12,<2.0)\",\n    \"platformdirs (>=3.0.0,<5)\",\n    \"pyproject-hooks (>=1.0.0,<2.0.0)\",\n    \"requests (>=2.26,<3.0)\",\n    \"requests-toolbelt (>=1.0.0,<2.0.0)\",\n    \"shellingham (>=1.5,<2.0)\",\n    \"tomli (>=2.0.1,<3.0.0) ; python_version < '3.11'\",\n    \"tomlkit (>=0.11.4,<1.0.0)\",\n    # trove-classifiers uses calver, so version is unclamped\n    \"trove-classifiers (>=2022.5.19)\",\n    \"virtualenv (>=20.26.6)\",\n    \"xattr (>=1.0.0,<2.0.0) ; sys_platform == 'darwin'\",\n    \"findpython (>=0.6.2,<0.8.0)\",\n    # pbs-installer uses calver, so version is unclamped\n    \"pbs-installer[download,install] (>=2025.6.10)\",\n]\nauthors = [\n    { name = \"Sébastien Eustace\", email = \"sebastien@eustace.io\" }\n]\nmaintainers = [\n    { name = \"Arun Babu Neelicattu\", email = \"arun.neelicattu@gmail.com\" },\n    { name = \"Bjorn Neergaard\", email = \"bjorn@neersighted.com\" },\n    { name = \"Branch Vincent\", email = \"branchevincent@gmail.com\" },\n    { name = \"Randy Döring\", email = \"radoering.poetry@gmail.com\" },\n    { name = \"Steph Samson\", email = \"hello@stephsamson.com\" },\n    { name = \"finswimmer\", email = \"finswimmer77@gmail.com\" },\n    { name = \"Bartosz Sokorski\", email = \"b.sokorski@gmail.com\" },\n]\nlicense = \"MIT\"\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n# classifiers are dynamic because we want to create Python classifiers automatically\ndynamic = [ \"classifiers\" ]\n\n[project.urls]\nHomepage = \"https://python-poetry.org/\"\nChangelog = \"https://python-poetry.org/history/\"\nRepository = \"https://github.com/python-poetry/poetry\"\nDocumentation = \"https://python-poetry.org/docs\"\n\n[project.scripts]\npoetry = \"poetry.console.application:main\"\n\n\n[tool.poetry]\nrequires-poetry = \">=2.0\"\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\ninclude = [{ path = \"tests\", format = \"sdist\" }]\n\n[tool.poetry.group.dev.dependencies]\npre-commit = \">=2.10\"\n\n[tool.poetry.group.test.dependencies]\ncoverage = \">=7.2.0\"\ndeepdiff = \">=6.3\"\nresponses = \">=0.25.8\"\njaraco-classes = \">=3.3.1\"\npytest = \">=8.0\"\npytest-cov = \">=4.0\"\npytest-mock = \">=3.9\"\npytest-randomly = \">=3.12\"\npytest-xdist = { version = \">=3.1\", extras = [\"psutil\"] }\n\n[tool.poetry.group.typing.dependencies]\nmypy = \">=1.8.0\"\ntypes-requests = \">=2.28.8\"\n\n# only used in github actions\n[tool.poetry.group.github-actions]\noptional = true\n[tool.poetry.group.github-actions.dependencies]\npytest-github-actions-annotate-failures = \"^0.1.7\"\n\n\n[build-system]\nrequires = [\"poetry-core>=2.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n\n[tool.ruff]\nextend-exclude = [\n    \"docs/*\",\n    # External to the project's coding standards\n    \"tests/fixtures/git/*\",\n    \"tests/fixtures/project_with_setup*/*\",\n]\nfix = true\nline-length = 88\n\n[tool.ruff.lint]\nextend-select = [\n    \"B\",   # flake8-bugbear\n    \"C4\",  # flake8-comprehensions\n    \"ERA\", # flake8-eradicate/eradicate\n    \"I\",   # isort\n    \"N\",   # pep8-naming\n    \"PIE\", # flake8-pie\n    \"PGH\", # pygrep\n    \"RUF\", # ruff checks\n    \"SIM\", # flake8-simplify\n    \"T20\", # flake8-print\n    \"TC\",  # flake8-type-checking\n    \"TID\", # flake8-tidy-imports\n    \"UP\",  # pyupgrade\n]\nignore = [\n    \"B904\", # use 'raise ... from err'\n    \"B905\", # use explicit 'strict=' parameter with 'zip()'\n]\nextend-safe-fixes = [\n    \"TC\",  # move import from and to TYPE_CHECKING blocks\n]\nunfixable = [\n    \"ERA\", # do not autoremove commented out code\n]\n\n[tool.ruff.lint.flake8-tidy-imports]\nban-relative-imports = \"all\"\n\n[tool.ruff.lint.isort]\nforce-single-line = true\nlines-between-types = 1\nlines-after-imports = 2\nknown-first-party = [\"poetry\"]\nknown-third-party = [\"poetry.core\"]\nrequired-imports = [\"from __future__ import annotations\"]\n\n\n[tool.mypy]\nfiles = \"src, tests\"\nmypy_path = \"src\"\nnamespace_packages = true\nexplicit_package_bases = true\nstrict = true\nenable_error_code = [\n    \"ignore-without-code\",\n    \"redundant-expr\",\n    \"truthy-bool\",\n]\nexclude = [\n    \"tests/fixtures\",\n    \"tests/masonry/builders/fixtures\",\n    \"tests/utils/fixtures\",\n]\n\n# use of importlib-metadata backport makes it impossible to satisfy mypy\n# without some ignores: but we get different sets of ignores at different\n# python versions.\n[[tool.mypy.overrides]]\nmodule = [\n    'poetry.repositories.installed_repository',\n    'tests.console.commands.self.test_show_plugins',\n    'tests.repositories.test_installed_repository',\n    'tests.helpers',\n]\nwarn_unused_ignores = false\n\n[[tool.mypy.overrides]]\nmodule = [\n    'deepdiff.*',\n    'fastjsonschema.*',\n    'findpython.*',\n    'requests_toolbelt.*',\n    'shellingham.*',\n    'virtualenv.*',\n    'xattr.*',\n]\nignore_missing_imports = true\n\n\n[tool.pytest.ini_options]\naddopts = [ \"-n\", \"logical\", \"-ra\", \"--strict-config\", \"--strict-markers\" ]\ntestpaths = [\"tests\"]\nmarkers = [\n    \"network: mark tests that require internet access\",\n    \"skip_git_mock: mark tests that should not auto-apply git_mock\"\n]\nlog_cli_level = \"INFO\"\nxfail_strict = true\n\n\n[tool.coverage.report]\nexclude_also = [\n    \"if TYPE_CHECKING:\"\n]\n\n\n[tool.repo-review]\nignore = [\n    \"PY007\",\n    \"PP302\",\n    \"PP309\",\n    \"GH101\",\n    \"GH212\",\n    \"PC111\",\n    \"PC140\",\n    \"PC160\",\n    \"PC170\",\n    \"PC180\",\n    \"PC180\",\n    \"PC901\",\n    \"MY103\",\n    \"RTD100\",\n]\n"
  },
  {
    "path": "src/poetry/__main__.py",
    "content": "from __future__ import annotations\n\nimport sys\n\n\nif __name__ == \"__main__\":\n    from poetry.console.application import main\n\n    sys.exit(main())\n"
  },
  {
    "path": "src/poetry/__version__.py",
    "content": "from __future__ import annotations\n\nfrom importlib import metadata\n\n\n__version__ = metadata.version(\"poetry\")\n"
  },
  {
    "path": "src/poetry/config/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/config/config.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport json\nimport logging\nimport os\nimport re\n\nfrom copy import deepcopy\nfrom json import JSONDecodeError\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.config.dict_config_source import DictConfigSource\nfrom poetry.config.file_config_source import FileConfigSource\nfrom poetry.locations import CONFIG_DIR\nfrom poetry.locations import DEFAULT_CACHE_DIR\nfrom poetry.locations import data_dir\nfrom poetry.toml import TOMLFile\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Mapping\n    from collections.abc import Sequence\n\n    from poetry.config.config_source import ConfigSource\n\n\ndef boolean_validator(val: str) -> bool:\n    return val in {\"true\", \"false\", \"1\", \"0\"}\n\n\ndef boolean_normalizer(val: str) -> bool:\n    return val.lower() in [\"true\", \"1\"]\n\n\ndef int_normalizer(val: str) -> int:\n    return int(val)\n\n\ndef build_config_setting_validator(val: str) -> bool:\n    try:\n        value = build_config_setting_normalizer(val)\n    except JSONDecodeError:\n        return False\n\n    if not isinstance(value, dict):\n        return False\n\n    for key, item in value.items():\n        # keys should be string\n        if not isinstance(key, str):\n            return False\n\n        # items are allowed to be a string\n        if isinstance(item, str):\n            continue\n\n        # list items should only contain strings\n        is_valid_list = isinstance(item, list) and all(isinstance(i, str) for i in item)\n        if not is_valid_list:\n            return False\n\n    return True\n\n\ndef build_config_setting_normalizer(val: str) -> Mapping[str, str | Sequence[str]]:\n    value: Mapping[str, str | Sequence[str]] = json.loads(val)\n    return value\n\n\n@dataclasses.dataclass\nclass PackageFilterPolicy:\n    policy: dataclasses.InitVar[str | list[str] | None]\n    packages: list[str] = dataclasses.field(init=False)\n\n    def __post_init__(self, policy: str | list[str] | None) -> None:\n        if not policy:\n            policy = []\n        elif isinstance(policy, str):\n            policy = self.normalize(policy)\n        self.packages = policy\n\n    def allows(self, package_name: str) -> bool:\n        if \":all:\" in self.packages:\n            return False\n\n        return (\n            not self.packages\n            or \":none:\" in self.packages\n            or canonicalize_name(package_name) not in self.packages\n        )\n\n    def has_exact_package(self, package_name: str) -> bool:\n        return canonicalize_name(package_name) in self.packages\n\n    @classmethod\n    def is_reserved(cls, name: str) -> bool:\n        return bool(re.match(r\":(all|none):\", name))\n\n    @classmethod\n    def normalize(cls, policy: str) -> list[str]:\n        if boolean_validator(policy):\n            if boolean_normalizer(policy):\n                return [\":all:\"]\n            else:\n                return [\":none:\"]\n\n        return list(\n            {\n                name.strip() if cls.is_reserved(name) else canonicalize_name(name)\n                for name in policy.strip().split(\",\")\n                if name\n            }\n        )\n\n    @classmethod\n    def validator(cls, policy: str) -> bool:\n        if boolean_validator(policy):\n            return True\n\n        names = policy.strip().split(\",\")\n\n        for name in names:\n            if (\n                not name\n                or (cls.is_reserved(name) and len(names) == 1)\n                or re.match(r\"^[a-zA-Z\\d_-]+$\", name)\n            ):\n                continue\n            return False\n\n        return True\n\n\nlogger = logging.getLogger(__name__)\n\n_default_config: Config | None = None\n\n\nclass Config:\n    default_config: ClassVar[dict[str, Any]] = {\n        \"cache-dir\": str(DEFAULT_CACHE_DIR),\n        \"data-dir\": str(data_dir()),\n        \"virtualenvs\": {\n            \"create\": True,\n            \"in-project\": None,\n            \"path\": os.path.join(\"{cache-dir}\", \"virtualenvs\"),\n            \"options\": {\n                \"always-copy\": False,\n                \"system-site-packages\": False,\n                \"no-pip\": False,\n            },\n            \"use-poetry-python\": False,\n            \"prompt\": \"{project_name}-py{python_version}\",\n        },\n        \"requests\": {\n            \"max-retries\": 0,\n        },\n        \"installer\": {\n            \"re-resolve\": False,\n            \"parallel\": True,\n            \"max-workers\": None,\n            \"no-binary\": None,\n            \"only-binary\": None,\n            \"build-config-settings\": {},\n        },\n        \"python\": {\"installation-dir\": os.path.join(\"{data-dir}\", \"python\")},\n        \"solver\": {\n            \"lazy-wheel\": True,\n        },\n        \"system-git-client\": False,\n        \"keyring\": {\n            \"enabled\": True,\n        },\n    }\n\n    def __init__(self, use_environment: bool = True) -> None:\n        self._config = deepcopy(self.default_config)\n        self._use_environment = use_environment\n        self._config_source: ConfigSource = DictConfigSource()\n        self._auth_config_source: ConfigSource = DictConfigSource()\n\n    @property\n    def config(self) -> dict[str, Any]:\n        return self._config\n\n    @property\n    def config_source(self) -> ConfigSource:\n        return self._config_source\n\n    @property\n    def auth_config_source(self) -> ConfigSource:\n        return self._auth_config_source\n\n    def set_config_source(self, config_source: ConfigSource) -> Config:\n        self._config_source = config_source\n\n        return self\n\n    def set_auth_config_source(self, config_source: ConfigSource) -> Config:\n        self._auth_config_source = config_source\n\n        return self\n\n    def merge(self, config: dict[str, Any]) -> None:\n        from poetry.utils.helpers import merge_dicts\n\n        merge_dicts(self._config, config)\n\n    def all(self) -> dict[str, Any]:\n        def _all(config: dict[str, Any], parent_key: str = \"\") -> dict[str, Any]:\n            all_ = {}\n\n            for key in config:\n                value = self.get(parent_key + key)\n                if isinstance(value, dict):\n                    if parent_key != \"\":\n                        current_parent = parent_key + key + \".\"\n                    else:\n                        current_parent = key + \".\"\n                    all_[key] = _all(config[key], parent_key=current_parent)\n                    continue\n\n                all_[key] = value\n\n            return all_\n\n        return _all(self.config)\n\n    def raw(self) -> dict[str, Any]:\n        return self._config\n\n    @staticmethod\n    def _get_environment_repositories() -> dict[str, dict[str, str]]:\n        repositories = {}\n        pattern = re.compile(r\"POETRY_REPOSITORIES_(?P<name>[A-Z_]+)_URL\")\n\n        for env_key in os.environ:\n            match = pattern.match(env_key)\n            if match:\n                repositories[match.group(\"name\").lower().replace(\"_\", \"-\")] = {\n                    \"url\": os.environ[env_key]\n                }\n\n        return repositories\n\n    @staticmethod\n    def _get_environment_build_config_settings() -> Mapping[\n        NormalizedName, Mapping[str, str | Sequence[str]]\n    ]:\n        build_config_settings = {}\n        pattern = re.compile(r\"POETRY_INSTALLER_BUILD_CONFIG_SETTINGS_(?P<name>[^.]+)\")\n\n        for env_key in os.environ:\n            if match := pattern.match(env_key):\n                if not build_config_setting_validator(os.environ[env_key]):\n                    logger.debug(\n                        \"Invalid value set for environment variable %s\", env_key\n                    )\n                    continue\n                build_config_settings[canonicalize_name(match.group(\"name\"))] = (\n                    build_config_setting_normalizer(os.environ[env_key])\n                )\n\n        return build_config_settings\n\n    @property\n    def repository_cache_directory(self) -> Path:\n        return Path(self.get(\"cache-dir\")).expanduser() / \"cache\" / \"repositories\"\n\n    @property\n    def artifacts_cache_directory(self) -> Path:\n        return Path(self.get(\"cache-dir\")).expanduser() / \"artifacts\"\n\n    @property\n    def virtualenvs_path(self) -> Path:\n        path = self.get(\"virtualenvs.path\")\n        if path is None:\n            path = Path(self.get(\"cache-dir\")) / \"virtualenvs\"\n        return Path(path).expanduser()\n\n    @property\n    def python_installation_dir(self) -> Path:\n        path = self.get(\"python.installation-dir\")\n        if path is None:\n            path = Path(self.get(\"data-dir\")) / \"python\"\n        return Path(path).expanduser()\n\n    @property\n    def installer_max_workers(self) -> int:\n        # This should be directly handled by ThreadPoolExecutor\n        # however, on some systems the number of CPUs cannot be determined\n        # (it raises a NotImplementedError), so, in this case, we assume\n        # that the system only has one CPU.\n        try:\n            default_max_workers = (os.cpu_count() or 1) + 4\n        except NotImplementedError:\n            default_max_workers = 5\n\n        desired_max_workers = self.get(\"installer.max-workers\")\n        if desired_max_workers is None:\n            return default_max_workers\n        return min(default_max_workers, int(desired_max_workers))\n\n    def get(self, setting_name: str, default: Any = None) -> Any:\n        \"\"\"\n        Retrieve a setting value.\n        \"\"\"\n        keys = setting_name.split(\".\")\n        build_config_settings: Mapping[\n            NormalizedName, Mapping[str, str | Sequence[str]]\n        ] = {}\n\n        # Looking in the environment if the setting\n        # is set via a POETRY_* environment variable\n        if self._use_environment:\n            if setting_name == \"repositories\":\n                # repositories setting is special for now\n                repositories = self._get_environment_repositories()\n                if repositories:\n                    return repositories\n\n            build_config_settings_key = \"installer.build-config-settings\"\n            if setting_name == build_config_settings_key or setting_name.startswith(\n                f\"{build_config_settings_key}.\"\n            ):\n                build_config_settings = self._get_environment_build_config_settings()\n            else:\n                env = \"POETRY_\" + \"_\".join(k.upper().replace(\"-\", \"_\") for k in keys)\n                env_value = os.getenv(env)\n                if env_value is not None:\n                    return self.process(self._get_normalizer(setting_name)(env_value))\n\n        value = self._config\n\n        # merge installer build config settings from the environment\n        for package_name in build_config_settings:\n            value[\"installer\"][\"build-config-settings\"][package_name] = (\n                build_config_settings[package_name]\n            )\n\n        for key in keys:\n            if key not in value:\n                return self.process(default)\n\n            value = value[key]\n\n        if self._use_environment and isinstance(value, dict):\n            # this is a configuration table, it is likely that we missed env vars\n            # in order to capture them recurse, eg: virtualenvs.options\n            return {k: self.get(f\"{setting_name}.{k}\") for k in value}\n\n        return self.process(value)\n\n    def process(self, value: Any) -> Any:\n        if not isinstance(value, str):\n            return value\n\n        def resolve_from_config(match: re.Match[str]) -> Any:\n            key = match.group(1)\n            config_value = self.get(key)\n            if config_value:\n                return config_value\n\n            # The key doesn't exist in the config but might be resolved later,\n            # so we keep it as a format variable.\n            return f\"{{{key}}}\"\n\n        return re.sub(r\"{(.+?)}\", resolve_from_config, value)\n\n    @staticmethod\n    def _get_normalizer(name: str) -> Callable[[str], Any]:\n        if name in {\n            \"virtualenvs.create\",\n            \"virtualenvs.in-project\",\n            \"virtualenvs.options.always-copy\",\n            \"virtualenvs.options.no-pip\",\n            \"virtualenvs.options.system-site-packages\",\n            \"virtualenvs.use-poetry-python\",\n            \"installer.re-resolve\",\n            \"installer.parallel\",\n            \"solver.lazy-wheel\",\n            \"system-git-client\",\n            \"keyring.enabled\",\n        }:\n            return boolean_normalizer\n\n        if name == \"virtualenvs.path\":\n            return lambda val: str(Path(val))\n\n        if name in {\n            \"installer.max-workers\",\n            \"requests.max-retries\",\n        }:\n            return int_normalizer\n\n        if name in [\"installer.no-binary\", \"installer.only-binary\"]:\n            return PackageFilterPolicy.normalize\n\n        if name.startswith(\"installer.build-config-settings.\"):\n            return build_config_setting_normalizer\n\n        return lambda val: val\n\n    @classmethod\n    def create(cls, reload: bool = False) -> Config:\n        global _default_config\n\n        if _default_config is None or reload:\n            _default_config = cls()\n\n            # Load global config\n            config_file = TOMLFile(CONFIG_DIR / \"config.toml\")\n            if config_file.exists():\n                logger.debug(\"Loading configuration file %s\", config_file.path)\n                _default_config.merge(config_file.read())\n\n            _default_config.set_config_source(FileConfigSource(config_file))\n\n            # Load global auth config\n            auth_config_file = TOMLFile(CONFIG_DIR / \"auth.toml\")\n            if auth_config_file.exists():\n                logger.debug(\"Loading configuration file %s\", auth_config_file.path)\n                _default_config.merge(auth_config_file.read())\n\n            _default_config.set_auth_config_source(FileConfigSource(auth_config_file))\n\n        return _default_config\n"
  },
  {
    "path": "src/poetry/config/config_source.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport json\n\nfrom abc import ABC\nfrom abc import abstractmethod\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom cleo.io.null_io import NullIO\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n\nUNSET = object()\n\n\nclass PropertyNotFoundError(ValueError):\n    pass\n\n\nclass ConfigSource(ABC):\n    @abstractmethod\n    def get_property(self, key: str) -> Any: ...\n\n    @abstractmethod\n    def add_property(self, key: str, value: Any) -> None: ...\n\n    @abstractmethod\n    def remove_property(self, key: str) -> None: ...\n\n\n@dataclasses.dataclass\nclass ConfigSourceMigration:\n    old_key: str\n    new_key: str | None\n    value_migration: dict[Any, Any] = dataclasses.field(default_factory=dict)\n\n    def dry_run(self, config_source: ConfigSource, io: IO | None = None) -> bool:\n        io = io or NullIO()\n\n        try:\n            old_value = config_source.get_property(self.old_key)\n        except PropertyNotFoundError:\n            return False\n\n        new_value = (\n            self.value_migration[old_value] if self.value_migration else old_value\n        )\n\n        msg = f\"<c1>{self.old_key}</c1> = <c2>{json.dumps(old_value)}</c2>\"\n\n        if self.new_key is not None and new_value is not UNSET:\n            msg += f\" -> <c1>{self.new_key}</c1> = <c2>{json.dumps(new_value)}</c2>\"\n        elif self.new_key is None:\n            msg += \" -> <c1>Removed from config</c1>\"\n        elif self.new_key and new_value is UNSET:\n            msg += f\" -> <c1>{self.new_key}</c1> = <c2>Not explicit set</c2>\"\n\n        io.write_line(msg)\n\n        return True\n\n    def apply(self, config_source: ConfigSource) -> None:\n        try:\n            old_value = config_source.get_property(self.old_key)\n        except PropertyNotFoundError:\n            return\n\n        new_value = (\n            self.value_migration[old_value] if self.value_migration else old_value\n        )\n\n        config_source.remove_property(self.old_key)\n\n        if self.new_key is not None and new_value is not UNSET:\n            config_source.add_property(self.new_key, new_value)\n\n\ndef drop_empty_config_category(\n    keys: list[str], config: dict[Any, Any]\n) -> dict[Any, Any]:\n    config_ = {}\n\n    for key, value in config.items():\n        if not keys or key != keys[0]:\n            config_[key] = value\n            continue\n        if keys and key == keys[0]:\n            if isinstance(value, dict):\n                value = drop_empty_config_category(keys[1:], value)\n\n            if value != {}:\n                config_[key] = value\n\n    return config_\n"
  },
  {
    "path": "src/poetry/config/dict_config_source.py",
    "content": "from __future__ import annotations\n\nfrom typing import Any\n\nfrom poetry.config.config_source import ConfigSource\nfrom poetry.config.config_source import PropertyNotFoundError\n\n\nclass DictConfigSource(ConfigSource):\n    def __init__(self) -> None:\n        self._config: dict[str, Any] = {}\n\n    @property\n    def config(self) -> dict[str, Any]:\n        return self._config\n\n    def get_property(self, key: str) -> Any:\n        keys = key.split(\".\")\n        config = self._config\n\n        for i, key in enumerate(keys):\n            if key not in config:\n                raise PropertyNotFoundError(f\"Key {'.'.join(keys)} not in config\")\n\n            if i == len(keys) - 1:\n                return config[key]\n\n            config = config[key]\n\n    def add_property(self, key: str, value: Any) -> None:\n        keys = key.split(\".\")\n        config = self._config\n\n        for i, key in enumerate(keys):\n            if key not in config and i < len(keys) - 1:\n                config[key] = {}\n\n            if i == len(keys) - 1:\n                config[key] = value\n                break\n\n            config = config[key]\n\n    def remove_property(self, key: str) -> None:\n        keys = key.split(\".\")\n\n        config = self._config\n        for i, key in enumerate(keys):\n            if key not in config:\n                return\n\n            if i == len(keys) - 1:\n                del config[key]\n\n                break\n\n            config = config[key]\n"
  },
  {
    "path": "src/poetry/config/file_config_source.py",
    "content": "from __future__ import annotations\n\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom tomlkit import document\nfrom tomlkit import table\n\nfrom poetry.config.config_source import ConfigSource\nfrom poetry.config.config_source import PropertyNotFoundError\nfrom poetry.config.config_source import drop_empty_config_category\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from tomlkit.toml_document import TOMLDocument\n\n    from poetry.toml.file import TOMLFile\n\n\nclass FileConfigSource(ConfigSource):\n    def __init__(self, file: TOMLFile) -> None:\n        self._file = file\n\n    @property\n    def name(self) -> str:\n        return str(self._file.path)\n\n    @property\n    def file(self) -> TOMLFile:\n        return self._file\n\n    def get_property(self, key: str) -> Any:\n        keys = key.split(\".\")\n\n        config = self.file.read() if self.file.exists() else {}\n\n        for i, key in enumerate(keys):\n            if key not in config:\n                raise PropertyNotFoundError(f\"Key {'.'.join(keys)} not in config\")\n\n            if i == len(keys) - 1:\n                return config[key]\n\n            config = config[key]\n\n    def add_property(self, key: str, value: Any) -> None:\n        with self.secure() as toml:\n            config: dict[str, Any] = toml\n            keys = key.split(\".\")\n\n            for i, key in enumerate(keys):\n                if key not in config and i < len(keys) - 1:\n                    config[key] = table()\n\n                if i == len(keys) - 1:\n                    config[key] = value\n                    break\n\n                config = config[key]\n\n    def remove_property(self, key: str) -> None:\n        with self.secure() as toml:\n            config: dict[str, Any] = toml\n            keys = key.split(\".\")\n\n            current_config = config\n            for i, key in enumerate(keys):\n                if key not in current_config:\n                    return\n\n                if i == len(keys) - 1:\n                    del current_config[key]\n\n                    break\n\n                current_config = current_config[key]\n\n            current_config = drop_empty_config_category(keys=keys[:-1], config=config)\n            config.clear()\n            config.update(current_config)\n\n    @contextmanager\n    def secure(self) -> Iterator[TOMLDocument]:\n        if self.file.exists():\n            initial_config = self.file.read()\n            config = self.file.read()\n        else:\n            initial_config = document()\n            config = document()\n\n        new_file = not self.file.exists()\n\n        yield config\n\n        try:\n            # Ensuring the file is only readable and writable\n            # by the current user\n            mode = 0o600\n\n            if new_file:\n                self.file.path.touch(mode=mode)\n\n            self.file.write(config)\n        except Exception:\n            self.file.write(initial_config)\n\n            raise\n"
  },
  {
    "path": "src/poetry/config/source.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.repositories.repository_pool import Priority\n\n\nif TYPE_CHECKING:\n    from tomlkit.items import Table\n\n\n@dataclasses.dataclass(order=True, eq=True)\nclass Source:\n    name: str\n    url: str = \"\"\n    priority: Priority = (\n        Priority.PRIMARY\n    )  # cheating in annotation: str will be converted to Priority in __post_init__\n\n    def __post_init__(self) -> None:\n        if isinstance(self.priority, str):\n            self.priority = Priority[self.priority.upper()]\n\n    def to_dict(self) -> dict[str, str | bool]:\n        return dataclasses.asdict(\n            self,\n            dict_factory=lambda x: {\n                k: v if not isinstance(v, Priority) else v.name.lower()\n                for (k, v) in x\n                if v\n            },\n        )\n\n    def to_toml_table(self) -> Table:\n        from tomlkit import nl\n        from tomlkit import table\n\n        source_table: Table = table()\n        for key, value in self.to_dict().items():\n            source_table.add(key, value)\n        source_table.add(nl())\n        return source_table\n"
  },
  {
    "path": "src/poetry/console/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/application.py",
    "content": "from __future__ import annotations\n\nimport argparse\nimport logging\n\nfrom contextlib import suppress\nfrom importlib import import_module\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import cast\n\nfrom cleo._utils import find_similar_names\nfrom cleo.application import Application as BaseApplication\nfrom cleo.events.console_command_event import ConsoleCommandEvent\nfrom cleo.events.console_events import COMMAND\nfrom cleo.events.event_dispatcher import EventDispatcher\nfrom cleo.exceptions import CleoCommandNotFoundError\nfrom cleo.exceptions import CleoError\nfrom cleo.formatters.style import Style\nfrom cleo.io.inputs.argv_input import ArgvInput\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.command_loader import CommandLoader\nfrom poetry.console.commands.command import Command\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.helpers import directory\nfrom poetry.utils.helpers import ensure_path\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from cleo.events.event import Event\n    from cleo.io.inputs.definition import Definition\n    from cleo.io.inputs.input import Input\n    from cleo.io.io import IO\n    from cleo.io.outputs.output import Output\n\n    from poetry.console.commands.installer_command import InstallerCommand\n    from poetry.poetry import Poetry\n\n\ndef load_command(name: str) -> Callable[[], Command]:\n    def _load() -> Command:\n        words = name.split(\" \")\n        module = import_module(\"poetry.console.commands.\" + \".\".join(words))\n        command_class = getattr(module, \"\".join(c.title() for c in words) + \"Command\")\n        command: Command = command_class()\n        return command\n\n    return _load\n\n\nCOMMANDS = [\n    \"about\",\n    \"add\",\n    \"build\",\n    \"check\",\n    \"config\",\n    \"init\",\n    \"install\",\n    \"lock\",\n    \"new\",\n    \"publish\",\n    \"remove\",\n    \"run\",\n    \"search\",\n    \"show\",\n    \"sync\",\n    \"update\",\n    \"version\",\n    # Cache commands\n    \"cache clear\",\n    \"cache list\",\n    # Debug commands\n    \"debug info\",\n    \"debug resolve\",\n    \"debug tags\",\n    # Env commands\n    \"env activate\",\n    \"env info\",\n    \"env list\",\n    \"env remove\",\n    \"env use\",\n    # Python commands,\n    \"python install\",\n    \"python list\",\n    \"python remove\",\n    # Self commands\n    \"self add\",\n    \"self install\",\n    \"self lock\",\n    \"self remove\",\n    \"self update\",\n    \"self show\",\n    \"self show plugins\",\n    \"self sync\",\n    # Source commands\n    \"source add\",\n    \"source remove\",\n    \"source show\",\n]\n\n# these are special messages to override the default message when a command is not found\n# in cases where a previously existing command has been moved to a plugin or outright\n# removed for various reasons\nCOMMAND_NOT_FOUND_PREFIX_MESSAGE = (\n    \"Looks like you're trying to use a Poetry command that is not available.\"\n)\nCOMMAND_NOT_FOUND_MESSAGES = {\n    \"shell\": \"\"\"\nSince <info>Poetry (<b>2.0.0</>)</>, the <c1>shell</> command is not installed by default. You can use,\n\n  - the new <c1>env activate</> command (<b>recommended</>); or\n  - the <c1>shell plugin</> to install the <c1>shell</> command\n\n<b>Documentation:</> https://python-poetry.org/docs/managing-environments/#activating-the-environment\n\n<warning>Note that the <c1>env activate</> command is not a direct replacement for <c1>shell</> command.\n\"\"\"\n}\n\n\nclass Application(BaseApplication):\n    def __init__(self) -> None:\n        super().__init__(\"poetry\", __version__)\n\n        self._poetry: Poetry | None = None\n        self._io: IO | None = None\n        self._disable_plugins = False\n        self._disable_cache = False\n        self._plugins_loaded = False\n        self._working_directory = Path.cwd()\n        self._project_directory: Path | None = None\n\n        dispatcher = EventDispatcher()\n        dispatcher.add_listener(COMMAND, self.register_command_loggers)\n        dispatcher.add_listener(COMMAND, self.configure_env)\n        dispatcher.add_listener(COMMAND, self.configure_installer_for_event)\n        self.set_event_dispatcher(dispatcher)\n\n        command_loader = CommandLoader({name: load_command(name) for name in COMMANDS})\n        self.set_command_loader(command_loader)\n\n    @property\n    def _default_definition(self) -> Definition:\n        from cleo.io.inputs.option import Option\n\n        definition = super()._default_definition\n\n        definition.add_option(\n            Option(\"--no-plugins\", flag=True, description=\"Disables plugins.\")\n        )\n\n        definition.add_option(\n            Option(\n                \"--no-cache\", flag=True, description=\"Disables Poetry source caches.\"\n            )\n        )\n\n        definition.add_option(\n            Option(\n                \"--project\",\n                \"-P\",\n                flag=False,\n                description=(\n                    \"Specify another path as the project root.\"\n                    \" All command-line arguments will be resolved relative to the current working directory.\"\n                ),\n            )\n        )\n\n        definition.add_option(\n            Option(\n                \"--directory\",\n                \"-C\",\n                flag=False,\n                description=(\n                    \"The working directory for the Poetry command (defaults to the\"\n                    \" current working directory). All command-line arguments will be\"\n                    \" resolved relative to the given directory.\"\n                ),\n            )\n        )\n\n        return definition\n\n    @property\n    def project_directory(self) -> Path:\n        return self._project_directory or self._working_directory\n\n    @property\n    def poetry(self) -> Poetry:\n        from poetry.factory import Factory\n\n        if self._poetry is not None:\n            return self._poetry\n\n        self._poetry = Factory().create_poetry(\n            cwd=self.project_directory,\n            io=self._io,\n            disable_plugins=self._disable_plugins,\n            disable_cache=self._disable_cache,\n        )\n\n        return self._poetry\n\n    @property\n    def command_loader(self) -> CommandLoader:\n        command_loader = self._command_loader\n        assert isinstance(command_loader, CommandLoader)\n        return command_loader\n\n    def reset_poetry(self) -> None:\n        self._poetry = None\n\n    def create_io(\n        self,\n        input: Input | None = None,\n        output: Output | None = None,\n        error_output: Output | None = None,\n    ) -> IO:\n        io = super().create_io(input, output, error_output)\n\n        # Set our own CLI styles\n        formatter = io.output.formatter\n        formatter.set_style(\"c1\", Style(\"cyan\"))\n        formatter.set_style(\"c2\", Style(\"default\", options=[\"bold\"]))\n        formatter.set_style(\"info\", Style(\"blue\"))\n        formatter.set_style(\"comment\", Style(\"green\"))\n        formatter.set_style(\"warning\", Style(\"yellow\"))\n        formatter.set_style(\"debug\", Style(\"default\", options=[\"dark\"]))\n        formatter.set_style(\"success\", Style(\"green\"))\n\n        # Dark variants\n        formatter.set_style(\"c1_dark\", Style(\"cyan\", options=[\"dark\"]))\n        formatter.set_style(\"c2_dark\", Style(\"default\", options=[\"bold\", \"dark\"]))\n        formatter.set_style(\"success_dark\", Style(\"green\", options=[\"dark\"]))\n\n        io.output.set_formatter(formatter)\n        io.error_output.set_formatter(formatter)\n\n        self._io = io\n\n        return io\n\n    def _run(self, io: IO) -> int:\n        # we do this here and not inside the _configure_io implementation in order\n        # to ensure the users are not exposed to a stack trace for providing invalid values to\n        # the options --directory or --project, configuring the options here allow cleo to trap and\n        # display the error cleanly unless the user uses verbose or debug\n        self._configure_global_options(io)\n\n        with directory(self._working_directory):\n            self._load_plugins(io)\n\n            exit_code: int = 1\n\n            try:\n                exit_code = super()._run(io)\n            except PoetryRuntimeError as e:\n                io.write_error_line(\"\")\n                e.write(io)\n                io.write_error_line(\"\")\n            except CleoCommandNotFoundError as e:\n                command = self._get_command_name(io)\n\n                if command is not None and (\n                    message := COMMAND_NOT_FOUND_MESSAGES.get(command)\n                ):\n                    io.write_error_line(\"\")\n                    io.write_error_line(COMMAND_NOT_FOUND_PREFIX_MESSAGE)\n                    io.write_error_line(message)\n                    return 1\n\n                if command is not None and command in self.get_namespaces():\n                    sub_commands = []\n\n                    for key in self._commands:\n                        if key.startswith(f\"{command} \"):\n                            sub_commands.append(key)\n\n                    io.write_error_line(\n                        f\"The requested command does not exist in the <c1>{command}</> namespace.\"\n                    )\n                    suggested_names = find_similar_names(command, sub_commands)\n                    self._error_write_command_suggestions(\n                        io, suggested_names, f\"#{command}\"\n                    )\n                    return 1\n\n                if command is not None:\n                    suggested_names = find_similar_names(\n                        command, list(self._commands.keys())\n                    )\n                    io.write_error_line(\n                        f\"The requested command <c1>{command}</> does not exist.\"\n                    )\n                    self._error_write_command_suggestions(io, suggested_names)\n                    return 1\n\n                raise e\n\n        return exit_code\n\n    def _error_write_command_suggestions(\n        self, io: IO, suggested_names: list[str], doc_tag: str | None = None\n    ) -> None:\n        if suggested_names:\n            suggestion_lines = [\n                f\"<c1>{name.replace(' ', '</> <b>', 1)}</>: {self._commands[name].description}\"\n                for name in suggested_names\n            ]\n            suggestions = \"\\n    \".join([\"\", *sorted(suggestion_lines)])\n            io.write_error_line(\n                f\"\\n<error>Did you mean one of these perhaps?</>{suggestions}\"\n            )\n\n        io.write_error_line(\n            \"\\n<b>Documentation: </>\"\n            f\"<info>https://python-poetry.org/docs/cli/{doc_tag or ''}</>\"\n        )\n\n    def _configure_global_options(self, io: IO) -> None:\n        \"\"\"\n        Configures global options for the application by setting up the relevant\n        directories, disabling plugins or cache, and managing the working and\n        project directories. This method ensures that all directories are valid\n        paths and handles the resolution of the project directory relative to the\n        working directory if necessary.\n\n        :param io: The IO instance whose input and options are being read.\n        :return: Nothing.\n        \"\"\"\n        self._disable_plugins = io.input.option(\"no-plugins\")\n        self._disable_cache = io.input.option(\"no-cache\")\n\n        # we use ensure_path for the directories to make sure these are valid paths\n        # this will raise an exception if the path is invalid\n        self._working_directory = ensure_path(\n            io.input.option(\"directory\") or Path.cwd(), is_directory=True\n        )\n\n        self._project_directory = io.input.option(\"project\")\n        if self._project_directory is not None:\n            self._project_directory = Path(self._project_directory)\n            self._project_directory = ensure_path(\n                self._project_directory\n                if self._project_directory.is_absolute()\n                else self._working_directory.joinpath(self._project_directory).resolve(\n                    strict=False\n                ),\n                is_directory=True,\n            )\n\n    def _sort_global_options(self, io: IO) -> None:\n        \"\"\"\n        Sorts global options of the provided IO instance according to the\n        definition of the available options, reordering and parsing arguments\n        to ensure consistency in input handling.\n\n        The function interprets the options and their corresponding values\n        using an argument parser, constructs a sorted list of tokens, and\n        recreates the input with the rearranged sequence while maintaining\n        compatibility with the initially provided input stream.\n\n        If using in conjunction with `_configure_run_command`, it is recommended that\n        it be called first in order to correctly handling cases like\n        `poetry run -V python -V`.\n\n        :param io: The IO instance whose input and options are being processed\n                   and reordered.\n        :return: Nothing.\n        \"\"\"\n        original_input = cast(\"ArgvInput\", io.input)\n        tokens: list[str] = original_input._tokens\n\n        parser = argparse.ArgumentParser(add_help=False)\n\n        for option in self.definition.options:\n            parser.add_argument(\n                f\"--{option.name}\",\n                *([f\"-{option.shortcut}\"] if option.shortcut else []),\n                action=\"store_true\" if option.is_flag() else \"store\",\n            )\n\n        args, remaining_args = parser.parse_known_args(tokens)\n\n        tokens = []\n        for option in self.definition.options:\n            key = option.name.replace(\"-\", \"_\")\n            value = getattr(args, key, None)\n\n            if value is not None:\n                if value:  # is truthy\n                    tokens.append(f\"--{option.name}\")\n\n                if option.accepts_value():\n                    tokens.append(str(value))\n\n        sorted_input = ArgvInput([self._name or \"\", *tokens, *remaining_args])\n\n        # this is required to ensure stdin is transferred\n        sorted_input.set_stream(original_input.stream)\n\n        # this is required as cleo internally checks for `io.input._interactive`\n        # when configuring io, and cleo's test applications overrides this attribute\n        # explicitly causing test setups to fail\n        sorted_input.interactive(io.input.is_interactive())\n\n        with suppress(CleoError):\n            sorted_input.bind(self.definition)\n\n        io.set_input(sorted_input)\n\n    def _configure_run_command(self, io: IO) -> None:\n        \"\"\"\n        Configures the input for the \"run\" command to properly handle cases where the user\n        executes commands such as \"poetry run -- <subcommand>\". This involves reorganizing\n        input tokens to ensure correct parsing and execution of the run command.\n        \"\"\"\n        with suppress(CleoError):\n            io.input.bind(self.definition)\n\n        command_name = io.input.first_argument\n\n        if command_name == \"run\":\n            original_input = cast(\"ArgvInput\", io.input)\n            tokens: list[str] = original_input._tokens\n\n            if \"--\" in tokens:\n                # this means the user has done the right thing and used \"poetry run -- echo hello\"\n                # in this case there is not much we need to do, we can skip the rest\n                return\n\n            # find the correct command index, in some cases this might not be first occurrence\n            # eg: poetry -C run run echo\n            command_index = tokens.index(command_name)\n\n            while command_index < (len(tokens) - 1):\n                try:\n                    # try parsing the tokens so far\n                    _ = ArgvInput(\n                        [self._name or \"\", *tokens[: command_index + 1]],\n                        definition=self.definition,\n                    )\n                    break\n                except CleoError:\n                    # parsing failed, try finding the next \"run\" token\n                    try:\n                        command_index += (\n                            tokens[command_index + 1 :].index(command_name) + 1\n                        )\n                    except ValueError:\n                        command_index = len(tokens)\n            else:\n                # looks like we reached the end of the road, let cleo deal with it\n                return\n\n            # fetch tokens after the \"run\" command\n            tokens_without_command = tokens[command_index + 1 :]\n\n            # we create a new input for parsing the subcommand pretending\n            # it is poetry command\n            without_command = ArgvInput(\n                [self._name or \"\", *tokens_without_command], None\n            )\n\n            with suppress(CleoError):\n                # we want to bind the definition here so that cleo knows what should be\n                # parsed, and how\n                without_command.bind(self.definition)\n\n            # the first argument here is the subcommand\n            subcommand = without_command.first_argument\n            subcommand_index = (\n                (tokens_without_command.index(subcommand) if subcommand else 0)\n                + command_index\n                + 1\n            )\n\n            # recreate the original input reordering in the following order\n            #   - all tokens before \"run\" command\n            #   - all tokens after \"run\" command but before the subcommand\n            #   - the \"run\" command token\n            #   - the \"--\" token to normalise the form\n            #   - all remaining tokens starting with the subcommand\n            run_input = ArgvInput(\n                [\n                    self._name or \"\",\n                    *tokens[:command_index],\n                    *tokens[command_index + 1 : subcommand_index],\n                    command_name,\n                    \"--\",\n                    *tokens[subcommand_index:],\n                ]\n            )\n            run_input.set_stream(original_input.stream)\n\n            with suppress(CleoError):\n                run_input.bind(self.definition)\n\n            # reset the input to our constructed form\n            io.set_input(run_input)\n\n    def _configure_io(self, io: IO) -> None:\n        self._configure_run_command(io)\n        self._sort_global_options(io)\n        super()._configure_io(io)\n\n    def register_command_loggers(\n        self, event: Event, event_name: str, _: EventDispatcher\n    ) -> None:\n        from poetry.console.logging.filters import POETRY_FILTER\n        from poetry.console.logging.io_formatter import IOFormatter\n        from poetry.console.logging.io_handler import IOHandler\n\n        assert isinstance(event, ConsoleCommandEvent)\n        command = event.command\n        if not isinstance(command, Command):\n            return\n\n        io = event.io\n\n        loggers = [\n            \"poetry.packages.locker\",\n            \"poetry.packages.package\",\n            \"poetry.utils.password_manager\",\n        ]\n\n        loggers += command.loggers\n\n        handler = IOHandler(io)\n        handler.setFormatter(IOFormatter())\n\n        level = logging.WARNING\n\n        if io.is_debug():\n            level = logging.DEBUG\n        elif io.is_very_verbose() or io.is_verbose():\n            level = logging.INFO\n\n        logging.basicConfig(level=level, handlers=[handler])\n\n        # only log third-party packages when very verbose\n        if not io.is_very_verbose():\n            handler.addFilter(POETRY_FILTER)\n\n        for name in loggers:\n            logger = logging.getLogger(name)\n\n            _level = level\n            # The builders loggers are special and we can actually\n            # start at the INFO level.\n            if (\n                logger.name.startswith(\"poetry.core.masonry.builders\")\n                and _level > logging.INFO\n            ):\n                _level = logging.INFO\n\n            logger.setLevel(_level)\n\n    def configure_env(self, event: Event, event_name: str, _: EventDispatcher) -> None:\n        from poetry.console.commands.env_command import EnvCommand\n        from poetry.console.commands.self.self_command import SelfCommand\n\n        assert isinstance(event, ConsoleCommandEvent)\n        command = event.command\n        if not isinstance(command, EnvCommand) or isinstance(command, SelfCommand):\n            return\n\n        if command._env is not None:\n            return\n\n        from poetry.utils.env import EnvManager\n\n        io = event.io\n        poetry = command.poetry\n\n        env_manager = EnvManager(poetry, io=io)\n        env = env_manager.create_venv()\n\n        if env.is_venv() and io.is_verbose():\n            io.write_error_line(f\"Using virtualenv: <comment>{env.path}</>\")\n\n        command.set_env(env)\n\n    @classmethod\n    def configure_installer_for_event(\n        cls, event: Event, event_name: str, _: EventDispatcher\n    ) -> None:\n        from poetry.console.commands.installer_command import InstallerCommand\n\n        assert isinstance(event, ConsoleCommandEvent)\n        command = event.command\n        if not isinstance(command, InstallerCommand):\n            return\n\n        # If the command already has an installer\n        # we skip this step\n        if command._installer is not None:\n            return\n\n        cls.configure_installer_for_command(command, event.io)\n\n    @staticmethod\n    def configure_installer_for_command(command: InstallerCommand, io: IO) -> None:\n        from poetry.installation.installer import Installer\n\n        poetry = command.poetry\n        installer = Installer(\n            io,\n            command.env,\n            poetry.package,\n            poetry.locker,\n            poetry.pool,\n            poetry.config,\n            disable_cache=poetry.disable_cache,\n            build_constraints=poetry.build_constraints,\n        )\n        command.set_installer(installer)\n\n    def _load_plugins(self, io: IO) -> None:\n        if self._plugins_loaded:\n            return\n\n        self._disable_plugins = io.input.has_parameter_option(\"--no-plugins\")\n\n        if not self._disable_plugins:\n            from poetry.plugins.application_plugin import ApplicationPlugin\n            from poetry.plugins.plugin_manager import PluginManager\n\n            PluginManager.add_project_plugin_path(self.project_directory)\n            manager = PluginManager(ApplicationPlugin.group)\n            manager.load_plugins()\n            manager.activate(self)\n\n        self._plugins_loaded = True\n\n\ndef main() -> int:\n    exit_code: int = Application().run()\n    return exit_code\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "src/poetry/console/command_loader.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom cleo.exceptions import CleoLogicError\nfrom cleo.loaders.factory_command_loader import FactoryCommandLoader\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from cleo.commands.command import Command\n\n\nclass CommandLoader(FactoryCommandLoader):\n    def register_factory(\n        self, command_name: str, factory: Callable[[], Command]\n    ) -> None:\n        if command_name in self._factories:\n            raise CleoLogicError(f'The command \"{command_name}\" already exists.')\n\n        self._factories[command_name] = factory\n"
  },
  {
    "path": "src/poetry/console/commands/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/about.py",
    "content": "from __future__ import annotations\n\nfrom poetry.console.commands.command import Command\n\n\nclass AboutCommand(Command):\n    name = \"about\"\n\n    description = \"Shows information about Poetry.\"\n\n    def handle(self) -> int:\n        from importlib import metadata\n\n        self.line(\n            f\"\"\"\\\n<info>Poetry - Package Management for Python\n\nVersion: {metadata.version(\"poetry\")}\nPoetry-Core Version: {metadata.version(\"poetry-core\")}</info>\n\n<comment>Poetry is a dependency manager tracking local dependencies of your projects\\\n and libraries.\nSee <fg=blue>https://github.com/python-poetry/poetry</> for more information.</comment>\\\n\"\"\"\n        )\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/add.py",
    "content": "from __future__ import annotations\n\nimport contextlib\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom typing import Literal\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom tomlkit.toml_document import TOMLDocument\n\nfrom poetry.console.commands.init import InitCommand\nfrom poetry.console.commands.installer_command import InstallerCommand\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from packaging.utils import NormalizedName\n\n\nclass AddCommand(InstallerCommand, InitCommand):\n    name = \"add\"\n    description = \"Adds a new dependency to <comment>pyproject.toml</> and installs it.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"name\", \"The packages to add.\", multiple=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\n            \"group\",\n            \"-G\",\n            \"The group to add the dependency to.\",\n            flag=False,\n            default=MAIN_GROUP,\n        ),\n        option(\n            \"dev\",\n            \"D\",\n            \"Add as a development dependency. (shortcut for '-G dev')\",\n        ),\n        option(\"editable\", \"e\", \"Add vcs/path dependencies as editable.\"),\n        option(\n            \"extras\",\n            \"E\",\n            \"Extras to activate for the dependency.\",\n            flag=False,\n            multiple=True,\n        ),\n        option(\n            \"optional\",\n            None,\n            \"Add as an optional dependency to an extra.\",\n            flag=False,\n        ),\n        option(\n            \"python\",\n            None,\n            \"Python version for which the dependency must be installed.\",\n            flag=False,\n        ),\n        option(\n            \"platform\",\n            None,\n            \"Platforms for which the dependency must be installed.\",\n            flag=False,\n        ),\n        option(\n            \"markers\",\n            None,\n            \"Environment markers which describe when the dependency should be installed.\",\n            flag=False,\n        ),\n        option(\n            \"source\",\n            None,\n            \"Name of the source to use to install the package.\",\n            flag=False,\n        ),\n        option(\"allow-prereleases\", None, \"Accept prereleases.\"),\n        option(\n            \"dry-run\",\n            None,\n            \"Output the operations but do not execute anything (implicitly enables\"\n            \" --verbose).\",\n        ),\n        option(\"lock\", None, \"Do not perform operations (only update the lockfile).\"),\n    ]\n    examples = \"\"\"\\\nIf you do not specify a version constraint, poetry will choose a suitable one based on\\\n the available package versions.\n\nYou can specify a package in the following forms:\n  - A single name (<b>requests</b>)\n  - A name and a constraint (<b>requests@^2.23.0</b>)\n  - A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)\n  - A git url with a revision\\\n (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)\n  - A subdirectory of a git repository\\\n (<b>git+https://github.com/python-poetry/poetry.git#subdirectory=tests/fixtures/sample_project</b>)\n  - A git SSH url (<b>git+ssh://git@github.com/python-poetry/poetry.git</b>)\n  - A git SSH url with a revision\\\n (<b>git+ssh://git@github.com/python-poetry/poetry.git#develop</b>)\n  - A file path (<b>../my-package/my-package.whl</b>)\n  - A directory (<b>../my-package/</b>)\n  - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)\n\"\"\"\n    help = f\"\"\"\\\nThe add command adds required packages to your <comment>pyproject.toml</> and installs\\\n them.\n\n{examples}\n\"\"\"\n\n    loggers: ClassVar[list[str]] = [\n        \"poetry.repositories.pypi_repository\",\n        \"poetry.inspection.info\",\n    ]\n\n    def handle(self) -> int:\n        from poetry.core.constraints.version import parse_constraint\n        from tomlkit import array\n        from tomlkit import inline_table\n        from tomlkit import nl\n        from tomlkit import table\n\n        from poetry.factory import Factory\n\n        packages = self.argument(\"name\")\n        if self.option(\"dev\"):\n            group = \"dev\"\n        else:\n            group = self.option(\"group\", self.default_group or MAIN_GROUP)\n\n        if self.option(\"extras\") and len(packages) > 1:\n            raise ValueError(\n                \"You can only specify one package when using the --extras option\"\n            )\n\n        optional = self.option(\"optional\")\n        if optional and group != MAIN_GROUP:\n            raise ValueError(\"You can only add optional dependencies to the main group\")\n\n        # tomlkit types are awkward to work with, treat content as a mostly untyped\n        # dictionary.\n        content: dict[str, Any] = self.poetry.file.read()\n        project_content = content.get(\"project\", table())\n        poetry_content = content.get(\"tool\", {}).get(\"poetry\", table())\n        groups_content = content.get(\"dependency-groups\", {})\n        project_name = (\n            canonicalize_name(name)\n            if (name := project_content.get(\"name\", poetry_content.get(\"name\")))\n            else None\n        )\n\n        use_project_section = False\n        use_groups_section = False\n        project_dependency_names: list[NormalizedName | Literal[\"<include-group>\"]] = []\n\n        # Run-Time Deps incl. extras\n        if group == MAIN_GROUP:\n            if (\n                \"dependencies\" in project_content\n                or \"optional-dependencies\" in project_content\n            ):\n                use_project_section = True\n                if optional:\n                    project_section = project_content.get(\n                        \"optional-dependencies\", {}\n                    ).get(optional, array())\n                else:\n                    project_section = project_content.get(\"dependencies\", array())\n                project_dependency_names = [\n                    Dependency.create_from_pep_508(dep).name for dep in project_section\n                ]\n            else:\n                project_section = array()\n\n            poetry_section = poetry_content.get(\"dependencies\", table())\n\n        # Dependency Groups\n        else:\n            if groups_content or \"group\" not in poetry_content:\n                use_groups_section = True\n                if not groups_content:\n                    groups_content = table(is_super_table=True)\n                if group not in groups_content:\n                    groups_content[group] = array(\"[\\n]\")\n\n                project_dependency_names = [\n                    Dependency.create_from_pep_508(dep).name\n                    if isinstance(dep, str)\n                    # We have to add an entry for \"include-group\" items because\n                    # later the index of the dependency is used to replace it.\n                    # We just choose a name that cannot be a package's name.\n                    else \"<include-group>\"\n                    for dep in groups_content[group]\n                ]\n\n            poetry_section = (\n                poetry_content.get(\"group\", {})\n                .get(group, {})\n                .get(\"dependencies\", table())\n            )\n            project_section = []\n\n        existing_packages = self.get_existing_packages_from_input(\n            packages, poetry_section, project_dependency_names\n        )\n\n        if existing_packages:\n            self.notify_about_existing_packages(existing_packages)\n\n        packages = [name for name in packages if name not in existing_packages]\n\n        if not packages:\n            self.line(\"Nothing to add.\")\n            return 0\n\n        if optional and not use_project_section:\n            self.line_error(\n                \"<warning>Optional dependencies will not be added to extras\"\n                \" in legacy mode. Consider converting your project to use the [project]\"\n                \" section.</warning>\"\n            )\n\n        requirements = self._determine_requirements(\n            packages,\n            allow_prereleases=self.option(\"allow-prereleases\") or None,\n            source=self.option(\"source\"),\n        )\n\n        for _constraint in requirements:\n            version = _constraint.get(\"version\")\n            if version is not None:\n                # Validate version constraint\n                assert isinstance(version, str)\n                parse_constraint(version)\n\n            constraint: dict[str, Any] = inline_table()\n            for key, value in _constraint.items():\n                if key == \"name\":\n                    continue\n\n                constraint[key] = value\n\n            if optional:\n                constraint[\"optional\"] = True\n\n            if self.option(\"allow-prereleases\"):\n                constraint[\"allow-prereleases\"] = True\n\n            if self.option(\"extras\"):\n                extras = []\n                for extra in self.option(\"extras\"):\n                    extras += extra.split()\n\n                constraint[\"extras\"] = extras\n\n            if self.option(\"editable\"):\n                if \"git\" in _constraint or \"path\" in _constraint:\n                    constraint[\"develop\"] = True\n                else:\n                    self.line_error(\n                        \"\\n\"\n                        \"<error>Failed to add packages. \"\n                        \"Only vcs/path dependencies support editable installs. \"\n                        f\"<c1>{_constraint['name']}</c1> is neither.\"\n                    )\n                    self.line_error(\"\\nNo changes were applied.\")\n                    return 1\n\n            if python := self.option(\"python\"):\n                constraint[\"python\"] = python\n\n            if platform := self.option(\"platform\"):\n                constraint[\"platform\"] = platform\n\n            if markers := self.option(\"markers\"):\n                constraint[\"markers\"] = markers\n\n            if source := self.option(\"source\"):\n                constraint[\"source\"] = source\n\n            if len(constraint) == 1 and \"version\" in constraint:\n                constraint = constraint[\"version\"]\n\n            constraint_name = _constraint[\"name\"]\n            assert isinstance(constraint_name, str)\n\n            canonical_constraint_name = canonicalize_name(constraint_name)\n\n            if canonical_constraint_name == project_name:\n                self.line_error(\n                    f\"<error>Cannot add dependency on <c1>{constraint_name}</c1> to\"\n                    \" project with the same name.\"\n                )\n                self.line_error(\"\\nNo changes were applied.\")\n                return 1\n\n            with contextlib.suppress(ValueError):\n                self.poetry.package.dependency_group(group).remove_dependency(\n                    constraint_name\n                )\n\n            dependency = Factory.create_dependency(\n                constraint_name,\n                constraint,\n                groups=[group],\n                root_dir=self.poetry.file.path.parent,\n            )\n            self.poetry.package.add_dependency(dependency)\n\n            if use_project_section or use_groups_section:\n                pep_section = (\n                    project_section if use_project_section else groups_content[group]\n                )\n                try:\n                    index = project_dependency_names.index(canonical_constraint_name)\n                except ValueError:\n                    pep_section.append(dependency.to_pep_508())\n                else:\n                    pep_section[index] = dependency.to_pep_508()\n\n                # create a second constraint for tool.poetry.dependencies with keys\n                # that cannot be stored in the project section\n                poetry_constraint: dict[str, Any] = inline_table()\n                if not isinstance(constraint, str):\n                    for key in [\"allow-prereleases\", \"develop\", \"source\"]:\n                        if value := constraint.get(key):\n                            poetry_constraint[key] = value\n                    if poetry_constraint:\n                        # add marker related keys to avoid ambiguity\n                        for key in [\"python\", \"platform\"]:\n                            if value := constraint.get(key):\n                                poetry_constraint[key] = value\n            else:\n                poetry_constraint = constraint\n\n            if poetry_constraint:\n                for key in poetry_section:\n                    if canonicalize_name(key) == canonical_constraint_name:\n                        poetry_section[key] = poetry_constraint\n                        break\n                else:\n                    poetry_section[constraint_name] = poetry_constraint\n\n            if optional:\n                extra_name = canonicalize_name(optional)\n                # _in_extras must be set after converting the dependency to PEP 508\n                # and adding it to the project section to avoid a redundant extra marker\n                dependency._in_extras = [extra_name]\n                self._add_dependency_to_extras(dependency, extra_name)\n\n        # Refresh the locker\n        if project_section:\n            assert group == MAIN_GROUP\n            if optional:\n                if \"optional-dependencies\" not in project_content:\n                    project_content[\"optional-dependencies\"] = table()\n                if optional not in project_content[\"optional-dependencies\"]:\n                    project_content[\"optional-dependencies\"][optional] = project_section\n            elif \"dependencies\" not in project_content:\n                project_content[\"dependencies\"] = project_section\n\n        if poetry_section:\n            if \"tool\" not in content:\n                content[\"tool\"] = table()\n            if \"poetry\" not in content[\"tool\"]:\n                content[\"tool\"][\"poetry\"] = poetry_content\n            if group == MAIN_GROUP:\n                if \"dependencies\" not in poetry_content:\n                    poetry_content[\"dependencies\"] = poetry_section\n            else:\n                if \"group\" not in poetry_content:\n                    poetry_content[\"group\"] = table(is_super_table=True)\n\n                groups = poetry_content[\"group\"]\n\n                if group not in groups:\n                    groups[group] = table()\n                    groups.add(nl())\n\n                if \"dependencies\" not in groups[group]:\n                    groups[group][\"dependencies\"] = poetry_section\n\n        if groups_content and group != MAIN_GROUP:\n            if \"dependency-groups\" not in content:\n                content[\"dependency-groups\"] = table()\n            content[\"dependency-groups\"][group] = groups_content[group]\n\n        self.poetry.locker.set_pyproject_data(content)\n        self.installer.set_locker(self.poetry.locker)\n\n        # Cosmetic new line\n        self.line(\"\")\n\n        self.installer.set_package(self.poetry.package)\n        self.installer.dry_run(self.option(\"dry-run\"))\n        self.installer.verbose(self.io.is_verbose())\n        self.installer.update(True)\n        self.installer.execute_operations(not self.option(\"lock\"))\n\n        self.installer.whitelist([r[\"name\"] for r in requirements])\n\n        status = self.installer.run()\n\n        if status == 0 and not self.option(\"dry-run\"):\n            assert isinstance(content, TOMLDocument)\n            self.poetry.file.write(content)\n\n        return status\n\n    def get_existing_packages_from_input(\n        self,\n        packages: list[str],\n        section: dict[str, Any],\n        project_dependencies: Collection[NormalizedName | Literal[\"<include-group>\"]],\n    ) -> list[str]:\n        existing_packages = []\n\n        for name in packages:\n            normalized_name = canonicalize_name(name)\n            if normalized_name in project_dependencies:\n                existing_packages.append(name)\n                continue\n            for key in section:\n                if normalized_name == canonicalize_name(key):\n                    existing_packages.append(name)\n\n        return existing_packages\n\n    @property\n    def _hint_update_packages(self) -> str:\n        return (\n            \"\\nIf you want to update it to the latest compatible version, you can use\"\n            \" `poetry update package`.\\nIf you prefer to upgrade it to the latest\"\n            \" available version, you can use `poetry add package@latest`.\\n\"\n        )\n\n    def notify_about_existing_packages(self, existing_packages: list[str]) -> None:\n        self.line(\n            \"The following packages are already present in the pyproject.toml and will\"\n            \" be skipped:\\n\"\n        )\n        for name in existing_packages:\n            self.line(f\"  - <c1>{name}</c1>\")\n        self.line(self._hint_update_packages)\n\n    def _add_dependency_to_extras(\n        self, dependency: Dependency, extra_name: NormalizedName\n    ) -> None:\n        extras = dict(self.poetry.package.extras)\n        extra_deps = []\n        replaced = False\n        for dep in extras.get(extra_name, ()):\n            if dep.name == dependency.name:\n                extra_deps.append(dependency)\n                replaced = True\n            else:\n                extra_deps.append(dep)\n        if not replaced:\n            extra_deps.append(dependency)\n        extras[extra_name] = extra_deps\n        self.poetry.package.extras = extras\n"
  },
  {
    "path": "src/poetry/console/commands/build.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\n\nfrom importlib import metadata\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom typing import Literal\n\nfrom cleo.helpers import option\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.masonry.builders import BUILD_FORMATS\nfrom poetry.utils.helpers import remove_directory\nfrom poetry.utils.isolated_build import isolated_builder\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from cleo.io.inputs.option import Option\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env import Env\n\nDistributionType = Literal[\"sdist\", \"wheel\"]\n\n\n@dataclasses.dataclass(frozen=True)\nclass BuildOptions:\n    clean: bool\n    formats: list[DistributionType]\n    output: str\n    config_settings: dict[str, Any] = dataclasses.field(default_factory=dict)\n\n    def __post_init__(self) -> None:\n        for fmt in self.formats:\n            if fmt not in BUILD_FORMATS:\n                raise ValueError(f\"Invalid format: {fmt}\")\n\n\nclass BuildHandler:\n    def __init__(self, poetry: Poetry, env: Env, io: IO) -> None:\n        self.poetry = poetry\n        self.env = env\n        self.io = io\n\n    def _build(\n        self,\n        fmt: DistributionType,\n        executable: Path,\n        target_dir: Path,\n        config_settings: dict[str, Any],\n    ) -> None:\n        builder = BUILD_FORMATS[fmt]\n\n        builder(\n            self.poetry,\n            executable=executable,\n            config_settings=config_settings,\n        ).build(target_dir)\n\n    def _isolated_build(\n        self,\n        fmt: DistributionType,\n        executable: Path,\n        target_dir: Path,\n        config_settings: dict[str, Any],\n    ) -> None:\n        with isolated_builder(\n            source=self.poetry.file.path.parent,\n            distribution=fmt,\n            python_executable=executable,\n        ) as builder:\n            builder.build(fmt, target_dir, config_settings=config_settings)\n\n    def _requires_isolated_build(self) -> bool:\n        \"\"\"\n        Determines if an isolated build is required.\n\n        An isolated build is required if:\n        - The package has a build script.\n        - There are multiple build system dependencies.\n        - The build dependency is not `poetry-core`.\n        - The installed `poetry-core` version does not satisfy the build dependency constraints.\n        - The build dependency has a source type (e.g. is a VcsDependency).\n\n        :returns: True if an isolated build is required, False otherwise.\n        \"\"\"\n        if not self._has_build_backend_defined():\n            self.io.write_error_line(\n                \"<warning><b>WARNING</>: No build backend defined. Please define one in the <c1>pyproject.toml</>.\\n\"\n                \"Falling back to using the built-in `poetry-core` version.\\n\"\n                \"In a future release Poetry will fallback to `setuptools` as defined by PEP 517.\\n\"\n                \"More details can be found at https://python-poetry.org/docs/libraries/#packaging</>\"\n            )\n            return False\n\n        if (\n            self.poetry.package.build_script\n            or len(self.poetry.build_system_dependencies) != 1\n        ):\n            return True\n\n        build_dependency = self.poetry.build_system_dependencies[0]\n        if build_dependency.name != \"poetry-core\":\n            return True\n\n        poetry_core_version = Version.parse(metadata.version(\"poetry-core\"))\n\n        return bool(\n            not build_dependency.constraint.allows(poetry_core_version)\n            or build_dependency.source_type\n        )\n\n    def _get_builder(self) -> Callable[..., None]:\n        if self._requires_isolated_build():\n            return self._isolated_build\n\n        return self._build\n\n    def _has_build_backend_defined(self) -> bool:\n        return \"build-backend\" in self.poetry.pyproject.data.get(\"build-system\", {})\n\n    def build(self, options: BuildOptions) -> int:\n        if not self.poetry.is_package_mode:\n            self.io.write_error_line(\n                \"Building a package is not possible in non-package mode.\"\n            )\n            return 1\n\n        dist_dir = Path(options.output)\n        package = self.poetry.package\n        self.io.write_line(\n            f\"Building <c1>{package.pretty_name}</c1> (<c2>{package.version}</c2>)\"\n        )\n\n        if not dist_dir.is_absolute():\n            dist_dir = self.poetry.pyproject_path.parent / dist_dir\n\n        if options.clean:\n            remove_directory(path=dist_dir, force=True)\n\n        build = self._get_builder()\n\n        for fmt in options.formats:\n            self.io.write_line(f\"Building <info>{fmt}</info>\")\n            build(\n                fmt,\n                executable=self.env.python,\n                target_dir=dist_dir,\n                config_settings=options.config_settings,\n            )\n\n        return 0\n\n\nclass BuildCommand(EnvCommand):\n    name = \"build\"\n    description = \"Builds a package, as a tarball and a wheel by default.\"\n\n    options: ClassVar[list[Option]] = [\n        option(\"format\", \"f\", \"Limit the format to either sdist or wheel.\", flag=False),\n        option(\n            \"clean\",\n            description=\"Clean output directory before building.\",\n            flag=True,\n        ),\n        option(\n            \"local-version\",\n            \"l\",\n            \"Add or replace a local version label to the build. (<warning>Deprecated</warning>)\",\n            flag=False,\n        ),\n        option(\n            \"output\",\n            \"o\",\n            \"Set output directory for build artifacts. Default is `dist`.\",\n            default=\"dist\",\n            flag=False,\n        ),\n        option(\n            \"config-settings\",\n            \"c\",\n            description=\"Provide config settings that should be passed to backend in <key>=<value> format.\",\n            flag=False,\n            multiple=True,\n        ),\n    ]\n\n    loggers: ClassVar[list[str]] = [\n        \"poetry.core.masonry.builders.builder\",\n        \"poetry.core.masonry.builders.sdist\",\n        \"poetry.core.masonry.builders.wheel\",\n    ]\n\n    @staticmethod\n    def _prepare_config_settings(\n        local_version: str | None, config_settings: list[str] | None, io: IO\n    ) -> dict[str, str]:\n        config_settings = config_settings or []\n        result = {}\n\n        if local_version:\n            io.write_error_line(\n                f\"<warning>`<fg=yellow;options=bold>--local-version</>` is deprecated.\"\n                f\" Use `<fg=yellow;options=bold>--config-settings local-version={local_version}</>`\"\n                f\" instead.</warning>\"\n            )\n            result[\"local-version\"] = local_version\n\n        for config_setting in config_settings:\n            if \"=\" not in config_setting:\n                raise ValueError(\n                    f\"Invalid config setting format: {config_setting}. \"\n                    \"Config settings must be in the format 'key=value'\"\n                )\n\n            key, _, value = config_setting.partition(\"=\")\n            result[key] = value\n\n        return result\n\n    @staticmethod\n    def _prepare_formats(fmt: str | None) -> list[str]:\n        fmt = fmt or \"all\"\n\n        return [\"sdist\", \"wheel\"] if fmt == \"all\" else [fmt]\n\n    def handle(self) -> int:\n        build_handler = BuildHandler(\n            poetry=self.poetry,\n            env=self.env,\n            io=self.io,\n        )\n        build_options = BuildOptions(\n            clean=self.option(\"clean\"),\n            formats=self._prepare_formats(self.option(\"format\")),  # type: ignore[arg-type]\n            output=self.option(\"output\"),\n            config_settings=self._prepare_config_settings(\n                local_version=self.option(\"local-version\"),\n                config_settings=self.option(\"config-settings\"),\n                io=self.io,\n            ),\n        )\n\n        return build_handler.build(options=build_options)\n"
  },
  {
    "path": "src/poetry/console/commands/cache/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/cache/clear.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.config.config import Config\nfrom poetry.console.commands.command import Command\nfrom poetry.utils.cache import FileCache\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass CacheClearCommand(Command):\n    name = \"cache clear\"\n    description = \"Clear Poetry's caches.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"cache\", description=\"The name of the cache to clear.\", optional=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\"all\", description=\"Clear all entries in the cache.\")\n    ]\n\n    def handle(self) -> int:\n        cache = self.argument(\"cache\")\n\n        if cache:\n            parts = cache.split(\":\")\n            root = parts[0]\n        else:\n            parts = []\n            root = \"\"\n\n        config = Config.create()\n        cache_dir = config.repository_cache_directory / root\n\n        try:\n            cache_dir.relative_to(config.repository_cache_directory)\n        except ValueError:\n            raise ValueError(f\"{root} is not a valid repository cache\")\n\n        cache = FileCache(cache_dir)\n\n        if len(parts) < 2:\n            if not self.option(\"all\"):\n                raise RuntimeError(\n                    \"Add the --all option if you want to clear all cache entries\"\n                )\n\n            if not cache_dir.exists():\n                self.line(\n                    f\"No cache entries for {root}\" if root else \"No cache entries\"\n                )\n                return 0\n\n            # Calculate number of entries\n            entries_count = sum(\n                len(files) for _path, _dirs, files in os.walk(str(cache_dir))\n            )\n\n            delete = self.confirm(f\"<question>Delete {entries_count} entries?</>\", True)\n            if not delete:\n                return 0\n\n            cache.flush()\n        elif len(parts) == 2:\n            raise RuntimeError(\n                \"Only specifying the package name is not yet supported. \"\n                \"Add a specific version to clear\"\n            )\n        elif len(parts) == 3:\n            package = canonicalize_name(parts[1])\n            version = parts[2]\n\n            if not cache.has(f\"{package}:{version}\"):\n                self.line(f\"No cache entries for {package}:{version}\")\n                return 0\n\n            delete = self.confirm(f\"Delete cache entry {package}:{version}\", True)\n            if not delete:\n                return 0\n\n            cache.forget(f\"{package}:{version}\")\n        else:\n            raise ValueError(\"Invalid cache key\")\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/cache/list.py",
    "content": "from __future__ import annotations\n\nfrom poetry.config.config import Config\nfrom poetry.console.commands.command import Command\n\n\nclass CacheListCommand(Command):\n    name = \"cache list\"\n    description = \"List Poetry's caches.\"\n\n    def handle(self) -> int:\n        config = Config.create()\n        if config.repository_cache_directory.exists():\n            caches = sorted(config.repository_cache_directory.iterdir())\n            if caches:\n                for cache in caches:\n                    self.line(f\"<info>{cache.name}</>\")\n                return 0\n\n        self.line_error(\"<warning>No caches found</>\")\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/check.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.io.inputs.option import Option\n\n\nclass CheckCommand(Command):\n    name = \"check\"\n    description = (\n        \"Validates the content of the <comment>pyproject.toml</> file and its\"\n        \" consistency with the poetry.lock file.\"\n    )\n\n    options: ClassVar[list[Option]] = [\n        option(\n            \"lock\",\n            None,\n            \"Checks that <comment>poetry.lock</> exists for the current\"\n            \" version of <comment>pyproject.toml</>.\",\n        ),\n        option(\n            \"strict\",\n            None,\n            \"Fail if check reports warnings.\",\n        ),\n    ]\n\n    def _validate_classifiers(\n        self, project_classifiers: set[str]\n    ) -> tuple[list[str], list[str]]:\n        \"\"\"Identify unrecognized and deprecated trove classifiers.\n\n        A fully-qualified classifier is a string delimited by `` :: `` separators. To\n        make the error message more readable we need to have visual clues to\n        materialize the start and end of a classifier string. That way the user can\n        easily copy and paste it from the messages while reducing mistakes because of\n        extra spaces.\n\n        We use ``!r`` (``repr()``) for classifiers and list of classifiers for\n        consistency. That way all strings will be rendered with the same kind of quotes\n        (i.e. simple tick: ``'``).\n        \"\"\"\n        from trove_classifiers import classifiers\n        from trove_classifiers import deprecated_classifiers\n\n        errors = []\n        warnings = []\n\n        unrecognized = sorted(\n            project_classifiers - set(classifiers) - set(deprecated_classifiers)\n        )\n        # Allow \"Private ::\" classifiers as recommended on PyPI and the packaging guide\n        # to allow users to avoid accidentally publishing private packages to PyPI.\n        # https://pypi.org/classifiers/\n        unrecognized = [u for u in unrecognized if not u.startswith(\"Private ::\")]\n        if unrecognized:\n            errors.append(f\"Unrecognized classifiers: {unrecognized!r}.\")\n\n        deprecated = sorted(\n            project_classifiers.intersection(set(deprecated_classifiers))\n        )\n        if deprecated:\n            for old_classifier in deprecated:\n                new_classifiers = deprecated_classifiers[old_classifier]\n                if new_classifiers:\n                    message = (\n                        f\"Deprecated classifier {old_classifier!r}. \"\n                        f\"Must be replaced by {new_classifiers!r}.\"\n                    )\n                else:\n                    message = (\n                        f\"Deprecated classifier {old_classifier!r}. Must be removed.\"\n                    )\n                warnings.append(message)\n\n        return errors, warnings\n\n    def _validate_readme(self, readme: str | list[str], poetry_file: Path) -> list[str]:\n        \"\"\"Check existence of referenced readme files\"\"\"\n        readmes = [readme] if isinstance(readme, str) else readme\n\n        errors = []\n        for name in readmes:\n            if not name:\n                errors.append(\"Declared README file is an empty string.\")\n            elif not (poetry_file.parent / name).exists():\n                errors.append(f\"Declared README file does not exist: {name}\")\n        return errors\n\n    def _validate_dependencies_source(self, config: dict[str, Any]) -> list[str]:\n        \"\"\"Check dependencies's source are valid\"\"\"\n        sources = {repository.name for repository in self.poetry.pool.all_repositories}\n\n        dependency_declarations: list[\n            dict[str, str | dict[str, str] | list[dict[str, str]]]\n        ] = []\n        # scan dependencies and group dependencies settings in pyproject.toml\n        if \"dependencies\" in config:\n            dependency_declarations.append(config[\"dependencies\"])\n\n        for group in config.get(\"group\", {}).values():\n            if \"dependencies\" in group:\n                dependency_declarations.append(group[\"dependencies\"])\n\n        all_referenced_sources: set[str] = set()\n\n        for dependency_declaration in dependency_declarations:\n            for declaration in dependency_declaration.values():\n                if isinstance(declaration, list):\n                    for item in declaration:\n                        if \"source\" in item:\n                            all_referenced_sources.add(item[\"source\"])\n                elif isinstance(declaration, dict) and \"source\" in declaration:\n                    all_referenced_sources.add(declaration[\"source\"])\n\n        return [\n            f'Invalid source \"{source}\" referenced in dependencies.'\n            for source in sorted(all_referenced_sources - sources)\n        ]\n\n    def handle(self) -> int:\n        from poetry.core.pyproject.toml import PyProjectTOML\n\n        from poetry.factory import Factory\n\n        # Load poetry config and display errors, if any\n        poetry_file = self.poetry.file.path\n        toml_data = PyProjectTOML(poetry_file).data\n        check_result = Factory.validate(toml_data, strict=True)\n\n        project = toml_data.get(\"project\", {})\n        poetry_config = toml_data[\"tool\"][\"poetry\"]\n\n        # Validate trove classifiers\n        project_classifiers = set(\n            project.get(\"classifiers\") or poetry_config.get(\"classifiers\", [])\n        )\n        errors, warnings = self._validate_classifiers(project_classifiers)\n        check_result[\"errors\"].extend(errors)\n        check_result[\"warnings\"].extend(warnings)\n\n        readme_errors = []\n\n        # Check poetry readme\n        if \"readme\" in poetry_config:\n            readme_errors += self._validate_readme(poetry_config[\"readme\"], poetry_file)\n\n        project_readme = project.get(\"readme\")\n        if project_readme is not None:\n            if isinstance(project_readme, dict):\n                readme_path = project_readme.get(\"file\")\n                if readme_path is not None:\n                    readme_errors += self._validate_readme(readme_path, poetry_file)\n            elif isinstance(project_readme, str):\n                readme_errors += self._validate_readme(project_readme, poetry_file)\n            else:\n                # should not happen due to prior schema validation, but just in case\n                readme_errors.append(\n                    f\"Invalid format for [project.readme]: {project_readme!r}\"\n                )\n\n        check_result[\"errors\"].extend(readme_errors)\n\n        # Validate dependencies' sources\n        check_result[\"errors\"] += self._validate_dependencies_source(poetry_config)\n\n        # Verify that lock file is consistent\n        if self.option(\"lock\") and not self.poetry.locker.is_locked():\n            check_result[\"errors\"] += [\"poetry.lock was not found.\"]\n        if self.poetry.locker.is_locked() and not self.poetry.locker.is_fresh():\n            check_result[\"errors\"] += [\n                \"pyproject.toml changed significantly since poetry.lock was last generated. \"\n                \"Run `poetry lock` to fix the lock file.\"\n            ]\n\n        return_code = 0\n\n        if check_result[\"errors\"] or (\n            check_result[\"warnings\"] and self.option(\"strict\")\n        ):\n            return_code = 1\n\n        if not check_result[\"errors\"] and not check_result[\"warnings\"]:\n            self.info(\"All set!\")\n\n        for error in check_result[\"errors\"]:\n            self.line_error(f\"<error>Error: {error}</error>\")\n\n        for error in check_result[\"warnings\"]:\n            self.line_error(f\"<warning>Warning: {error}</warning>\")\n\n        return return_code\n"
  },
  {
    "path": "src/poetry/console/commands/command.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.commands.command import Command as BaseCommand\nfrom cleo.exceptions import CleoValueError\n\n\nif TYPE_CHECKING:\n    from poetry.console.application import Application\n    from poetry.poetry import Poetry\n\n\nclass Command(BaseCommand):\n    loggers: ClassVar[list[str]] = []\n\n    _poetry: Poetry | None = None\n\n    @property\n    def poetry(self) -> Poetry:\n        if self._poetry is None:\n            return self.get_application().poetry\n\n        return self._poetry\n\n    def set_poetry(self, poetry: Poetry) -> None:\n        \"\"\"Explicitly set the current Poetry.\n\n        Useful for Plugins that extends the features of a Poetry CLI Command.\n        \"\"\"\n\n        self._poetry = poetry\n\n    def get_application(self) -> Application:\n        from poetry.console.application import Application\n\n        application = self.application\n        assert isinstance(application, Application)\n        return application\n\n    def reset_poetry(self) -> None:\n        self.get_application().reset_poetry()\n\n    def option(self, name: str, default: Any = None) -> Any:\n        try:\n            return super().option(name)\n        except CleoValueError:\n            return default\n"
  },
  {
    "path": "src/poetry/console/commands/config.py",
    "content": "from __future__ import annotations\n\nimport json\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom typing import cast\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom installer.utils import canonicalize_name\n\nfrom poetry.config.config import PackageFilterPolicy\nfrom poetry.config.config import boolean_normalizer\nfrom poetry.config.config import boolean_validator\nfrom poetry.config.config import build_config_setting_normalizer\nfrom poetry.config.config import build_config_setting_validator\nfrom poetry.config.config import int_normalizer\nfrom poetry.config.config_source import UNSET\nfrom poetry.config.config_source import ConfigSourceMigration\nfrom poetry.config.config_source import PropertyNotFoundError\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n    from poetry.config.config_source import ConfigSource\n\nCONFIG_MIGRATIONS = [\n    ConfigSourceMigration(\n        old_key=\"experimental.system-git-client\", new_key=\"system-git-client\"\n    ),\n    ConfigSourceMigration(\n        old_key=\"virtualenvs.prefer-active-python\",\n        new_key=\"virtualenvs.use-poetry-python\",\n        value_migration={True: UNSET, False: True},\n    ),\n]\n\n\nclass ConfigCommand(Command):\n    name = \"config\"\n    description = \"Manages configuration settings.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"key\", \"Setting key.\", optional=True),\n        argument(\"value\", \"Setting value.\", optional=True, multiple=True),\n    ]\n\n    options: ClassVar[list[Option]] = [\n        option(\"list\", None, \"List configuration settings.\"),\n        option(\"unset\", None, \"Unset configuration setting.\"),\n        option(\"local\", None, \"Set/Get from the project's local configuration.\"),\n        option(\"migrate\", None, \"Migrate outdated configuration settings.\"),\n    ]\n\n    help = \"\"\"\\\nThis command allows you to edit the poetry config settings and repositories.\n\nTo add a repository:\n\n    <comment>poetry config repositories.foo https://bar.com/simple/</comment>\n\nTo remove a repository (repo is a short alias for repositories):\n\n    <comment>poetry config --unset repo.foo</comment>\"\"\"\n\n    LIST_PROHIBITED_SETTINGS: ClassVar[set[str]] = {\"http-basic\", \"pypi-token\"}\n\n    @property\n    def unique_config_values(self) -> dict[str, tuple[Any, Any]]:\n        unique_config_values = {\n            \"cache-dir\": (str, lambda val: str(Path(val))),\n            \"data-dir\": (str, lambda val: str(Path(val))),\n            \"virtualenvs.create\": (boolean_validator, boolean_normalizer),\n            \"virtualenvs.in-project\": (boolean_validator, boolean_normalizer),\n            \"virtualenvs.options.always-copy\": (boolean_validator, boolean_normalizer),\n            \"virtualenvs.options.system-site-packages\": (\n                boolean_validator,\n                boolean_normalizer,\n            ),\n            \"virtualenvs.options.no-pip\": (boolean_validator, boolean_normalizer),\n            \"virtualenvs.path\": (str, lambda val: str(Path(val))),\n            \"virtualenvs.use-poetry-python\": (boolean_validator, boolean_normalizer),\n            \"virtualenvs.prompt\": (str, str),\n            \"system-git-client\": (boolean_validator, boolean_normalizer),\n            \"requests.max-retries\": (lambda val: int(val) >= 0, int_normalizer),\n            \"installer.re-resolve\": (boolean_validator, boolean_normalizer),\n            \"installer.parallel\": (boolean_validator, boolean_normalizer),\n            \"installer.max-workers\": (lambda val: int(val) > 0, int_normalizer),\n            \"installer.no-binary\": (\n                PackageFilterPolicy.validator,\n                PackageFilterPolicy.normalize,\n            ),\n            \"installer.only-binary\": (\n                PackageFilterPolicy.validator,\n                PackageFilterPolicy.normalize,\n            ),\n            \"solver.lazy-wheel\": (boolean_validator, boolean_normalizer),\n            \"keyring.enabled\": (boolean_validator, boolean_normalizer),\n            \"python.installation-dir\": (str, lambda val: str(Path(val))),\n        }\n\n        return unique_config_values\n\n    def handle(self) -> int:\n        from pathlib import Path\n\n        from poetry.core.pyproject.exceptions import PyProjectError\n\n        from poetry.config.config import Config\n        from poetry.config.file_config_source import FileConfigSource\n        from poetry.locations import CONFIG_DIR\n        from poetry.toml.file import TOMLFile\n\n        if self.option(\"migrate\"):\n            self._migrate()\n\n        config = Config.create()\n        config_file = TOMLFile(CONFIG_DIR / \"config.toml\")\n\n        try:\n            local_config_file = TOMLFile(self.poetry.file.path.parent / \"poetry.toml\")\n            if local_config_file.exists():\n                config.merge(local_config_file.read())\n        except (RuntimeError, PyProjectError):\n            local_config_file = TOMLFile(Path.cwd() / \"poetry.toml\")\n\n        if self.option(\"local\"):\n            config.set_config_source(FileConfigSource(local_config_file))\n\n        if not config_file.exists():\n            config_file.path.parent.mkdir(parents=True, exist_ok=True)\n            config_file.path.touch(mode=0o0600)\n\n        if self.option(\"list\"):\n            self._list_configuration(config.all(), config.raw())\n\n            return 0\n\n        setting_key = self.argument(\"key\")\n        if not setting_key:\n            return 0\n\n        if self.argument(\"value\") and self.option(\"unset\"):\n            raise RuntimeError(\"You can not combine a setting value with --unset\")\n\n        # show the value if no value is provided\n        if not self.argument(\"value\") and not self.option(\"unset\"):\n            if setting_key.split(\".\")[0] in self.LIST_PROHIBITED_SETTINGS:\n                raise ValueError(f\"Expected a value for {setting_key} setting.\")\n\n            value: str | dict[str, Any] | list[str]\n\n            if m := re.match(\n                r\"installer\\.build-config-settings(\\.([^.]+))?\", self.argument(\"key\")\n            ):\n                if not m.group(1):\n                    if value := config.get(\"installer.build-config-settings\"):\n                        self._list_configuration(value, value)\n                    else:\n                        self.line(\"No packages configured with build config settings.\")\n                else:\n                    package_name = canonicalize_name(m.group(2))\n                    key = f\"installer.build-config-settings.{package_name}\"\n\n                    if value := config.get(key):\n                        self.line(json.dumps(value))\n                    else:\n                        self.line(\n                            f\"No build config settings configured for <c1>{package_name}</>.\"\n                        )\n                return 0\n            elif m := re.match(r\"^repos?(?:itories)?(?:\\.(.+))?\", self.argument(\"key\")):\n                if not m.group(1):\n                    value = {}\n                    if config.get(\"repositories\") is not None:\n                        value = config.get(\"repositories\")\n                else:\n                    repo = config.get(f\"repositories.{m.group(1)}\")\n                    if repo is None:\n                        raise ValueError(f\"There is no {m.group(1)} repository defined\")\n\n                    value = repo\n\n                self.line(str(value))\n            else:\n                if setting_key not in self.unique_config_values:\n                    raise ValueError(f\"There is no {setting_key} setting.\")\n\n                value = config.get(setting_key)\n\n                if not isinstance(value, str):\n                    value = json.dumps(value)\n\n                self.line(value)\n\n            return 0\n\n        values: list[str] = self.argument(\"value\")\n\n        if setting_key in self.unique_config_values:\n            if self.option(\"unset\"):\n                config.config_source.remove_property(setting_key)\n                return 0\n\n            return self._handle_single_value(\n                config.config_source,\n                setting_key,\n                self.unique_config_values[setting_key],\n                values,\n            )\n\n        # handle repositories\n        m = re.match(r\"^repos?(?:itories)?(?:\\.(.+))?\", self.argument(\"key\"))\n        if m:\n            if not m.group(1):\n                raise ValueError(\"You cannot remove the [repositories] section\")\n\n            if self.option(\"unset\"):\n                repo = config.get(f\"repositories.{m.group(1)}\")\n                if repo is None:\n                    raise ValueError(f\"There is no {m.group(1)} repository defined\")\n\n                config.config_source.remove_property(f\"repositories.{m.group(1)}\")\n\n                return 0\n\n            if len(values) == 1:\n                url = values[0]\n\n                config.config_source.add_property(f\"repositories.{m.group(1)}.url\", url)\n\n                return 0\n\n            raise ValueError(\n                \"You must pass the url. \"\n                \"Example: poetry config repositories.foo https://bar.com\"\n            )\n\n        # handle auth\n        m = re.match(r\"^(http-basic|pypi-token)\\.(.+)\", self.argument(\"key\"))\n        if m:\n            from poetry.utils.password_manager import PasswordManager\n\n            password_manager = PasswordManager(config)\n            if self.option(\"unset\"):\n                if m.group(1) == \"http-basic\":\n                    password_manager.delete_http_password(m.group(2))\n                elif m.group(1) == \"pypi-token\":\n                    password_manager.delete_pypi_token(m.group(2))\n\n                return 0\n\n            if m.group(1) == \"http-basic\":\n                if len(values) == 1:\n                    username = values[0]\n                    # Only username, so we prompt for password\n                    password = self.secret(\"Password:\")\n                    assert isinstance(password, str)\n                elif len(values) != 2:\n                    raise ValueError(\n                        \"Expected one or two arguments \"\n                        f\"(username, password), got {len(values)}\"\n                    )\n                else:\n                    username = values[0]\n                    password = values[1]\n\n                password_manager.set_http_password(m.group(2), username, password)\n            elif m.group(1) == \"pypi-token\":\n                if len(values) != 1:\n                    raise ValueError(\n                        f\"Expected only one argument (token), got {len(values)}\"\n                    )\n\n                token = values[0]\n\n                password_manager.set_pypi_token(m.group(2), token)\n\n            return 0\n\n        # handle certs\n        m = re.match(r\"certificates\\.([^.]+)\\.(cert|client-cert)\", self.argument(\"key\"))\n        if m:\n            repository = m.group(1)\n            key = m.group(2)\n\n            if self.option(\"unset\"):\n                config.auth_config_source.remove_property(\n                    f\"certificates.{repository}.{key}\"\n                )\n\n                return 0\n\n            if len(values) == 1:\n                new_value: str | bool = values[0]\n\n                if key == \"cert\" and boolean_validator(values[0]):\n                    new_value = boolean_normalizer(values[0])\n\n                config.auth_config_source.add_property(\n                    f\"certificates.{repository}.{key}\", new_value\n                )\n            else:\n                raise ValueError(\"You must pass exactly 1 value\")\n\n            return 0\n\n        # handle build config settings\n        m = re.match(r\"installer\\.build-config-settings\\.([^.]+)\", self.argument(\"key\"))\n        if m:\n            key = f\"installer.build-config-settings.{canonicalize_name(m.group(1))}\"\n\n            if self.option(\"unset\"):\n                config.config_source.remove_property(key)\n                return 0\n\n            try:\n                settings = config.config_source.get_property(key)\n            except PropertyNotFoundError:\n                settings = {}\n\n            for value in values:\n                if build_config_setting_validator(value):\n                    config_settings = build_config_setting_normalizer(value)\n                    for setting_name, item in config_settings.items():\n                        settings[setting_name] = item\n                else:\n                    raise ValueError(\n                        f\"Invalid build config setting '{value}'. \"\n                        \"It must be a valid JSON with each property a string or a list of strings.\"\n                    )\n\n            config.config_source.add_property(key, settings)\n\n            return 0\n\n        raise ValueError(f\"Setting {self.argument('key')} does not exist\")\n\n    def _handle_single_value(\n        self,\n        source: ConfigSource,\n        key: str,\n        callbacks: tuple[Any, Any],\n        values: list[Any],\n    ) -> int:\n        validator, normalizer = callbacks\n\n        if len(values) > 1:\n            raise RuntimeError(\"You can only pass one value.\")\n\n        value = values[0]\n        if not validator(value):\n            raise RuntimeError(f'\"{value}\" is an invalid value for {key}')\n\n        source.add_property(key, normalizer(value))\n\n        return 0\n\n    def _list_configuration(\n        self, config: dict[str, Any], raw: dict[str, Any], k: str = \"\"\n    ) -> None:\n        orig_k = k\n        for key, value in sorted(config.items()):\n            if k + key in self.LIST_PROHIBITED_SETTINGS:\n                continue\n\n            raw_val = raw.get(key)\n\n            if isinstance(value, dict):\n                k += f\"{key}.\"\n                raw_val = cast(\"dict[str, Any]\", raw_val)\n                self._list_configuration(value, raw_val, k=k)\n                k = orig_k\n\n                continue\n            elif isinstance(value, list):\n                value = \", \".join(\n                    json.dumps(val) if isinstance(val, list) else val for val in value\n                )\n                value = f\"[{value}]\"\n\n            if k.startswith(\"repositories.\"):\n                message = f\"<c1>{k + key}</c1> = <c2>{json.dumps(raw_val)}</c2>\"\n            elif isinstance(raw_val, str) and raw_val != value:\n                message = (\n                    f\"<c1>{k + key}</c1> = <c2>{json.dumps(raw_val)}</c2>  # {value}\"\n                )\n            else:\n                message = f\"<c1>{k + key}</c1> = <c2>{json.dumps(value)}</c2>\"\n\n            self.line(message)\n\n    def _migrate(self) -> None:\n        from poetry.config.file_config_source import FileConfigSource\n        from poetry.locations import CONFIG_DIR\n        from poetry.toml.file import TOMLFile\n\n        config_file = TOMLFile(CONFIG_DIR / \"config.toml\")\n\n        if self.option(\"local\"):\n            config_file = TOMLFile(self.poetry.file.path.parent / \"poetry.toml\")\n            if not config_file.exists():\n                raise RuntimeError(\"No local config file found\")\n\n        config_source = FileConfigSource(config_file)\n\n        self.io.write_line(\"Checking for required migrations ...\")\n\n        required_migrations = [\n            migration\n            for migration in CONFIG_MIGRATIONS\n            if migration.dry_run(config_source, io=self.io)\n        ]\n\n        if not required_migrations:\n            self.io.write_line(\"Already up to date.\")\n            return\n\n        if not self.io.is_interactive() or self.confirm(\n            \"Proceed with migration?: \", False\n        ):\n            for migration in required_migrations:\n                migration.apply(config_source)\n\n            self.io.write_line(\"Config migration successfully done.\")\n"
  },
  {
    "path": "src/poetry/console/commands/debug/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/debug/info.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\n\nfrom poetry.console.commands.command import Command\n\n\nclass DebugInfoCommand(Command):\n    name = \"debug info\"\n    description = \"Shows debug information.\"\n\n    def handle(self) -> int:\n        poetry_python_version = \".\".join(str(s) for s in sys.version_info[:3])\n\n        self.line(\"\")\n        self.line(\"<b>Poetry</b>\")\n        self.line(\n            \"\\n\".join(\n                [\n                    f\"<info>Version</info>:    <comment>{self.poetry.VERSION}</>\",\n                    f\"<info>Python</info>:     <comment>{poetry_python_version}</>\",\n                    f\"<info>Path</info>:       <comment>{Path(sys.prefix)}</>\",\n                    f\"<info>Executable</info>: <comment>{Path(sys.executable) if sys.executable else 'Unknown'}</>\",\n                ]\n            )\n        )\n        command = self.get_application().get(\"env info\")\n\n        exit_code: int = command.run(self.io)\n        return exit_code\n"
  },
  {
    "path": "src/poetry/console/commands/debug/resolve.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom cleo.io.outputs.output import Verbosity\n\nfrom poetry.console.commands.init import InitCommand\nfrom poetry.console.commands.show import ShowCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from cleo.ui.table import Rows\n\n\nclass DebugResolveCommand(InitCommand):\n    name = \"debug resolve\"\n    description = \"Debugs dependency resolution.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"package\", \"The packages to resolve.\", optional=True, multiple=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\n            \"extras\",\n            \"E\",\n            \"Extras to activate for the dependency.\",\n            flag=False,\n            multiple=True,\n        ),\n        option(\"python\", None, \"Python version(s) to use for resolution.\", flag=False),\n        option(\"tree\", None, \"Display the dependency tree.\"),\n        option(\"install\", None, \"Show what would be installed for the current system.\"),\n    ]\n\n    loggers: ClassVar[list[str]] = [\n        \"poetry.repositories.pypi_repository\",\n        \"poetry.inspection.info\",\n    ]\n\n    def handle(self) -> int:\n        from cleo.io.null_io import NullIO\n        from poetry.core.packages.project_package import ProjectPackage\n\n        from poetry.factory import Factory\n        from poetry.puzzle.solver import Solver\n        from poetry.repositories.repository import Repository\n        from poetry.repositories.repository_pool import RepositoryPool\n        from poetry.utils.env import EnvManager\n\n        packages = self.argument(\"package\")\n\n        if not packages:\n            package = self.poetry.package\n        else:\n            # Using current pool for determine_requirements()\n            self._pool = self.poetry.pool\n\n            package = ProjectPackage(\n                self.poetry.package.name, self.poetry.package.version\n            )\n\n            # Silencing output\n            verbosity = self.io.output.verbosity\n            self.io.output.set_verbosity(Verbosity.QUIET)\n\n            requirements = self._determine_requirements(packages)\n\n            self.io.output.set_verbosity(verbosity)\n\n            for constraint in requirements:\n                name = constraint.pop(\"name\")\n                assert isinstance(name, str)\n                extras = []\n                for extra in self.option(\"extras\"):\n                    extras += extra.split()\n\n                constraint[\"extras\"] = extras\n\n                package.add_dependency(Factory.create_dependency(name, constraint))\n\n        package.python_versions = self.option(\"python\") or (\n            self.poetry.package.python_versions\n        )\n\n        pool = self.poetry.pool\n\n        solver = Solver(package, pool, [], [], self.io)\n\n        ops = solver.solve().calculate_operations()\n\n        self.line(\"\")\n        self.line(\"Resolution results:\")\n        self.line(\"\")\n\n        if self.option(\"tree\"):\n            show_command = self.get_application().find(\"show\")\n            assert isinstance(show_command, ShowCommand)\n            show_command.init_styles(self.io)\n\n            packages = [op.package for op in ops]\n\n            requires = package.all_requires\n            for pkg in packages:\n                for require in requires:\n                    if pkg.name == require.name:\n                        show_command.display_package_tree(self.io, pkg, packages)\n                        break\n\n            return 0\n\n        table = self.table(style=\"compact\")\n        table.style.set_vertical_border_chars(\"\", \" \")\n        rows: Rows = []\n\n        if self.option(\"install\"):\n            env = EnvManager(self.poetry).get()\n            pool = RepositoryPool(config=self.poetry.config)\n            locked_repository = Repository(\"poetry-locked\")\n            for op in ops:\n                locked_repository.add_package(op.package)\n\n            pool.add_repository(locked_repository)\n\n            solver = Solver(package, pool, [], [], NullIO())\n            with solver.use_environment(env):\n                ops = solver.solve().calculate_operations()\n\n        for op in ops:\n            if self.option(\"install\") and op.skipped:\n                continue\n\n            pkg = op.package\n            row = [\n                f\"<c1>{pkg.complete_name}</c1>\",\n                f\"<b>{pkg.version}</b>\",\n            ]\n\n            if not pkg.marker.is_any():\n                row[2] = str(pkg.marker)\n\n            rows.append(row)\n\n        table.set_rows(rows)\n        table.render()\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/debug/tags.py",
    "content": "from __future__ import annotations\n\nfrom poetry.console.commands.env_command import EnvCommand\n\n\nclass DebugTagsCommand(EnvCommand):\n    name = \"debug tags\"\n    description = \"Shows compatible tags for your project's current active environment.\"\n\n    def handle(self) -> int:\n        for tag in self.env.get_supported_tags():\n            self.io.write_line(\n                f\"<c1>{tag.interpreter}</>\"\n                f\"-<c2>{tag.abi}</>\"\n                f\"-<fg=yellow;options=bold>{tag.platform}</>\"\n            )\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/env/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/env/activate.py",
    "content": "from __future__ import annotations\n\nimport shlex\n\nfrom typing import TYPE_CHECKING\n\nimport shellingham\n\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.utils._compat import WINDOWS\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from poetry.utils.env import Env\n\n\nclass ShellNotSupportedError(Exception):\n    \"\"\"Raised when a shell doesn't have an activator in virtual environment\"\"\"\n\n\nclass EnvActivateCommand(EnvCommand):\n    name = \"env activate\"\n    description = \"Print the command to activate a virtual environment.\"\n\n    def handle(self) -> int:\n        from poetry.utils.env import EnvManager\n\n        env = EnvManager(self.poetry).get()\n\n        try:\n            shell, _ = shellingham.detect_shell()\n        except shellingham.ShellDetectionFailure:\n            shell = \"\"\n\n        if command := self._get_activate_command(env, shell):\n            self.line(command)\n            return 0\n\n        raise ShellNotSupportedError(\n            f\"Discovered shell '{shell}' doesn't have an activator in virtual environment\"\n        )\n\n    def _get_activate_command(self, env: Env, shell: str) -> str:\n        if shell == \"fish\":\n            command, filename = \"source\", \"activate.fish\"\n        elif shell == \"nu\":\n            command, filename = \"overlay use\", \"activate.nu\"\n        elif shell in [\"csh\", \"tcsh\"]:\n            command, filename = \"source\", \"activate.csh\"\n        elif shell in [\"powershell\", \"pwsh\"]:\n            command, filename = \"&\", \"activate.ps1\"\n        elif shell == \"cmd\":\n            command, filename = \"\", \"activate.bat\"\n        elif shell in [\"bash\", \"mksh\", \"zsh\"]:\n            command, filename = \"source\", \"activate\"\n        else:\n            command, filename = \".\", \"activate\"\n\n        if (activation_script := env.bin_dir / filename).exists():\n            quoted = self._quote(activation_script, shell)\n            return f\"{command} {quoted}\".strip()\n        return \"\"\n\n    @staticmethod\n    def _quote(activation_script: Path, shell: str) -> str:\n        if WINDOWS and shell in {\"cmd\", \"powershell\", \"pwsh\"}:\n            return f'\"{activation_script}\"'\n        return shlex.quote(activation_script.as_posix())\n"
  },
  {
    "path": "src/poetry/console/commands/env/info.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n    from poetry.utils.env import Env\n\n\nclass EnvInfoCommand(Command):\n    name = \"env info\"\n    description = \"Displays information about the current environment.\"\n\n    options: ClassVar[list[Option]] = [\n        option(\"path\", \"p\", \"Only display the environment's path.\"),\n        option(\n            \"executable\", \"e\", \"Only display the environment's python executable path.\"\n        ),\n    ]\n\n    def handle(self) -> int:\n        from poetry.utils.env import EnvManager\n\n        env = EnvManager(self.poetry).get()\n\n        if self.option(\"path\"):\n            if not env.is_venv():\n                return 1\n\n            self.line(str(env.path))\n\n            return 0\n\n        if self.option(\"executable\"):\n            if not env.is_venv():\n                return 1\n\n            self.line(str(env.python))\n\n            return 0\n\n        self._display_complete_info(env)\n        return 0\n\n    def _display_complete_info(self, env: Env) -> None:\n        env_python_version = \".\".join(str(s) for s in env.version_info[:3])\n        self.line(\"\")\n        self.line(\"<b>Virtualenv</b>\")\n        listing = [\n            f\"<info>Python</info>:         <comment>{env_python_version}</>\",\n            f\"<info>Implementation</info>: <comment>{env.python_implementation}</>\",\n            (\n                \"<info>Path</info>:          \"\n                f\" <comment>{env.path if env.is_venv() else 'NA'}</>\"\n            ),\n            (\n                \"<info>Executable</info>:    \"\n                f\" <comment>{env.python if env.is_venv() else 'NA'}</>\"\n            ),\n        ]\n        if env.is_venv():\n            listing.append(\n                \"<info>Valid</info>:         \"\n                f\" <{'comment' if env.is_sane() else 'error'}>{env.is_sane()}</>\"\n            )\n        self.line(\"\\n\".join(listing))\n\n        self.line(\"\")\n\n        base_env = env.parent_env\n        python = \".\".join(str(v) for v in base_env.version_info[:3])\n        self.line(\"<b>Base</b>\")\n        self.line(\n            \"\\n\".join(\n                [\n                    f\"<info>Platform</info>:   <comment>{env.platform}</>\",\n                    f\"<info>OS</info>:         <comment>{env.os}</>\",\n                    f\"<info>Python</info>:     <comment>{python}</>\",\n                    f\"<info>Path</info>:       <comment>{base_env.path}</>\",\n                    f\"<info>Executable</info>: <comment>{base_env.python}</>\",\n                ]\n            )\n        )\n"
  },
  {
    "path": "src/poetry/console/commands/env/list.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass EnvListCommand(Command):\n    name = \"env list\"\n    description = \"Lists all virtualenvs associated with the current project.\"\n\n    options: ClassVar[list[Option]] = [\n        option(\"full-path\", None, \"Output the full paths of the virtualenvs.\")\n    ]\n\n    def handle(self) -> int:\n        from poetry.utils.env import EnvManager\n\n        manager = EnvManager(self.poetry)\n        current_env = manager.get()\n\n        for venv in manager.list():\n            name = venv.path.name\n            if self.option(\"full-path\"):\n                name = str(venv.path)\n\n            if venv == current_env:\n                self.line(f\"<info>{name} (Activated)</info>\")\n\n                continue\n\n            self.line(name)\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/env/remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass EnvRemoveCommand(Command):\n    name = \"env remove\"\n    description = \"Remove virtual environments associated with the project.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"python\",\n            \"The python executables associated with, or names of the virtual\"\n            \" environments which are to be removed.\",\n            optional=True,\n            multiple=True,\n        )\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\n            \"all\",\n            description=(\n                \"Remove all managed virtual environments associated with the project.\"\n            ),\n        ),\n    ]\n\n    def handle(self) -> int:\n        from poetry.utils.env import EnvManager\n\n        is_in_project = self.poetry.config.get(\"virtualenvs.in-project\")\n\n        pythons = self.argument(\"python\")\n        remove_all_envs = self.option(\"all\")\n\n        if not (pythons or remove_all_envs or is_in_project):\n            self.line(\"No virtualenv provided.\")\n\n        manager = EnvManager(self.poetry)\n        # TODO: refactor env.py to allow removal with one loop\n        for python in pythons:\n            venv = manager.remove(python)\n            self.line(f\"Deleted virtualenv: <comment>{venv.path}</comment>\")\n        if remove_all_envs or is_in_project:\n            for venv in manager.list():\n                if not is_in_project or venv.path.is_relative_to(\n                    self.poetry.pyproject_path.parent\n                ):\n                    manager.remove_venv(venv.path)\n                    self.line(f\"Deleted virtualenv: <comment>{venv.path}</comment>\")\n            # Since we remove all the virtualenvs, we can also remove the entry\n            # in the envs file. (Strictly speaking, we should do this explicitly,\n            # in case it points to a virtualenv that had been removed manually before.)\n            if remove_all_envs and manager.envs_file.exists():\n                manager.envs_file.remove_section(manager.base_env_name)\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/env/use.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n\n\nclass EnvUseCommand(Command):\n    name = \"env use\"\n    description = \"Activates or creates a new virtualenv for the current project.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"python\", \"The python executable to use.\")\n    ]\n\n    def handle(self) -> int:\n        from poetry.utils.env import EnvManager\n\n        manager = EnvManager(self.poetry, io=self.io)\n\n        if self.argument(\"python\") == \"system\":\n            manager.deactivate()\n\n            return 0\n\n        env = manager.activate(self.argument(\"python\"))\n\n        self.line(f\"Using virtualenv: <comment>{env.path}</>\")\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/env_command.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from poetry.utils.env import Env\n\n\nclass EnvCommand(Command):\n    def __init__(self) -> None:\n        # Set in poetry.console.application.Application.configure_env\n        self._env: Env | None = None\n\n        super().__init__()\n\n    @property\n    def env(self) -> Env:\n        assert self._env is not None\n        return self._env\n\n    def set_env(self, env: Env) -> None:\n        self._env = env\n"
  },
  {
    "path": "src/poetry/console/commands/group_command.py",
    "content": "from __future__ import annotations\n\nfrom collections import defaultdict\nfrom typing import TYPE_CHECKING\n\nfrom cleo.helpers import option\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.console.commands.command import Command\nfrom poetry.console.exceptions import GroupNotFoundError\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n    from poetry.core.packages.project_package import ProjectPackage\n\n\nclass GroupCommand(Command):\n    @staticmethod\n    def _group_dependency_options() -> list[Option]:\n        return [\n            option(\n                \"without\",\n                None,\n                \"The dependency groups to ignore.\",\n                flag=False,\n                multiple=True,\n            ),\n            option(\n                \"with\",\n                None,\n                \"The optional dependency groups to include.\",\n                flag=False,\n                multiple=True,\n            ),\n            option(\n                \"only\",\n                None,\n                \"The only dependency groups to include.\",\n                flag=False,\n                multiple=True,\n            ),\n        ]\n\n    @property\n    def non_optional_groups(self) -> set[str]:\n        # TODO: this should move into poetry-core\n        return {\n            group.name\n            for group in self.poetry.package._dependency_groups.values()\n            if not group.is_optional()\n        }\n\n    @property\n    def default_group(self) -> str | None:\n        \"\"\"\n        The default group to use when no group is specified. This is useful\n        for command that have the `--group` option, eg: add, remove.\n\n        Can be overridden to adapt behavior.\n        \"\"\"\n        return None\n\n    @property\n    def default_groups(self) -> set[str]:\n        \"\"\"\n        The groups that are considered by the command by default.\n\n        Can be overridden to adapt behavior.\n        \"\"\"\n        return self.non_optional_groups\n\n    @property\n    def activated_groups(self) -> set[NormalizedName]:\n        groups = {}\n\n        for key in {\"with\", \"without\", \"only\"}:\n            groups[key] = {\n                group.strip()\n                for groups in self.option(key, \"\")\n                for group in groups.split(\",\")\n            }\n\n        if self.option(\"all-groups\"):\n            groups[\"with\"] = self.poetry.package.dependency_group_names(\n                include_optional=True\n            )\n\n        self._validate_group_options(groups)\n\n        if groups[\"only\"] and (groups[\"with\"] or groups[\"without\"]):\n            self.line_error(\n                \"<warning>The `<fg=yellow;options=bold>--with</>` and \"\n                \"`<fg=yellow;options=bold>--without</>` options are ignored when used\"\n                \" along with the `<fg=yellow;options=bold>--only</>` option.\"\n                \"</warning>\"\n            )\n\n        # Normalize after validating so that original names are printed\n        # in case of an error.\n        norm_groups = {\n            key: {canonicalize_name(group) for group in key_groups}\n            for key, key_groups in groups.items()\n        }\n        norm_default_groups = {canonicalize_name(name) for name in self.default_groups}\n\n        return norm_groups[\"only\"] or norm_default_groups.union(\n            norm_groups[\"with\"]\n        ).difference(norm_groups[\"without\"])\n\n    def project_with_activated_groups_only(self) -> ProjectPackage:\n        return self.poetry.package.with_dependency_groups(\n            list(self.activated_groups), only=True\n        )\n\n    def _validate_group_options(self, group_options: dict[str, set[str]]) -> None:\n        \"\"\"\n        Raises an error if it detects that a group is not part of pyproject.toml\n        \"\"\"\n        invalid_options = defaultdict(set)\n        for opt, groups in group_options.items():\n            for group in groups:\n                if not self.poetry.package.has_dependency_group(group):\n                    invalid_options[group].add(opt)\n        if invalid_options:\n            message_parts = []\n            for group in sorted(invalid_options):\n                opts = \", \".join(\n                    f\"<fg=yellow;options=bold>--{opt}</>\"\n                    for opt in sorted(invalid_options[group])\n                )\n                message_parts.append(f\"{group} (via {opts})\")\n            raise GroupNotFoundError(f\"Group(s) not found: {', '.join(message_parts)}\")\n"
  },
  {
    "path": "src/poetry/console/commands/init.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom collections.abc import Mapping\nfrom contextlib import suppress\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\nfrom tomlkit import inline_table\n\nfrom poetry.console.commands.command import Command\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.utils.dependency_specification import RequirementsParser\nfrom poetry.utils.env.python import Python\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import Package\n    from tomlkit.items import InlineTable\n\n    from poetry.repositories import RepositoryPool\n\nRequirements = dict[str, str | Mapping[str, Any]]\n\n\nclass InitCommand(Command):\n    name = \"init\"\n    description = (\n        \"Creates a basic <comment>pyproject.toml</> file in the current directory.\"\n    )\n\n    options: ClassVar[list[Option]] = [\n        option(\"name\", None, \"Name of the package.\", flag=False),\n        option(\"description\", None, \"Description of the package.\", flag=False),\n        option(\"author\", None, \"Author name of the package.\", flag=False),\n        option(\"python\", None, \"Compatible Python versions.\", flag=False),\n        option(\n            \"dependency\",\n            None,\n            \"Package to require, with an optional version constraint, \"\n            \"e.g. requests:^2.10.0 or requests=2.11.1.\",\n            flag=False,\n            multiple=True,\n        ),\n        option(\n            \"dev-dependency\",\n            None,\n            \"Package to require for development, with an optional version\"\n            \" constraint, e.g. requests:^2.10.0 or requests=2.11.1.\",\n            flag=False,\n            multiple=True,\n        ),\n        option(\"license\", \"l\", \"License of the package.\", flag=False),\n    ]\n\n    help = \"\"\"\\\nThe <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the\\\n current directory.\n\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n\n        self._pool: RepositoryPool | None = None\n\n    def handle(self) -> int:\n        from pathlib import Path\n\n        project_path = Path.cwd()\n\n        if self.io.input.option(\"project\"):\n            project_path = Path(self.io.input.option(\"project\"))\n            if not project_path.exists() or not project_path.is_dir():\n                self.line_error(\"<error>The --project path is not a directory.</error>\")\n                return 1\n\n        return self._init_pyproject(project_path=project_path)\n\n    def _init_pyproject(\n        self,\n        project_path: Path,\n        allow_interactive: bool = True,\n        layout_name: str = \"standard\",\n        readme_format: str = \"md\",\n        allow_layout_creation_on_empty: bool = False,\n    ) -> int:\n        from poetry.core.vcs.git import GitConfig\n\n        from poetry.config.config import Config\n        from poetry.layouts import layout\n        from poetry.pyproject.toml import PyProjectTOML\n\n        is_interactive = self.io.is_interactive() and allow_interactive\n\n        pyproject = PyProjectTOML(project_path / \"pyproject.toml\")\n\n        if pyproject.file.exists():\n            if pyproject.is_poetry_project():\n                self.line_error(\n                    \"<error>A pyproject.toml file with a project and/or\"\n                    \" a poetry section already exists.</error>\"\n                )\n                return 1\n\n            if pyproject.data.get(\"build-system\"):\n                self.line_error(\n                    \"<error>A pyproject.toml file with a defined build-system already\"\n                    \" exists.</error>\"\n                )\n                return 1\n\n        vcs_config = GitConfig()\n\n        if is_interactive:\n            self.line(\"\")\n            self.line(\n                \"This command will guide you through creating your\"\n                \" <info>pyproject.toml</> config.\"\n            )\n            self.line(\"\")\n\n        name = self.option(\"name\")\n        if not name:\n            name = project_path.name.lower()\n\n            if is_interactive:\n                question = self.create_question(\n                    f\"Package name [<comment>{name}</comment>]: \", default=name\n                )\n                name = self.ask(question)\n\n        version = \"0.1.0\"\n\n        if is_interactive:\n            question = self.create_question(\n                f\"Version [<comment>{version}</comment>]: \", default=version\n            )\n            version = self.ask(question)\n\n        description = self.option(\"description\") or \"\"\n        if not description and is_interactive:\n            description = self.ask(self.create_question(\"Description []: \", default=\"\"))\n\n        author = self.option(\"author\")\n        if not author and vcs_config.get(\"user.name\"):\n            author = vcs_config[\"user.name\"]\n            author_email = vcs_config.get(\"user.email\")\n            if author_email:\n                author += f\" <{author_email}>\"\n\n        if is_interactive:\n            question = self.create_question(\n                f\"Author [<comment>{author}</comment>, n to skip]: \", default=author\n            )\n            question.set_validator(lambda v: self._validate_author(v, author))\n            author = self.ask(question)\n\n        authors = [author] if author else []\n\n        license_name = self.option(\"license\")\n        if not license_name and is_interactive:\n            license_name = self.ask(self.create_question(\"License []: \", default=\"\"))\n\n        python = self.option(\"python\")\n        if not python:\n            config = Config.create()\n            python = (\n                \">=\"\n                + Python.get_preferred_python(config, self.io).minor_version.to_string()\n            )\n\n            if is_interactive:\n                question = self.create_question(\n                    f\"Compatible Python versions [<comment>{python}</comment>]: \",\n                    default=python,\n                )\n                python = self.ask(question)\n\n        if is_interactive:\n            self.line(\"\")\n\n        requirements: Requirements = {}\n        if self.option(\"dependency\"):\n            requirements = self._format_requirements(\n                self._determine_requirements(self.option(\"dependency\"))\n            )\n\n        question_text = \"Would you like to define your main dependencies interactively?\"\n        help_message = \"\"\"\\\n        You can specify a package in the following forms:\n          - A single name (<b>requests</b>): this will search for matches on PyPI\n          - A name and a constraint (<b>requests@^2.23.0</b>)\n          - A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)\n          - A git url with a revision\\\n         (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)\n          - A file path (<b>../my-package/my-package.whl</b>)\n          - A directory (<b>../my-package/</b>)\n          - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)\n        \"\"\"\n\n        help_displayed = False\n        if is_interactive and self.confirm(question_text, True):\n            self.line(help_message)\n            help_displayed = True\n            requirements.update(\n                self._format_requirements(self._determine_requirements([]))\n            )\n            self.line(\"\")\n\n        dev_requirements: Requirements = {}\n        if self.option(\"dev-dependency\"):\n            dev_requirements = self._format_requirements(\n                self._determine_requirements(self.option(\"dev-dependency\"))\n            )\n\n        question_text = (\n            \"Would you like to define your development dependencies interactively?\"\n        )\n        if is_interactive and self.confirm(question_text, True):\n            if not help_displayed:\n                self.line(help_message)\n\n            dev_requirements.update(\n                self._format_requirements(self._determine_requirements([]))\n            )\n\n            self.line(\"\")\n\n        layout_ = layout(layout_name)(\n            name,\n            version,\n            description=description,\n            author=authors[0] if authors else None,\n            readme_format=readme_format,\n            license=license_name,\n            python=python,\n            dependencies=requirements,\n            dev_dependencies=dev_requirements,\n        )\n\n        create_layout = not project_path.exists() or (\n            allow_layout_creation_on_empty and not any(project_path.iterdir())\n        )\n\n        if create_layout:\n            layout_.create(project_path, with_pyproject=False)\n\n        content = layout_.generate_project_content(project_path)\n        for section, item in content.items():\n            pyproject.data.append(section, item)\n\n        if is_interactive:\n            self.line(\"<info>Generated file</info>\")\n            self.line(\"\")\n            self.line(pyproject.data.as_string().replace(\"\\r\\n\", \"\\n\"))\n            self.line(\"\")\n\n        if is_interactive and not self.confirm(\"Do you confirm generation?\", True):\n            self.line_error(\"<error>Command aborted</error>\")\n\n            return 1\n\n        pyproject.save()\n\n        if create_layout:\n            path = project_path.resolve()\n\n            with suppress(ValueError):\n                path = path.relative_to(Path.cwd())\n\n            self.line(\n                f\"Created package <info>{layout_._package_name}</> in\"\n                f\" <fg=blue>{path.as_posix()}</>\"\n            )\n\n        return 0\n\n    def _generate_choice_list(\n        self, matches: list[Package], canonicalized_name: NormalizedName\n    ) -> list[str]:\n        choices = []\n        matches_names = [p.name for p in matches]\n        exact_match = canonicalized_name in matches_names\n        if exact_match:\n            choices.append(matches[matches_names.index(canonicalized_name)].pretty_name)\n\n        for found_package in matches:\n            if len(choices) >= 10:\n                break\n\n            if found_package.name == canonicalized_name:\n                continue\n\n            choices.append(found_package.pretty_name)\n\n        return choices\n\n    def _determine_requirements(\n        self,\n        requires: list[str],\n        allow_prereleases: bool | None = None,\n        source: str | None = None,\n        is_interactive: bool | None = None,\n    ) -> list[dict[str, Any]]:\n        if is_interactive is None:\n            is_interactive = self.io.is_interactive()\n\n        if not requires:\n            result = []\n\n            question = self.create_question(\n                \"Package to add or search for (leave blank to skip):\"\n            )\n            question.set_validator(self._validate_package)\n\n            follow_up_question = self.create_question(\n                \"\\nAdd a package (leave blank to skip):\"\n            )\n            follow_up_question.set_validator(self._validate_package)\n\n            package = self.ask(question)\n            while package:\n                constraint = self._parse_requirements([package])[0]\n                if (\n                    \"git\" in constraint\n                    or \"url\" in constraint\n                    or \"path\" in constraint\n                    or \"version\" in constraint\n                ):\n                    self.line(f\"Adding <info>{package}</info>\")\n                    result.append(constraint)\n                    package = self.ask(follow_up_question)\n                    continue\n\n                canonicalized_name = canonicalize_name(constraint[\"name\"])\n                matches = self._get_pool().search(canonicalized_name)\n                if not matches:\n                    self.line_error(\"<error>Unable to find package</error>\")\n                    package = False\n                else:\n                    choices = self._generate_choice_list(matches, canonicalized_name)\n\n                    info_string = (\n                        f\"Found <info>{len(matches)}</info> packages matching\"\n                        f\" <c1>{package}</c1>\"\n                    )\n\n                    if len(matches) > 10:\n                        info_string += \"\\nShowing the first 10 matches\"\n\n                    self.line(info_string)\n\n                    # Default to an empty value to signal no package was selected\n                    choices.append(\"\")\n\n                    package = self.choice(\n                        \"\\nEnter package # to add, or the complete package name if\"\n                        \" it is not listed\",\n                        choices,\n                        attempts=3,\n                        default=len(choices) - 1,\n                    )\n\n                    if not package:\n                        self.line(\"<warning>No package selected</warning>\")\n\n                    # package selected by user, set constraint name to package name\n                    if package:\n                        constraint[\"name\"] = package\n\n                # no constraint yet, determine the best version automatically\n                if package and \"version\" not in constraint:\n                    question = self.create_question(\n                        \"Enter the version constraint to require \"\n                        \"(or leave blank to use the latest version):\"\n                    )\n                    question.set_max_attempts(3)\n                    question.set_validator(lambda x: (x or \"\").strip() or None)\n\n                    package_constraint = self.ask(question)\n\n                    if package_constraint is None:\n                        _, package_constraint = self._find_best_version_for_package(\n                            package\n                        )\n\n                        self.line(\n                            f\"Using version <b>{package_constraint}</b> for\"\n                            f\" <c1>{package}</c1>\"\n                        )\n\n                    constraint[\"version\"] = package_constraint\n\n                if package:\n                    result.append(constraint)\n\n                if is_interactive:\n                    package = self.ask(follow_up_question)\n\n            return result\n\n        result = []\n        for requirement in self._parse_requirements(requires):\n            if \"git\" in requirement or \"url\" in requirement or \"path\" in requirement:\n                result.append(requirement)\n                continue\n            elif \"version\" not in requirement:\n                # determine the best version automatically\n                name, version = self._find_best_version_for_package(\n                    requirement[\"name\"],\n                    allow_prereleases=allow_prereleases,\n                    source=source,\n                )\n                requirement[\"version\"] = version\n                requirement[\"name\"] = name\n\n                self.line(f\"Using version <b>{version}</b> for <c1>{name}</c1>\")\n            else:\n                # check that the specified version/constraint exists\n                # before we proceed\n                name, _ = self._find_best_version_for_package(\n                    requirement[\"name\"],\n                    requirement[\"version\"],\n                    allow_prereleases=allow_prereleases,\n                    source=source,\n                )\n\n                requirement[\"name\"] = name\n\n            result.append(requirement)\n\n        return result\n\n    def _find_best_version_for_package(\n        self,\n        name: str,\n        required_version: str | None = None,\n        allow_prereleases: bool | None = None,\n        source: str | None = None,\n    ) -> tuple[str, str]:\n        from poetry.version.version_selector import VersionSelector\n\n        selector = VersionSelector(self._get_pool())\n        package = selector.find_best_candidate(\n            name, required_version, allow_prereleases=allow_prereleases, source=source\n        )\n\n        if not package:\n            # TODO: find similar\n            raise ValueError(f\"Could not find a matching version of package {name}\")\n\n        version = package.version.without_local()\n        return package.pretty_name, f\"^{version.to_string()}\"\n\n    def _parse_requirements(self, requirements: list[str]) -> list[dict[str, Any]]:\n        from poetry.core.pyproject.exceptions import PyProjectError\n\n        try:\n            cwd = self.poetry.file.path.parent\n            artifact_cache = self.poetry.pool.artifact_cache\n        except (PyProjectError, RuntimeError):\n            cwd = Path.cwd()\n            artifact_cache = self._get_pool().artifact_cache\n\n        parser = RequirementsParser(\n            artifact_cache=artifact_cache,\n            env=self.env if isinstance(self, EnvCommand) else None,\n            cwd=cwd,\n        )\n        return [\n            parser.parse(re.sub(r\"@\\s*latest$\", \"\", requirement, flags=re.I))\n            for requirement in requirements\n        ]\n\n    def _format_requirements(self, requirements: list[dict[str, str]]) -> Requirements:\n        requires: Requirements = {}\n        for requirement in requirements:\n            name = requirement.pop(\"name\")\n            constraint: str | InlineTable\n            if \"version\" in requirement and len(requirement) == 1:\n                constraint = requirement[\"version\"]\n            else:\n                constraint = inline_table()\n                constraint.trivia.trail = \"\\n\"\n                constraint.update(requirement)\n\n            requires[name] = constraint\n\n        return requires\n\n    @staticmethod\n    def _validate_author(author: str, default: str) -> str | None:\n        from poetry.core.utils.helpers import combine_unicode\n        from poetry.core.utils.patterns import AUTHOR_REGEX\n\n        author = combine_unicode(author or default)\n\n        if author in [\"n\", \"no\"]:\n            return None\n\n        m = AUTHOR_REGEX.match(author)\n        if not m:\n            raise ValueError(\n                \"Invalid author string. Must be in the format: \"\n                \"John Smith <john@example.com>\"\n            )\n\n        return author\n\n    @staticmethod\n    def _validate_package(package: str | None) -> str | None:\n        if package and len(package.split()) > 2:\n            raise ValueError(\"Invalid package definition.\")\n\n        return package\n\n    def _get_pool(self) -> RepositoryPool:\n        from poetry.config.config import Config\n        from poetry.repositories import RepositoryPool\n        from poetry.repositories.pypi_repository import PyPiRepository\n\n        if isinstance(self, EnvCommand):\n            return self.poetry.pool\n\n        if self._pool is None:\n            self._pool = RepositoryPool()\n            pool_size = Config.create().installer_max_workers\n            self._pool.add_repository(PyPiRepository(pool_size=pool_size))\n\n        return self._pool\n"
  },
  {
    "path": "src/poetry/console/commands/install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.installer_command import InstallerCommand\nfrom poetry.plugins.plugin_manager import PluginManager\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n    from packaging.utils import NormalizedName\n\n\nclass InstallCommand(InstallerCommand):\n    name = \"install\"\n    description = \"Installs the project dependencies.\"\n\n    options: ClassVar[list[Option]] = [\n        *InstallerCommand._group_dependency_options(),\n        option(\n            \"sync\",\n            None,\n            \"Synchronize the environment with the locked packages and the specified\"\n            \" groups. (<warning>Deprecated</warning>)\",\n        ),\n        option(\n            \"no-root\", None, \"Do not install the root package (the current project).\"\n        ),\n        option(\n            \"no-directory\",\n            None,\n            \"Do not install any directory path dependencies; useful to install\"\n            \" dependencies without source code, e.g. for caching of Docker layers)\",\n            flag=True,\n            multiple=False,\n        ),\n        option(\n            \"dry-run\",\n            None,\n            \"Output the operations but do not execute anything \"\n            \"(implicitly enables --verbose).\",\n        ),\n        option(\n            \"extras\",\n            \"E\",\n            \"Extra sets of dependencies to install.\",\n            flag=False,\n            multiple=True,\n        ),\n        option(\"all-extras\", None, \"Install all extra dependencies.\"),\n        option(\"all-groups\", None, \"Install dependencies from all groups.\"),\n        option(\"only-root\", None, \"Exclude all dependencies.\"),\n        option(\n            \"compile\",\n            None,\n            \"Compile Python source files to bytecode.\",\n        ),\n    ]\n\n    help = \"\"\"\\\nThe <info>install</info> command reads the <comment>poetry.lock</> file from\nthe current directory, processes it, and downloads and installs all the\nlibraries and dependencies outlined in that file. If the file does not\nexist it will look for <comment>pyproject.toml</> and do the same.\n\n<info>poetry install</info>\n\nBy default, the above command will also install the current project. To install only the\ndependencies and not including the current project, run the command with the\n<info>--no-root</info> option like below:\n\n<info> poetry install --no-root</info>\n\nIf you want to use Poetry only for dependency management but not for packaging,\nyou can set the \"package-mode\" to false in your pyproject.toml file.\n\"\"\"\n\n    _loggers: ClassVar[list[str]] = [\n        \"poetry.repositories.pypi_repository\",\n        \"poetry.inspection.info\",\n    ]\n\n    @property\n    def activated_groups(self) -> set[NormalizedName]:\n        if self.option(\"only-root\"):\n            return set()\n        else:\n            return super().activated_groups\n\n    @property\n    def _alternative_sync_command(self) -> str:\n        return \"poetry sync\"\n\n    @property\n    def _with_synchronization(self) -> bool:\n        with_synchronization = self.option(\"sync\")\n        if with_synchronization:\n            self.line_error(\n                \"<warning>The `<fg=yellow;options=bold>--sync</>` option is\"\n                \" deprecated and slated for removal, use the\"\n                f\" `<fg=yellow;options=bold>{self._alternative_sync_command}</>`\"\n                \" command instead.</warning>\"\n            )\n        return bool(with_synchronization)\n\n    def handle(self) -> int:\n        from poetry.core.masonry.utils.module import ModuleOrPackageNotFoundError\n\n        from poetry.masonry.builders.editable import EditableBuilder\n\n        if not self.option(\"no-plugins\"):\n            PluginManager.ensure_project_plugins(self.poetry, self.io)\n\n        if self.option(\"extras\") and self.option(\"all-extras\"):\n            self.line_error(\n                \"<error>You cannot specify explicit\"\n                \" `<fg=yellow;options=bold>--extras</>` while installing\"\n                \" using `<fg=yellow;options=bold>--all-extras</>`.</error>\"\n            )\n            return 1\n\n        if self.option(\"only-root\") and any(\n            self.option(key) for key in {\"with\", \"without\", \"only\", \"all-groups\"}\n        ):\n            self.line_error(\n                \"<error>The `<fg=yellow;options=bold>--with</>`,\"\n                \" `<fg=yellow;options=bold>--without</>`,\"\n                \" `<fg=yellow;options=bold>--only</>` and\"\n                \" `<fg=yellow;options=bold>--all-groups</>`\"\n                \" options cannot be used with\"\n                \" the `<fg=yellow;options=bold>--only-root</>`\"\n                \" option.</error>\"\n            )\n            return 1\n\n        if self.option(\"only-root\") and self.option(\"no-root\"):\n            self.line_error(\n                \"<error>You cannot specify `<fg=yellow;options=bold>--no-root</>`\"\n                \" when using `<fg=yellow;options=bold>--only-root</>`.</error>\"\n            )\n            return 1\n\n        if (\n            self.option(\"only\") or self.option(\"with\") or self.option(\"without\")\n        ) and self.option(\"all-groups\"):\n            self.line_error(\n                \"<error>You cannot specify `<fg=yellow;options=bold>--with</>`,\"\n                \" `<fg=yellow;options=bold>--without</>`, or\"\n                \" `<fg=yellow;options=bold>--only</>` when using\"\n                \" `<fg=yellow;options=bold>--all-groups</>`.</error>\"\n            )\n            return 1\n\n        extras: list[str]\n        if self.option(\"all-extras\"):\n            extras = list(self.poetry.package.extras.keys())\n        else:\n            extras = []\n            for extra in self.option(\"extras\", []):\n                extras += extra.split()\n\n        self.installer.extras(extras)\n\n        self.installer.only_groups(self.activated_groups)\n        self.installer.skip_directory(self.option(\"no-directory\"))\n        self.installer.dry_run(self.option(\"dry-run\"))\n        self.installer.requires_synchronization(self._with_synchronization)\n        self.installer.executor.enable_bytecode_compilation(self.option(\"compile\"))\n        self.installer.verbose(self.io.is_verbose())\n\n        return_code = self.installer.run()\n\n        if return_code != 0:\n            return return_code\n\n        if self.option(\"no-root\") or not self.poetry.is_package_mode:\n            return 0\n\n        log_install = (\n            \"<b>Installing</> the current project:\"\n            f\" <c1>{self.poetry.package.pretty_name}</c1>\"\n            f\" (<{{tag}}>{self.poetry.package.pretty_version}</>)\"\n        )\n        overwrite = self.io.output.is_decorated() and not self.io.is_debug()\n        self.line(\"\")\n        self.write(log_install.format(tag=\"c2\"))\n        if not overwrite:\n            self.line(\"\")\n\n        if self.option(\"dry-run\"):\n            self.line(\"\")\n            return 0\n\n        # Prior to https://github.com/python-poetry/poetry-core/pull/629\n        # the existence of a module/package was checked when creating the\n        # EditableBuilder. Afterwards, the existence is checked after\n        # executing the build script (if there is one),\n        # i.e. during EditableBuilder.build().\n        try:\n            builder = EditableBuilder(self.poetry, self.env, self.io)\n            builder.build()\n        except (ModuleOrPackageNotFoundError, FileNotFoundError) as e:\n            # This is likely due to the fact that the project is an application\n            # not following the structure expected by Poetry.\n            # No need for an editable install in this case.\n            self.line(\"\")\n            self.line_error(\n                f\"Error: The current project could not be installed: {e}\\n\"\n                \"If you do not want to install the current project\"\n                \" use <c1>--no-root</c1>.\\n\"\n                \"If you want to use Poetry only for dependency management\"\n                \" but not for packaging, you can disable package mode by setting\"\n                \" <c1>package-mode = false</> in your pyproject.toml file.\\n\"\n                \"If you did intend to install the current project, you may need\"\n                \" to set `packages` in your pyproject.toml file.\\n\",\n                style=\"error\",\n            )\n            return 1\n\n        if overwrite:\n            self.overwrite(log_install.format(tag=\"success\"))\n            self.line(\"\")\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/installer_command.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.console.commands.group_command import GroupCommand\nfrom poetry.utils.password_manager import PoetryKeyring\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n    from poetry.installation.installer import Installer\n\n\nclass InstallerCommand(GroupCommand, EnvCommand):\n    def __init__(self) -> None:\n        # Set in poetry.console.application.Application.configure_installer\n        self._installer: Installer | None = None\n\n        super().__init__()\n\n    def reset_poetry(self) -> None:\n        super().reset_poetry()\n\n        self.installer.set_package(self.poetry.package)\n        self.installer.set_locker(self.poetry.locker)\n\n    @property\n    def installer(self) -> Installer:\n        assert self._installer is not None\n        return self._installer\n\n    def set_installer(self, installer: Installer) -> None:\n        self._installer = installer\n\n    def execute(self, io: IO) -> int:\n        PoetryKeyring.preflight_check(io, self.poetry.config)\n        return super().execute(io)\n"
  },
  {
    "path": "src/poetry/console/commands/lock.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.installer_command import InstallerCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass LockCommand(InstallerCommand):\n    name = \"lock\"\n    description = \"Locks the project dependencies.\"\n\n    options: ClassVar[list[Option]] = [\n        option(\n            \"regenerate\",\n            None,\n            \"Ignore existing lock file\"\n            \" and overwrite it with a new lock file created from scratch.\",\n        ),\n    ]\n\n    help = \"\"\"\nThe <info>lock</info> command reads the <comment>pyproject.toml</> file from the\ncurrent directory, processes it, and locks the dependencies in the\\\n <comment>poetry.lock</>\nfile.\nBy default, packages that have already been added to the lock file before\nwill not be updated.\n\n<info>poetry lock</info>\n\"\"\"\n\n    loggers: ClassVar[list[str]] = [\"poetry.repositories.pypi_repository\"]\n\n    def handle(self) -> int:\n        self.installer.lock(update=self.option(\"regenerate\"))\n\n        return self.installer.run()\n"
  },
  {
    "path": "src/poetry/console/commands/new.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.init import InitCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass NewCommand(InitCommand):\n    name = \"new\"\n    description = \"Creates a new Python project at <path>.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"path\", \"The path to create the project at.\")\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\n            \"interactive\",\n            \"i\",\n            \"Allow interactive specification of project configuration.\",\n            flag=True,\n        ),\n        option(\"name\", None, \"Set the resulting package name.\", flag=False),\n        option(\n            \"src\",\n            None,\n            \"Use the src layout for the project. \"\n            \"<warning>Deprecated</>: This is the default option now.\",\n        ),\n        option(\"flat\", None, \"Use the flat layout for the project.\"),\n        option(\n            \"readme\",\n            None,\n            \"Specify the readme file format. Default is md.\",\n            flag=False,\n        ),\n        *[\n            o\n            for o in InitCommand.options\n            if o.name\n            in {\n                \"description\",\n                \"author\",\n                \"python\",\n                \"dependency\",\n                \"dev-dependency\",\n                \"license\",\n            }\n        ],\n    ]\n\n    def handle(self) -> int:\n        from pathlib import Path\n\n        if self.io.input.option(\"project\"):\n            self.line_error(\n                \"<warning>--project only makes sense with existing projects, and will\"\n                \" be ignored. You should consider the option --path instead.</warning>\"\n            )\n\n        path = Path(self.argument(\"path\"))\n        if not path.is_absolute():\n            # we do not use resolve here due to compatibility issues\n            # for path.resolve(strict=False)\n            path = Path.cwd().joinpath(path)\n\n        if path.exists() and list(path.glob(\"*\")):\n            # Directory is not empty. Aborting.\n            raise RuntimeError(\n                f\"Destination <fg=yellow>{path}</> exists and is not empty. Did you mean `poetry init`?\"\n            )\n\n        if self.option(\"src\"):\n            self.line_error(\n                \"The <c1>--src</> option is now the default and will be removed in a future version.\"\n            )\n\n        return self._init_pyproject(\n            project_path=path,\n            allow_interactive=self.option(\"interactive\"),\n            layout_name=\"standard\" if self.option(\"flat\") else \"src\",\n            readme_format=self.option(\"readme\") or \"md\",\n            allow_layout_creation_on_empty=True,\n        )\n"
  },
  {
    "path": "src/poetry/console/commands/publish.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass PublishCommand(Command):\n    name = \"publish\"\n    description = \"Publishes a package to a remote repository.\"\n\n    options: ClassVar[list[Option]] = [\n        option(\n            \"repository\", \"r\", \"The repository to publish the package to.\", flag=False\n        ),\n        option(\"username\", \"u\", \"The username to access the repository.\", flag=False),\n        option(\"password\", \"p\", \"The password to access the repository.\", flag=False),\n        option(\n            \"cert\", None, \"Certificate authority to access the repository.\", flag=False\n        ),\n        option(\n            \"client-cert\",\n            None,\n            \"Client certificate to access the repository.\",\n            flag=False,\n        ),\n        option(\n            \"dist-dir\",\n            None,\n            \"Dist directory where built artifact are stored. Default is `dist`.\",\n            default=\"dist\",\n            flag=False,\n        ),\n        option(\"build\", None, \"Build the package before publishing.\"),\n        option(\"dry-run\", None, \"Perform all actions except upload the package.\"),\n        option(\n            \"skip-existing\",\n            None,\n            \"Ignore errors from files already existing in the repository.\",\n        ),\n    ]\n\n    help = \"\"\"The publish command builds and uploads the package to a remote repository.\n\nBy default, it will upload to PyPI but if you pass the --repository option it will\nupload to it instead.\n\nThe --repository option should match the name of a configured repository using\nthe config command.\n\"\"\"\n\n    loggers: ClassVar[list[str]] = [\"poetry.publishing.publisher\"]\n\n    def handle(self) -> int:\n        from poetry.publishing.publisher import Publisher\n\n        if not self.poetry.is_package_mode:\n            self.line_error(\"Publishing a package is not possible in non-package mode.\")\n            return 1\n\n        dist_dir = self.option(\"dist-dir\")\n\n        publisher = Publisher(self.poetry, self.io, Path(dist_dir))\n\n        # Building package first, if told\n        if self.option(\"build\"):\n            if publisher.files and not self.confirm(\n                f\"There are <info>{len(publisher.files)}</info> files ready for\"\n                \" publishing. Build anyway?\"\n            ):\n                self.line_error(\"<error>Aborted!</error>\")\n\n                return 1\n\n            self.call(\"build\", args=f\"--output {dist_dir}\")\n\n            publisher = Publisher(self.poetry, self.io, Path(dist_dir))\n\n        if not publisher.files:\n            self.line_error(\n                \"<error>No files to publish. \"\n                \"Run poetry build first or use the --build option.</error>\"\n            )\n\n            return 1\n\n        self.line(\"\")\n\n        cert = Path(self.option(\"cert\")) if self.option(\"cert\") else None\n        client_cert = (\n            Path(self.option(\"client-cert\")) if self.option(\"client-cert\") else None\n        )\n\n        publisher.publish(\n            self.option(\"repository\"),\n            self.option(\"username\"),\n            self.option(\"password\"),\n            cert,\n            client_cert,\n            self.option(\"dry-run\"),\n            self.option(\"skip-existing\"),\n        )\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/python/__init__.py",
    "content": "from __future__ import annotations\n\n\ndef get_request_title(request: str, implementation: str, free_threaded: bool) -> str:\n    add_info = implementation\n    if free_threaded:\n        add_info += \", free-threaded\"\n    return f\"<c1>{request}</> (<b>{add_info}</>)\"\n"
  },
  {
    "path": "src/poetry/console/commands/python/install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom poetry.core.constraints.version.version import Version\nfrom poetry.core.version.exceptions import InvalidVersionError\n\nfrom poetry.console.commands.command import Command\nfrom poetry.console.commands.python import get_request_title\nfrom poetry.console.commands.python.remove import PythonRemoveCommand\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.env.python.installer import PythonDownloadNotFoundError\nfrom poetry.utils.env.python.installer import PythonInstallationError\nfrom poetry.utils.env.python.installer import PythonInstaller\nfrom poetry.utils.env.python.providers import PoetryPythonPathProvider\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass PythonInstallCommand(Command):\n    name = \"python install\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"python\", \"The python version to install.\")\n    ]\n\n    options: ClassVar[list[Option]] = [\n        option(\"clean\", \"c\", \"Clean up installation if check fails.\", flag=True),\n        option(\n            \"free-threaded\", \"t\", \"Use free-threaded version if available.\", flag=True\n        ),\n        option(\n            \"implementation\",\n            \"i\",\n            \"Python implementation to use. (cpython, pypy)\",\n            flag=False,\n            default=\"cpython\",\n        ),\n        option(\n            \"reinstall\", \"r\", \"Reinstall if installation already exists.\", flag=True\n        ),\n    ]\n\n    description = (\n        \"Install the specified Python version from the Python Standalone Builds project.\"\n        \" (<warning>experimental feature</warning>)\"\n    )\n\n    def handle(self) -> int:\n        request = self.argument(\"python\")\n        impl = self.option(\"implementation\").lower()\n        reinstall = self.option(\"reinstall\")\n        free_threaded = self.option(\"free-threaded\")\n        if request.endswith(\"t\"):\n            free_threaded = True\n            request = request[:-1]\n\n        try:\n            version = Version.parse(request)\n        except (ValueError, InvalidVersionError):\n            self.io.write_error_line(\n                f\"<error>Invalid Python version requested <b>{request}</></error>\"\n            )\n            return 1\n\n        if free_threaded and version < Version.parse(\"3.13.0\"):\n            self.io.write_error_line(\"\")\n            self.io.write_error_line(\n                \"Free threading is not supported for Python versions prior to <c1>3.13.0</>.\\n\\n\"\n                \"See https://docs.python.org/3/howto/free-threading-python.html for more information.\"\n            )\n            self.io.write_error_line(\"\")\n            return 1\n\n        installer = PythonInstaller(request, impl, free_threaded)\n\n        try:\n            if installer.exists() and not reinstall:\n                self.io.write_error_line(\n                    \"Python version already installed at \"\n                    f\"<b>{PoetryPythonPathProvider.installation_dir(version, impl, free_threaded)}</>.\\n\"\n                )\n                self.io.write_error_line(\n                    f\"Use <c1>--reinstall</> to install anyway, \"\n                    f\"or use <c1>poetry python remove {version}</> first.\"\n                )\n                return 1\n        except PythonDownloadNotFoundError:\n            self.io.write_error_line(\n                \"No suitable standalone build found for the requested Python version.\"\n            )\n            return 1\n\n        request_title = get_request_title(request, impl, free_threaded)\n\n        try:\n            self.io.write(f\"Downloading and installing {request_title} ... \")\n            installer.install()\n        except PythonInstallationError as e:\n            self.io.write(\"<fg=red>Failed</>\\n\")\n            self.io.write_error_line(\"\")\n            self.io.write_error_line(str(e))\n            self.io.write_error_line(\"\")\n            return 1\n\n        self.io.write(\"<fg=green>Done</>\\n\")\n        self.io.write(f\"Testing {request_title} ... \")\n\n        try:\n            installer.exists()\n        except PoetryRuntimeError as e:\n            self.io.write(\"<fg=red>Failed</>\\n\")\n\n            if installer.installation_directory.exists() and self.option(\"clean\"):\n                PythonRemoveCommand.remove_python_installation(\n                    str(installer.version), impl, free_threaded, self.io\n                )\n\n            raise e\n\n        self.io.write(\"<fg=green>Done</>\\n\")\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/python/list.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.version.exceptions import InvalidVersionError\n\nfrom poetry.config.config import Config\nfrom poetry.console.commands.command import Command\nfrom poetry.utils.env.python import Python\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n    from poetry.utils.env.python.manager import PythonInfo\n\n\nclass PythonListCommand(Command):\n    name = \"python list\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"version\", \"Python version to search for.\", optional=True)\n    ]\n\n    options: ClassVar[list[Option]] = [\n        option(\n            \"all\",\n            \"a\",\n            \"List all versions, including those available for download.\",\n            flag=True,\n        ),\n        option(\n            \"free-threaded\", \"t\", \"List only free-threaded Python versions.\", flag=True\n        ),\n        option(\n            \"implementation\", \"i\", \"Python implementation to search for.\", flag=False\n        ),\n        option(\"managed\", \"m\", \"List only Poetry managed Python versions.\", flag=True),\n    ]\n\n    description = (\n        \"Shows Python versions available for this environment.\"\n        \" (<warning>experimental feature</warning>)\"\n    )\n\n    def handle(self) -> int:\n        rows: list[PythonInfo] = []\n        constraint = None\n\n        if self.argument(\"version\"):\n            request = self.argument(\"version\")\n            version = f\"~{request}\" if request.count(\".\") < 2 else request\n            try:\n                constraint = parse_constraint(version)\n            except (ValueError, InvalidVersionError):\n                self.io.write_error_line(\n                    f\"<error>Invalid Python version requested <b>{request}</></error>\"\n                )\n                return 1\n\n        for info in Python.find_all_versions(\n            constraint=constraint,\n            implementation=self.option(\"implementation\"),\n            free_threaded=self.option(\"free-threaded\") or None,\n        ):\n            rows.append(info)\n\n        if self.option(\"all\"):\n            for info in Python.find_downloadable_versions(constraint):\n                rows.append(info)\n\n        rows.sort(\n            key=lambda x: (\n                x.major,\n                x.minor,\n                x.patch,\n                x.implementation,\n                x.free_threaded,\n            ),\n            reverse=True,\n        )\n\n        table = self.table(style=\"compact\")\n        table.set_headers(\n            [\n                \"<fg=magenta;options=bold>Version</>\",\n                \"<fg=magenta;options=bold>Implementation</>\",\n                \"<fg=magenta;options=bold>Manager</>\",\n                \"<fg=magenta;options=bold>Path</>\",\n            ]\n        )\n\n        implementations = {\"cpython\": \"CPython\", \"pypy\": \"PyPy\"}\n        python_installation_path = Config.create().python_installation_dir\n\n        row_count = 0\n\n        for pv in rows:\n            version = f\"{pv.major}.{pv.minor}.{pv.patch}\"\n            if pv.free_threaded:\n                version += \"t\"\n            implementation = implementations.get(\n                pv.implementation.lower(), pv.implementation\n            )\n            is_poetry_managed = (\n                pv.executable is None\n                or pv.executable.resolve().is_relative_to(python_installation_path)\n            )\n\n            if self.option(\"managed\") and not is_poetry_managed:\n                continue\n\n            manager = (\n                \"<fg=blue>Poetry</>\" if is_poetry_managed else \"<fg=yellow>System</>\"\n            )\n            path = (\n                f\"<fg=green>{pv.executable.as_posix()}</>\"\n                if pv.executable\n                else \"Available for download\"\n            )\n\n            table.add_row(\n                [\n                    f\"<c1>{version}</>\",\n                    f\"<b>{implementation}</>\",\n                    f\"{manager}\",\n                    f\"{path}\",\n                ]\n            )\n            row_count += 1\n\n        if row_count > 0:\n            table.render()\n        else:\n            self.io.write_line(\"No Python installations found.\")\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/python/remove.py",
    "content": "from __future__ import annotations\n\nimport shutil\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom poetry.core.constraints.version.version import Version\nfrom poetry.core.version.exceptions import InvalidVersionError\n\nfrom poetry.console.commands.command import Command\nfrom poetry.console.commands.python import get_request_title\nfrom poetry.utils.env.python.providers import PoetryPythonPathProvider\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from cleo.io.io import IO\n\n\nclass PythonRemoveCommand(Command):\n    name = \"python remove\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"python\", \"The python version to remove.\", multiple=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\n            \"free-threaded\", \"t\", \"Use free-threaded version if available.\", flag=True\n        ),\n        option(\n            \"implementation\",\n            \"i\",\n            \"Python implementation to use. (cpython, pypy)\",\n            flag=False,\n            default=\"cpython\",\n        ),\n    ]\n\n    description = (\n        \"Remove the specified Python version if managed by Poetry.\"\n        \" (<warning>experimental feature</warning>)\"\n    )\n\n    @staticmethod\n    def remove_python_installation(\n        request: str, implementation: str, free_threaded: bool, io: IO\n    ) -> int:\n        if request.endswith(\"t\"):\n            free_threaded = True\n            request = request[:-1]\n        try:\n            version = Version.parse(request)\n        except (ValueError, InvalidVersionError):\n            io.write_error_line(\n                f\"<error>Invalid Python version requested <b>{request}</></error>\"\n            )\n            return 1\n\n        if version.minor is None or version.patch is None:\n            io.write_error_line(\n                f\"<error>Invalid Python version requested <b>{request}</></error>\\n\"\n            )\n            io.write_error_line(\n                \"You need to provide an exact Python version in the format <c1>X.Y.Z</> to be removed.\\n\\n\"\n                \"You can use <c1>poetry python list -m</> to list installed Poetry managed Python versions.\"\n            )\n\n            return 1\n\n        request_title = get_request_title(request, implementation, free_threaded)\n        path = PoetryPythonPathProvider.installation_dir(\n            version, implementation, free_threaded\n        )\n\n        if path.exists():\n            if io.is_verbose():\n                io.write_line(f\"Installation path: {path}\")\n\n            io.write(f\"Removing installation {request_title} ... \")\n\n            try:\n                shutil.rmtree(path)\n            except OSError as e:\n                io.write(\"<fg=red>Failed</>\\n\")\n\n                if io.is_verbose():\n                    io.write_line(f\"Failed to remove directory: {e}\")\n\n            io.write(\"<fg=green>Done</>\\n\")\n        else:\n            io.write_line(f\"No installation was found at {path}.\")\n\n        return 0\n\n    def handle(self) -> int:\n        implementation = self.option(\"implementation\").lower()\n        free_threaded = self.option(\"free-threaded\")\n\n        result = 0\n\n        for request in self.argument(\"python\"):\n            result += self.remove_python_installation(\n                request, implementation, free_threaded, self.io\n            )\n\n        return result\n"
  },
  {
    "path": "src/poetry/console/commands/remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom tomlkit.toml_document import TOMLDocument\n\nfrom poetry.console.commands.installer_command import InstallerCommand\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass RemoveCommand(InstallerCommand):\n    name = \"remove\"\n    description = \"Removes a package from the project dependencies.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"packages\", \"The packages to remove.\", multiple=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\"group\", \"G\", \"The group to remove the dependency from.\", flag=False),\n        option(\n            \"dev\",\n            \"D\",\n            \"Remove a package from the development dependencies.\"\n            \" (shortcut for '-G dev')\",\n        ),\n        option(\n            \"dry-run\",\n            None,\n            \"Output the operations but do not execute anything \"\n            \"(implicitly enables --verbose).\",\n        ),\n        option(\"lock\", None, \"Do not perform operations (only update the lockfile).\"),\n    ]\n\n    help = \"\"\"The <info>remove</info> command removes a package from the current\nlist of installed packages\n\n<info>poetry remove</info>\"\"\"\n\n    loggers: ClassVar[list[str]] = [\n        \"poetry.repositories.pypi_repository\",\n        \"poetry.inspection.info\",\n    ]\n\n    def handle(self) -> int:\n        packages = self.argument(\"packages\")\n\n        if self.option(\"dev\"):\n            group = \"dev\"\n        else:\n            group = self.option(\"group\", self.default_group)\n\n        content: dict[str, Any] = self.poetry.file.read()\n        project_content = content.get(\"project\", {})\n        groups_content = content.get(\"dependency-groups\", {})\n        poetry_content = content.get(\"tool\", {}).get(\"poetry\", {})\n        poetry_groups_content = poetry_content.get(\"group\", {})\n\n        if group is None:\n            # remove from all groups\n            removed = set()\n            group_sections = []\n            project_dependencies = project_content.get(\"dependencies\", [])\n            poetry_dependencies = poetry_content.get(\"dependencies\", {})\n\n            if project_dependencies or poetry_dependencies:\n                group_sections.append(\n                    (MAIN_GROUP, project_dependencies, poetry_dependencies)\n                )\n            group_sections.extend(\n                (\n                    group_name,\n                    dependencies,\n                    poetry_groups_content.get(group_name, {}).get(\"dependencies\", {}),\n                )\n                for group_name, dependencies in groups_content.items()\n            )\n            group_sections.extend(\n                (group_name, [], group_section.get(\"dependencies\", {}))\n                for group_name, group_section in poetry_groups_content.items()\n                if group_name not in groups_content and group_name != MAIN_GROUP\n            )\n\n            for group_name, standard_section, poetry_section in group_sections:\n                removed |= self._remove_packages(\n                    packages=packages,\n                    standard_section=standard_section,\n                    poetry_section=poetry_section,\n                    group_name=group_name,\n                )\n                if group_name != MAIN_GROUP:\n                    if (\n                        not poetry_section\n                        and \"dependencies\" in poetry_groups_content.get(group_name, {})\n                    ):\n                        del poetry_content[\"group\"][group_name][\"dependencies\"]\n                        if not poetry_content[\"group\"][group_name]:\n                            del poetry_content[\"group\"][group_name]\n                    if not standard_section and group_name in groups_content:\n                        del groups_content[group_name]\n                    if (\n                        group_name not in groups_content\n                        and group_name not in poetry_groups_content\n                    ):\n                        self._remove_references_to_group(group_name, content)\n\n        elif group == \"dev\" and \"dev-dependencies\" in poetry_content:\n            # We need to account for the old `dev-dependencies` section\n            removed = self._remove_packages(\n                packages, [], poetry_content[\"dev-dependencies\"], \"dev\"\n            )\n\n            if not poetry_content[\"dev-dependencies\"]:\n                del poetry_content[\"dev-dependencies\"]\n        else:\n            removed = set()\n            if group_content := poetry_groups_content.get(group):\n                poetry_section = group_content.get(\"dependencies\", {})\n                removed.update(\n                    self._remove_packages(\n                        packages=packages,\n                        standard_section=[],\n                        poetry_section=poetry_section,\n                        group_name=group,\n                    )\n                )\n                if not poetry_section and \"dependencies\" in group_content:\n                    del group_content[\"dependencies\"]\n                    if not group_content:\n                        del poetry_content[\"group\"][group]\n            if group in groups_content:\n                removed.update(\n                    self._remove_packages(\n                        packages=packages,\n                        standard_section=groups_content[group],\n                        poetry_section={},\n                        group_name=group,\n                    )\n                )\n                if not groups_content[group]:\n                    del groups_content[group]\n            if group not in groups_content and group not in poetry_groups_content:\n                self._remove_references_to_group(group, content)\n\n        if \"group\" in poetry_content and not poetry_content[\"group\"]:\n            del poetry_content[\"group\"]\n        if \"dependency-groups\" in content and not content[\"dependency-groups\"]:\n            del content[\"dependency-groups\"]\n\n        not_found = set(packages).difference(removed)\n        if not_found:\n            raise ValueError(\n                \"The following packages were not found: \" + \", \".join(sorted(not_found))\n            )\n\n        # Refresh the locker\n        self.poetry.locker.set_pyproject_data(content)\n        self.installer.set_locker(self.poetry.locker)\n        self.installer.set_package(self.poetry.package)\n        self.installer.dry_run(self.option(\"dry-run\", False))\n        self.installer.verbose(self.io.is_verbose())\n        self.installer.update(True)\n        self.installer.execute_operations(not self.option(\"lock\"))\n        self.installer.whitelist(removed)\n\n        status = self.installer.run()\n\n        if not self.option(\"dry-run\") and status == 0:\n            assert isinstance(content, TOMLDocument)\n            self.poetry.file.write(content)\n\n        return status\n\n    def _remove_packages(\n        self,\n        packages: list[str],\n        standard_section: list[str | Mapping[str, Any]],\n        poetry_section: dict[str, Any],\n        group_name: str,\n    ) -> set[str]:\n        removed = set()\n        group = self.poetry.package.dependency_group(group_name)\n\n        for package in packages:\n            normalized_name = canonicalize_name(package)\n            for requirement in standard_section.copy():\n                if not isinstance(requirement, str):\n                    continue\n                if Dependency.create_from_pep_508(requirement).name == normalized_name:\n                    standard_section.remove(requirement)\n                    removed.add(package)\n            for existing_package in list(poetry_section):\n                if canonicalize_name(existing_package) == normalized_name:\n                    del poetry_section[existing_package]\n                    removed.add(package)\n\n        for package in removed:\n            group.remove_dependency(package)\n\n        return removed\n\n    def _remove_references_to_group(\n        self, group_name: str, content: dict[str, Any]\n    ) -> None:\n        \"\"\"\n        Removes references to the given group from other groups.\n        \"\"\"\n        # 1. PEP 735: [dependency-groups]\n        if \"dependency-groups\" in content:\n            groups_to_remove = []\n            for group_key, group_content in content[\"dependency-groups\"].items():\n                if not isinstance(group_content, list):\n                    continue\n\n                to_remove = []\n                for item in group_content:\n                    if (\n                        isinstance(item, dict)\n                        and item.get(\"include-group\") == group_name\n                    ):\n                        to_remove.append(item)\n\n                for item in to_remove:\n                    group_content.remove(item)\n\n                # Clean up now-empty lists (normalize with legacy behavior)\n                # Only remove groups that became empty due to include-group cleanup,\n                # not the target group itself (which is handled by the caller)\n                if not group_content and group_key != group_name:\n                    groups_to_remove.append(group_key)\n\n            for group_key in groups_to_remove:\n                del content[\"dependency-groups\"][group_key]\n\n        # 2. Legacy: [tool.poetry.group.<name>] include-groups = [...]\n        poetry_content = content.get(\"tool\", {}).get(\"poetry\", {})\n        if \"group\" in poetry_content:\n            groups_to_remove = []\n            for group_key, group_content in poetry_content[\"group\"].items():\n                if \"include-groups\" not in group_content:\n                    continue\n\n                if group_name in group_content[\"include-groups\"]:\n                    group_content[\"include-groups\"].remove(group_name)\n\n                if not group_content[\"include-groups\"]:\n                    del group_content[\"include-groups\"]\n\n                    if not group_content:\n                        groups_to_remove.append(group_key)\n\n            for group_key in groups_to_remove:\n                del poetry_content[\"group\"][group_key]\n"
  },
  {
    "path": "src/poetry/console/commands/run.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\n\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.utils._compat import WINDOWS\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from poetry.core.masonry.utils.module import Module\n\n\nclass RunCommand(EnvCommand):\n    name = \"run\"\n    description = \"Runs a command in the appropriate environment.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"args\", \"The command and arguments/options to run.\", multiple=True)\n    ]\n\n    def handle(self) -> int:\n        args = self.argument(\"args\")\n        script = args[0]\n        scripts = self.poetry.local_config.get(\"scripts\")\n\n        if scripts and script in scripts:\n            return self.run_script(scripts[script], args)\n\n        try:\n            return self.env.execute(*args)\n        except FileNotFoundError:\n            self.line_error(f\"<error>Command not found: <c1>{script}</c1></error>\")\n            return 1\n\n    @property\n    def _module(self) -> Module:\n        from poetry.core.masonry.utils.module import Module\n\n        poetry = self.poetry\n        package = poetry.package\n        path = poetry.file.path.parent\n        module = Module(package.name, path.as_posix(), package.packages)\n\n        return module\n\n    def run_script(self, script: str | dict[str, str], args: list[str]) -> int:\n        \"\"\"Runs an entry point script defined in the section ``[tool.poetry.scripts]``.\n\n        When a script exists in the venv bin folder, i.e. after ``poetry install``,\n        then ``sys.argv[0]`` must be set to the full path of the executable, so\n        ``poetry run foo`` and ``poetry shell``, ``foo`` have the same ``sys.argv[0]``\n        that points to the full path.\n\n        Otherwise (when an entry point script does not exist), ``sys.argv[0]`` is the\n        script name only, i.e. ``poetry run foo`` has ``sys.argv == ['foo']``.\n        \"\"\"\n        for script_dir in self.env.script_dirs:\n            script_path = script_dir / args[0]\n            if WINDOWS:\n                script_path = script_path.with_suffix(\".cmd\")\n            if script_path.exists():\n                args = [str(script_path), *args[1:]]\n                break\n        else:\n            # If we reach this point, the script is not installed\n            self._warning_not_installed_script(args[0])\n\n        if isinstance(script, dict):\n            script = script[\"callable\"]\n\n        module, callable_ = script.split(\":\")\n\n        src_in_sys_path = \"sys.path.append('src'); \" if self._module.is_in_src() else \"\"\n\n        cmd = [\"python\", \"-c\"]\n\n        cmd += [\n            \"import sys; \"\n            \"from importlib import import_module; \"\n            f\"sys.argv = {args!r}; {src_in_sys_path}\"\n            f\"sys.exit(import_module('{module}').{callable_}())\"\n        ]\n\n        return self.env.execute(*cmd)\n\n    def _warning_not_installed_script(self, script: str) -> None:\n        message = f\"\"\"\\\nWarning: '{script}' is an entry point defined in pyproject.toml, but it's not \\\ninstalled as a script. You may get improper `sys.argv[0]`.\n\nThe support to run uninstalled scripts will be removed in a future release.\n\nRun `poetry install` to resolve and get rid of this message.\n\"\"\"\n        self.line_error(message, style=\"warning\")\n"
  },
  {
    "path": "src/poetry/console/commands/search.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n\n\nclass SearchCommand(Command):\n    name = \"search\"\n    description = \"Searches for packages on remote repositories.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"tokens\", \"The tokens to search for.\", multiple=True)\n    ]\n\n    def handle(self) -> int:\n        seen = set()\n\n        table = self.table(style=\"compact\")\n        table.set_headers(\n            [\"<b>Package</>\", \"<b>Version</>\", \"<b>Source</>\", \"<b>Description</>\"]\n        )\n\n        rows = []\n\n        for repository in self.poetry.pool.repositories:\n            for result in repository.search(self.argument(\"tokens\")):\n                key = f\"{repository.name}::{result.pretty_string}\"\n                if key in seen:\n                    continue\n                seen.add(key)\n                rows.append((result, repository.name))\n\n        if not rows:\n            self.line(\"<info>No matching packages were found.</>\")\n            return 0\n\n        for package, source in sorted(\n            rows, key=lambda x: (x[0].name, x[0].version, x[1])\n        ):\n            table.add_row(\n                [\n                    f\"<c1>{package.name}</>\",\n                    f\"<b>{package.version}</b>\",\n                    f\"<fg=yellow;options=bold>{source}</>\",\n                    str(package.description),\n                ]\n            )\n\n        table.render()\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/self/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/self/add.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.commands.add import AddCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass SelfAddCommand(SelfCommand, AddCommand):\n    name = \"self add\"\n    description = \"Add additional packages to Poetry's runtime environment.\"\n    options: ClassVar[list[Option]] = [\n        o\n        for o in AddCommand.options\n        if o.name in {\"editable\", \"extras\", \"source\", \"dry-run\", \"allow-prereleases\"}\n    ]\n    help = f\"\"\"\\\nThe <c1>self add</c1> command installs additional packages to Poetry's runtime \\\nenvironment.\n\nThis is managed in the <comment>{SelfCommand.get_default_system_pyproject_file()}</> \\\nfile.\n\n{AddCommand.examples}\n\"\"\"\n\n    @property\n    def _hint_update_packages(self) -> str:\n        version = Version.parse(__version__)\n        flags = \"\"\n\n        if not version.is_stable():\n            flags = \" --preview\"\n\n        return (\n            \"\\nIf you want to update it to the latest compatible version, you can use\"\n            f\" `poetry self update{flags}`.\\nIf you prefer to upgrade it to the latest\"\n            \" available version, you can use `poetry self add package@latest`.\\n\"\n        )\n"
  },
  {
    "path": "src/poetry/console/commands/self/install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\n\nfrom poetry.console.commands.install import InstallCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass SelfInstallCommand(SelfCommand, InstallCommand):\n    name = \"self install\"\n    description = (\n        \"Install locked packages (incl. addons) required by this Poetry installation.\"\n    )\n    options: ClassVar[list[Option]] = [\n        o for o in InstallCommand.options if o.name in {\"sync\", \"dry-run\"}\n    ]\n    help = f\"\"\"\\\nThe <c1>self install</c1> command ensures all additional packages specified are \\\ninstalled in the current runtime environment.\n\nThis is managed in the <comment>{SelfCommand.get_default_system_pyproject_file()}</> \\\nfile.\n\nYou can add more packages using the <c1>self add</c1> command and remove them using \\\nthe <c1>self remove</c1> command.\n\"\"\"\n\n    @property\n    def activated_groups(self) -> set[NormalizedName]:\n        return {MAIN_GROUP, canonicalize_name(self.default_group)}\n\n    @property\n    def _alternative_sync_command(self) -> str:\n        return \"poetry self sync\"\n"
  },
  {
    "path": "src/poetry/console/commands/self/lock.py",
    "content": "from __future__ import annotations\n\nfrom poetry.console.commands.lock import LockCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nclass SelfLockCommand(SelfCommand, LockCommand):\n    name = \"self lock\"\n    description = \"Lock the Poetry installation's system requirements.\"\n    help = f\"\"\"\\\nThe <c1>self lock</c1> command reads this Poetry installation's system requirements as \\\nspecified in the <comment>{SelfCommand.get_default_system_pyproject_file()}</> file.\n\nThe system dependencies are locked in the <comment>\\\n{SelfCommand.get_default_system_pyproject_file().parent.joinpath(\"poetry.lock\")}</> \\\nfile.\n\"\"\"\n"
  },
  {
    "path": "src/poetry/console/commands/self/remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.console.commands.remove import RemoveCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass SelfRemoveCommand(SelfCommand, RemoveCommand):\n    name = \"self remove\"\n    description = \"Remove additional packages from Poetry's runtime environment.\"\n    options: ClassVar[list[Option]] = [\n        o for o in RemoveCommand.options if o.name in {\"dry-run\"}\n    ]\n    help = f\"\"\"\\\nThe <c1>self remove</c1> command removes additional package's to Poetry's runtime \\\nenvironment.\n\nThis is managed in the <comment>{SelfCommand.get_default_system_pyproject_file()}</> \\\nfile.\n\"\"\"\n"
  },
  {
    "path": "src/poetry/console/commands/self/self_command.py",
    "content": "from __future__ import annotations\n\nimport typing\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.commands.installer_command import InstallerCommand\nfrom poetry.factory import Factory\nfrom poetry.pyproject.toml import PyProjectTOML\nfrom poetry.utils.constants import POETRY_SYSTEM_PROJECT_NAME\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import SystemEnv\nfrom poetry.utils.helpers import directory\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env import Env\n\n\nclass SelfCommand(InstallerCommand):\n    ADDITIONAL_PACKAGE_GROUP = canonicalize_name(\"additional\")\n\n    @staticmethod\n    def get_default_system_pyproject_file() -> Path:\n        # We separate this out to avoid unwanted side effect during testing while\n        # maintaining dynamic use in help text.\n        #\n        # This is not ideal, but is the simplest solution for now.\n        from poetry.locations import CONFIG_DIR\n\n        return Path(CONFIG_DIR).joinpath(\"pyproject.toml\")\n\n    @property\n    def system_pyproject(self) -> Path:\n        file = self.get_default_system_pyproject_file()\n        file.parent.mkdir(parents=True, exist_ok=True)\n        return file\n\n    def reset_env(self) -> None:\n        self._env = EnvManager.get_system_env(naive=True)\n\n    @property\n    def env(self) -> Env:\n        if not isinstance(self._env, SystemEnv):\n            self.reset_env()\n        assert self._env is not None\n        return self._env\n\n    @property\n    def default_group(self) -> str:\n        return self.ADDITIONAL_PACKAGE_GROUP\n\n    @property\n    def activated_groups(self) -> set[NormalizedName]:\n        return {canonicalize_name(self.default_group)}\n\n    def generate_system_pyproject(self) -> None:\n        preserved = {}\n        preserved_groups: dict[str, Any] = {}\n\n        if self.system_pyproject.exists():\n            toml_file = PyProjectTOML(self.system_pyproject)\n            content = toml_file.data\n\n            for key in {\"group\", \"source\"}:\n                if key in toml_file.poetry_config:\n                    preserved[key] = toml_file.poetry_config[key]\n\n            if \"dependency-groups\" in content:\n                preserved_groups = typing.cast(\n                    \"dict[str, Any]\", content[\"dependency-groups\"]\n                )\n\n        package = ProjectPackage(name=POETRY_SYSTEM_PROJECT_NAME, version=__version__)\n        package.add_dependency(Dependency(name=\"poetry\", constraint=f\"{__version__}\"))\n\n        package.python_versions = \".\".join(str(v) for v in self.env.version_info[:3])\n\n        content = Factory.create_legacy_pyproject_from_package(package=package)\n        content[\"tool\"][\"poetry\"][\"package-mode\"] = False  # type: ignore[index]\n\n        for key in preserved:\n            content[\"tool\"][\"poetry\"][key] = preserved[key]  # type: ignore[index]\n\n        if preserved_groups:\n            content[\"dependency-groups\"] = preserved_groups\n\n        pyproject = PyProjectTOML(self.system_pyproject)\n        pyproject.file.write(content)\n\n    def reset_poetry(self) -> None:\n        with directory(self.system_pyproject.parent):\n            self.generate_system_pyproject()\n            self._poetry = Factory().create_poetry(\n                self.system_pyproject.parent, io=self.io, disable_plugins=True\n            )\n\n    @property\n    def poetry(self) -> Poetry:\n        if self._poetry is None:\n            self.reset_poetry()\n\n        assert self._poetry is not None\n        return self._poetry\n\n    def _system_project_handle(self) -> int:\n        \"\"\"\n        This is a helper method that by default calls the handle method implemented in\n        the child class's next MRO sibling. Override this if you want special handling\n        either before calling the handle() from the super class or have custom logic\n        to handle the command.\n\n        The default implementations handles cases where a `self` command delegates\n        handling to an existing command. Eg: `SelfAddCommand(SelfCommand, AddCommand)`.\n        \"\"\"\n        return_code: int = super().handle()\n        return return_code\n\n    def reset(self) -> None:\n        \"\"\"\n        Reset current command instance's environment and poetry instances to ensure\n        use of the system specific ones.\n        \"\"\"\n        self.reset_env()\n        self.reset_poetry()\n\n    def handle(self) -> int:\n        # We override the base class's handle() method to ensure that poetry and env\n        # are reset to work within the system project instead of current context.\n        # Further, during execution, the working directory is temporarily changed\n        # to parent directory of Poetry system pyproject.toml file.\n        #\n        # This method **should not** be overridden in child classes as it may have\n        # unexpected consequences.\n\n        self.reset()\n\n        with directory(self.system_pyproject.parent):\n            return self._system_project_handle()\n"
  },
  {
    "path": "src/poetry/console/commands/self/show/__init__.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import option\n\nfrom poetry.console.commands.self.self_command import SelfCommand\nfrom poetry.console.commands.show import ShowCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n    from packaging.utils import NormalizedName\n\n\nclass SelfShowCommand(SelfCommand, ShowCommand):\n    name = \"self show\"\n    options: ClassVar[list[Option]] = [\n        option(\"addons\", None, \"List only add-on packages installed.\"),\n        *[\n            o\n            for o in ShowCommand.options\n            if o.name in {\"tree\", \"latest\", \"outdated\", \"format\"}\n        ],\n    ]\n    description = \"Show packages from Poetry's runtime environment.\"\n    help = f\"\"\"\\\nThe <c1>self show</c1> command behaves similar to the <c1>show</c1> command, but\nworking within Poetry's runtime environment. This lists all packages installed within\nthe Poetry install environment.\n\nTo show only additional packages that have been added via <c1>self add</c1> and their\ndependencies use <c1>self show --addons</c1>.\n\nThis is managed in the <comment>{SelfCommand.get_default_system_pyproject_file()}</> \\\nfile.\n\"\"\"\n\n    @property\n    def activated_groups(self) -> set[NormalizedName]:\n        if self.option(\"addons\", False):\n            return {SelfCommand.ADDITIONAL_PACKAGE_GROUP}\n\n        return super(ShowCommand, self).activated_groups\n"
  },
  {
    "path": "src/poetry/console/commands/self/show/plugins.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from importlib import metadata\n\n    from poetry.core.packages.package import Package\n\n\n@dataclasses.dataclass\nclass PluginPackage:\n    package: Package\n    plugins: list[metadata.EntryPoint] = dataclasses.field(default_factory=list)\n    application_plugins: list[metadata.EntryPoint] = dataclasses.field(\n        default_factory=list\n    )\n\n    def append(self, entry_point: metadata.EntryPoint) -> None:\n        from poetry.plugins.application_plugin import ApplicationPlugin\n        from poetry.plugins.plugin import Plugin\n\n        group = entry_point.group\n\n        if group == ApplicationPlugin.group:\n            self.application_plugins.append(entry_point)\n        elif group == Plugin.group:\n            self.plugins.append(entry_point)\n        else:\n            name = entry_point.name\n            raise ValueError(f\"Unknown plugin group ({group}) for {name}\")\n\n\nclass SelfShowPluginsCommand(SelfCommand):\n    name = \"self show plugins\"\n    description = \"Shows information about the currently installed plugins.\"\n    help = \"\"\"\\\nThe <c1>self show plugins</c1> command lists all installed Poetry plugins.\n\nPlugins can be added and removed using the <c1>self add</c1> and <c1>self remove</c1> \\\ncommands respectively.\n\n<warning>This command does not list packages that do not provide a Poetry plugin.</>\n\"\"\"\n\n    def _system_project_handle(self) -> int:\n        from packaging.utils import canonicalize_name\n\n        from poetry.plugins.application_plugin import ApplicationPlugin\n        from poetry.plugins.plugin import Plugin\n        from poetry.plugins.plugin_manager import PluginManager\n        from poetry.repositories.installed_repository import InstalledRepository\n        from poetry.utils.env import EnvManager\n        from poetry.utils.helpers import pluralize\n\n        plugins: dict[str, PluginPackage] = {}\n\n        system_env = EnvManager.get_system_env(naive=True)\n        installed_repository = InstalledRepository.load(\n            system_env, with_dependencies=True\n        )\n\n        packages_by_name: dict[str, Package] = {\n            pkg.name: pkg for pkg in installed_repository.packages\n        }\n\n        for group in [ApplicationPlugin.group, Plugin.group]:\n            for entry_point in PluginManager(group).get_plugin_entry_points():\n                assert entry_point.dist is not None\n\n                package = packages_by_name[canonicalize_name(entry_point.dist.name)]\n\n                name = package.pretty_name\n\n                info = plugins.get(name) or PluginPackage(package=package)\n                info.append(entry_point)\n\n                plugins[name] = info\n\n        for name, info in plugins.items():\n            package = info.package\n            description = \" \" + package.description if package.description else \"\"\n            self.line(\"\")\n            self.line(f\"  - <c1>{name}</c1> (<c2>{package.version}</c2>){description}\")\n            provide_line = \"     \"\n\n            if info.plugins:\n                count = len(info.plugins)\n                provide_line += f\" <info>{count}</info> plugin{pluralize(count)}\"\n\n            if info.application_plugins:\n                if info.plugins:\n                    provide_line += \" and\"\n\n                count = len(info.application_plugins)\n                provide_line += (\n                    f\" <info>{count}</info> application plugin{pluralize(count)}\"\n                )\n\n            self.line(provide_line)\n\n            if package.requires:\n                self.line(\"\")\n                self.line(\"      <info>Dependencies</info>\")\n                for dependency in package.requires:\n                    self.line(\n                        f\"        - {dependency.pretty_name}\"\n                        f\" (<c2>{dependency.pretty_constraint}</c2>)\"\n                    )\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/self/sync.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.console.commands.self.install import SelfInstallCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass SelfSyncCommand(SelfInstallCommand):\n    name = \"self sync\"\n    description = (\n        \"Sync Poetry's own environment according to the locked packages (incl. addons)\"\n        \" required by this Poetry installation.\"\n    )\n    options: ClassVar[list[Option]] = [\n        opt for opt in SelfInstallCommand.options if opt.name != \"sync\"\n    ]\n    help = f\"\"\"\\\nThe <c1>self sync</c1> command ensures all additional (and no other) packages \\\nspecified are installed in the current runtime environment.\n\nThis is managed in the \\\n<comment>{SelfInstallCommand.get_default_system_pyproject_file()}</> file.\n\nYou can add more packages using the <c1>self add</c1> command and remove them using \\\nthe <c1>self remove</c1> command.\n\"\"\"\n\n    @property\n    def _with_synchronization(self) -> bool:\n        return True\n"
  },
  {
    "path": "src/poetry/console/commands/self/update.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom cleo.io.inputs.string_input import StringInput\nfrom cleo.io.io import IO\n\nfrom poetry.console.commands.add import AddCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass SelfUpdateCommand(SelfCommand):\n    name = \"self update\"\n    description = \"Updates Poetry to the latest version.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"version\", \"The version to update to.\", optional=True, default=\"latest\"\n        )\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\"preview\", None, \"Allow the installation of pre-release versions.\"),\n        option(\n            \"dry-run\",\n            None,\n            \"Output the operations but do not execute anything \"\n            \"(implicitly enables --verbose).\",\n        ),\n    ]\n    help = \"\"\"\\\nThe <c1>self update</c1> command updates Poetry version in its current runtime \\\nenvironment.\n\"\"\"\n\n    def _system_project_handle(self) -> int:\n        self.write(\"<info>Updating Poetry version ...</info>\\n\\n\")\n        application = self.get_application()\n        add_command = application.find(\"add\")\n        assert isinstance(add_command, AddCommand)\n        add_command.set_env(self.env)\n        add_command.set_poetry(self.poetry)\n        application.configure_installer_for_command(add_command, self.io)\n\n        argv = [\"add\", f\"poetry@{self.argument('version')}\"]\n\n        if self.option(\"dry-run\"):\n            argv.append(\"--dry-run\")\n\n        if self.option(\"preview\"):\n            argv.append(\"--allow-prereleases\")\n\n        exit_code: int = add_command.run(\n            IO(\n                StringInput(\" \".join(argv)),\n                self.io.output,\n                self.io.error_output,\n            )\n        )\n        return exit_code\n"
  },
  {
    "path": "src/poetry/console/commands/show.py",
    "content": "from __future__ import annotations\n\nimport json\nimport sys\n\nfrom enum import Enum\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.console.commands.env_command import EnvCommand\nfrom poetry.console.commands.group_command import GroupCommand\nfrom poetry.utils.constants import POETRY_SYSTEM_PROJECT_NAME\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from cleo.io.io import IO\n    from cleo.ui.table import Rows\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories.repository import Repository\n\n\ndef reverse_deps(pkg: Package, repo: Repository) -> dict[str, str]:\n    required_by = {}\n    for locked in repo.packages:\n        dependencies = {d.name: d.pretty_constraint for d in locked.requires}\n\n        if pkg.name in dependencies:\n            required_by[locked.pretty_name] = dependencies[pkg.name]\n\n    return required_by\n\n\nclass OutputFormats(str, Enum):\n    JSON = \"json\"\n    TEXT = \"text\"\n\n\nclass ShowCommand(GroupCommand, EnvCommand):\n    name = \"show\"\n    description = \"Shows information about packages.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"package\", \"The package to inspect\", optional=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        *GroupCommand._group_dependency_options(),\n        option(\"tree\", \"t\", \"List the dependencies as a tree.\"),\n        option(\n            \"why\",\n            None,\n            \"When showing the full list, or a <info>--tree</info> for a single package,\"\n            \" display whether they are a direct dependency or required by other\"\n            \" packages\",\n        ),\n        option(\"latest\", \"l\", \"Show the latest version.\"),\n        option(\n            \"outdated\",\n            \"o\",\n            \"Show the latest version but only for packages that are outdated.\",\n        ),\n        option(\n            \"all\",\n            \"a\",\n            \"Show all packages (even those not compatible with current system).\",\n        ),\n        option(\"top-level\", \"T\", \"Show only top-level dependencies.\"),\n        option(\n            \"no-truncate\",\n            None,\n            \"Do not truncate the output based on the terminal width.\",\n        ),\n        option(\n            \"format\",\n            \"f\",\n            \"Specify the output format (`json` or `text`). Default is `text`. `json` cannot be combined with the <info>--tree</info> option.\",\n            flag=False,\n            default=\"text\",\n        ),\n    ]\n\n    help = \"\"\"The show command displays detailed information about a package, or\nlists all packages available.\"\"\"\n\n    colors: ClassVar[list[str]] = [\"cyan\", \"yellow\", \"green\", \"magenta\", \"blue\"]\n\n    def handle(self) -> int:\n        package = self.argument(\"package\")\n\n        if self.option(\"tree\"):\n            self.init_styles(self.io)\n\n        if self.option(\"top-level\"):\n            if self.option(\"tree\"):\n                self.line_error(\n                    \"<error>Error: Cannot use --tree and --top-level at the same\"\n                    \" time.</error>\"\n                )\n                return 1\n            if package is not None:\n                self.line_error(\n                    \"<error>Error: Cannot use --top-level when displaying a single\"\n                    \" package.</error>\"\n                )\n                return 1\n\n        if self.option(\"why\"):\n            if self.option(\"tree\") and package is None:\n                self.line_error(\n                    \"<error>Error: --why requires a package when combined with\"\n                    \" --tree.</error>\"\n                )\n\n                return 1\n\n            if not self.option(\"tree\") and package:\n                self.line_error(\n                    \"<error>Error: --why cannot be used without --tree when displaying\"\n                    \" a single package.</error>\"\n                )\n\n                return 1\n\n        if self.option(\"format\") not in set(OutputFormats):\n            self.line_error(\n                f\"<error>Error: Invalid output format. Supported formats are: {', '.join(OutputFormats)}.</error>\"\n            )\n\n            return 1\n\n        if self.option(\"format\") != OutputFormats.TEXT and self.option(\"tree\"):\n            self.line_error(\n                \"<error>Error: --tree option can only be used with the text output option.</error>\"\n            )\n            return 1\n\n        if self.option(\"outdated\"):\n            self.io.input.set_option(\"latest\", True)\n\n        if not self.poetry.locker.is_locked():\n            self.line_error(\n                f\"<error>Error: poetry.lock not found. Run `{self._lock_create_command()}`\"\n                \" to create it.</error>\"\n            )\n            return 1\n\n        locked_repo = self.poetry.locker.locked_repository()\n\n        if package:\n            return self._display_single_package_information(package, locked_repo)\n\n        root = self.project_with_activated_groups_only()\n\n        # Show tree view if requested\n        if self.option(\"tree\"):\n            return self._display_packages_tree_information(locked_repo, root)\n\n        return self._display_packages_information(locked_repo, root)\n\n    def _lock_create_command(self) -> str:\n        if self.poetry.package.name == POETRY_SYSTEM_PROJECT_NAME:\n            return \"poetry self lock\"\n\n        return \"poetry lock\"\n\n    def _display_single_package_information(\n        self, package: str, locked_repository: Repository\n    ) -> int:\n        locked_packages = locked_repository.packages\n        canonicalized_package = canonicalize_name(package)\n        pkg = None\n\n        for locked in locked_packages:\n            if locked.name == canonicalized_package:\n                pkg = locked\n                break\n\n        if not pkg:\n            raise ValueError(f\"Package {package} not found\")\n\n        required_by = reverse_deps(pkg, locked_repository)\n\n        if self.option(\"tree\"):\n            if self.option(\"why\"):\n                # The default case if there's no reverse dependencies is to query\n                # the subtree for pkg but if any rev-deps exist we'll query for each\n                # of them in turn\n                packages = [pkg]\n                if required_by:\n                    packages = [\n                        p for p in locked_packages for r in required_by if p.name == r\n                    ]\n                else:\n                    # if no rev-deps exist we'll make this clear as it can otherwise\n                    # look very odd for packages that also have no or few direct\n                    # dependencies\n                    self.io.write_line(f\"Package {package} is a direct dependency.\")\n\n                for p in packages:\n                    self.display_package_tree(\n                        self.io, p, locked_packages, why_package=pkg\n                    )\n\n            else:\n                self.display_package_tree(self.io, pkg, locked_packages)\n\n            return 0\n\n        if self.option(\"format\") == OutputFormats.JSON:\n            package_info: dict[str, str | dict[str, str]] = {\n                \"name\": pkg.pretty_name,\n                \"version\": pkg.pretty_version,\n                \"description\": pkg.description,\n            }\n            if pkg.requires:\n                package_info[\"dependencies\"] = {\n                    dependency.pretty_name: dependency.pretty_constraint\n                    for dependency in pkg.requires\n                }\n            if required_by:\n                package_info[\"required_by\"] = required_by\n\n            self.line(json.dumps(package_info))\n\n            return 0\n\n        rows: Rows = [\n            [\"<info>name</>\", f\" : <c1>{pkg.pretty_name}</>\"],\n            [\"<info>version</>\", f\" : <b>{pkg.pretty_version}</b>\"],\n            [\"<info>description</>\", f\" : {pkg.description}\"],\n        ]\n\n        self.table(rows=rows, style=\"compact\").render()\n\n        if pkg.requires:\n            self.line(\"\")\n            self.line(\"<info>dependencies</info>\")\n            for dependency in pkg.requires:\n                self.line(\n                    f\" - <c1>{dependency.pretty_name}</c1>\"\n                    f\" <b>{dependency.pretty_constraint}</b>\"\n                )\n\n        if required_by:\n            self.line(\"\")\n            self.line(\"<info>required by</info>\")\n            for parent, requires_version in required_by.items():\n                self.line(f\" - <c1>{parent}</c1> requires <b>{requires_version}</b>\")\n\n        return 0\n\n    def _display_packages_information(\n        self, locked_repository: Repository, root: ProjectPackage\n    ) -> int:\n        import shutil\n\n        from cleo.io.null_io import NullIO\n\n        from poetry.puzzle.solver import Solver\n        from poetry.repositories.installed_repository import InstalledRepository\n        from poetry.repositories.repository_pool import RepositoryPool\n        from poetry.utils.helpers import get_package_version_display_string\n\n        locked_packages = locked_repository.packages\n        pool = RepositoryPool.from_packages(locked_packages, self.poetry.config)\n        solver = Solver(\n            root,\n            pool=pool,\n            installed=[],\n            locked=locked_packages,\n            io=NullIO(),\n        )\n        solver.provider.load_deferred(False)\n        with solver.use_environment(self.env):\n            ops = solver.solve().calculate_operations()\n\n        required_locked_packages = {op.package for op in ops if not op.skipped}\n\n        show_latest = self.option(\"latest\")\n        show_all = self.option(\"all\")\n        show_top_level = self.option(\"top-level\")\n        show_why = self.option(\"why\")\n        width = (\n            sys.maxsize\n            if self.option(\"no-truncate\")\n            else shutil.get_terminal_size().columns\n        )\n        name_length = version_length = latest_length = required_by_length = 0\n        latest_packages = {}\n        latest_statuses = {}\n        installed_repo = InstalledRepository.load(self.env)\n        requires = root.all_requires\n\n        # Computing widths\n        for locked in locked_packages:\n            if locked not in required_locked_packages and not show_all:\n                continue\n\n            current_length = len(locked.pretty_name)\n            if not self.io.output.is_decorated():\n                installed_status = self.get_installed_status(\n                    locked, installed_repo.packages\n                )\n\n                if installed_status == \"not-installed\":\n                    current_length += 4\n\n            if show_latest:\n                latest = self.find_latest_package(locked, root)\n                if not latest:\n                    latest = locked\n\n                latest_packages[locked.pretty_name] = latest\n                update_status = latest_statuses[locked.pretty_name] = (\n                    self.get_update_status(latest, locked)\n                )\n\n                if not self.option(\"outdated\") or update_status != \"up-to-date\":\n                    name_length = max(name_length, current_length)\n                    version_length = max(\n                        version_length,\n                        len(\n                            get_package_version_display_string(\n                                locked, root=self.poetry.file.path.parent\n                            )\n                        ),\n                    )\n                    latest_length = max(\n                        latest_length,\n                        len(\n                            get_package_version_display_string(\n                                latest, root=self.poetry.file.path.parent\n                            )\n                        ),\n                    )\n\n                    if show_why:\n                        required_by = reverse_deps(locked, locked_repository)\n                        required_by_length = max(\n                            required_by_length,\n                            len(\" from \" + \",\".join(required_by.keys())),\n                        )\n            else:\n                name_length = max(name_length, current_length)\n                version_length = max(\n                    version_length,\n                    len(\n                        get_package_version_display_string(\n                            locked, root=self.poetry.file.path.parent\n                        )\n                    ),\n                )\n\n                if show_why:\n                    required_by = reverse_deps(locked, locked_repository)\n                    required_by_length = max(\n                        required_by_length, len(\" from \" + \",\".join(required_by.keys()))\n                    )\n\n        if self.option(\"format\") == OutputFormats.JSON:\n            packages = []\n\n            for locked in locked_packages:\n                if locked not in required_locked_packages and not show_all:\n                    continue\n\n                if (\n                    show_latest\n                    and self.option(\"outdated\")\n                    and latest_statuses[locked.pretty_name] == \"up-to-date\"\n                ):\n                    continue\n\n                if show_top_level and not any(locked.satisfies(r) for r in requires):\n                    continue\n\n                package: dict[str, str | list[str]] = {}\n                package[\"name\"] = locked.pretty_name\n                package[\"installed_status\"] = self.get_installed_status(\n                    locked, installed_repo.packages\n                )\n                package[\"version\"] = get_package_version_display_string(\n                    locked, root=self.poetry.file.path.parent\n                )\n\n                if show_latest:\n                    latest = latest_packages[locked.pretty_name]\n                    package[\"latest_version\"] = get_package_version_display_string(\n                        latest, root=self.poetry.file.path.parent\n                    )\n\n                if show_why:\n                    required_by = reverse_deps(locked, locked_repository)\n                    if required_by:\n                        package[\"required_by\"] = list(required_by.keys())\n\n                package[\"description\"] = locked.description\n\n                packages.append(package)\n\n            self.line(json.dumps(packages))\n\n            return 0\n\n        write_version = name_length + version_length + 3 <= width\n        write_latest = name_length + version_length + latest_length + 3 <= width\n\n        why_end_column = (\n            name_length + version_length + latest_length + required_by_length\n        )\n        write_why = show_why and (why_end_column + 3) <= width\n        write_description = (why_end_column + 24) <= width\n\n        for locked in locked_packages:\n            color = \"cyan\"\n            name = locked.pretty_name\n            install_marker = \"\"\n\n            if show_top_level and not any(locked.satisfies(r) for r in requires):\n                continue\n\n            if locked not in required_locked_packages:\n                if not show_all:\n                    continue\n\n                color = \"black;options=bold\"\n            else:\n                installed_status = self.get_installed_status(\n                    locked, installed_repo.packages\n                )\n                if installed_status == \"not-installed\":\n                    color = \"red\"\n\n                    if not self.io.output.is_decorated():\n                        # Non installed in non decorated mode\n                        install_marker = \" (!)\"\n\n            if (\n                show_latest\n                and self.option(\"outdated\")\n                and latest_statuses[locked.pretty_name] == \"up-to-date\"\n            ):\n                continue\n\n            line = (\n                f\"<fg={color}>\"\n                f\"{name:{name_length - len(install_marker)}}{install_marker}</>\"\n            )\n            if write_version:\n                version = get_package_version_display_string(\n                    locked, root=self.poetry.file.path.parent\n                )\n                line += f\" <b>{version:{version_length}}</b>\"\n            if show_latest:\n                latest = latest_packages[locked.pretty_name]\n                update_status = latest_statuses[locked.pretty_name]\n\n                if write_latest:\n                    color = \"green\"\n                    if update_status == \"semver-safe-update\":\n                        color = \"red\"\n                    elif update_status == \"update-possible\":\n                        color = \"yellow\"\n\n                    version = get_package_version_display_string(\n                        latest, root=self.poetry.file.path.parent\n                    )\n                    line += f\" <fg={color}>{version:{latest_length}}</>\"\n\n            if write_why:\n                required_by = reverse_deps(locked, locked_repository)\n                if required_by:\n                    content = \",\".join(required_by.keys())\n                    # subtract 6 for ' from '\n                    line += f\" from {content:{required_by_length - 6}}\"\n                else:\n                    line += \" \" * required_by_length\n\n            if write_description:\n                description = locked.description\n                remaining = (\n                    width - name_length - version_length - required_by_length - 4\n                )\n\n                if show_latest:\n                    remaining -= latest_length\n\n                if len(locked.description) > remaining:\n                    description = description[: remaining - 3] + \"...\"\n\n                line += \" \" + description\n\n            self.line(line)\n\n        return 0\n\n    def _display_packages_tree_information(\n        self, locked_repository: Repository, root: ProjectPackage\n    ) -> int:\n        packages = locked_repository.packages\n\n        for p in packages:\n            for require in root.all_requires:\n                if p.name == require.name:\n                    self.display_package_tree(self.io, p, packages)\n                    break\n\n        return 0\n\n    def display_package_tree(\n        self,\n        io: IO,\n        package: Package,\n        installed_packages: list[Package],\n        why_package: Package | None = None,\n    ) -> None:\n        io.write(f\"<c1>{package.pretty_name}</c1>\")\n        description = \"\"\n        if package.description:\n            description = \" \" + package.description\n\n        io.write_line(f\" <b>{package.pretty_version}</b>{description}\")\n\n        if why_package is not None:\n            dependencies = [p for p in package.requires if p.name == why_package.name]\n        else:\n            dependencies = package.requires\n            dependencies = sorted(\n                dependencies,\n                key=lambda x: x.name,\n            )\n\n        tree_bar = \"├\"\n        total = len(dependencies)\n        for i, dependency in enumerate(dependencies, 1):\n            if i == total:\n                tree_bar = \"└\"\n\n            level = 1\n            color = self.colors[level]\n            info = (\n                f\"{tree_bar}── <{color}>{dependency.name}</{color}>\"\n                f\" {dependency.pretty_constraint}\"\n            )\n            self._write_tree_line(io, info)\n\n            tree_bar = tree_bar.replace(\"└\", \" \")\n            packages_in_tree = [package.name, dependency.name]\n\n            self._display_tree(\n                io,\n                dependency,\n                installed_packages,\n                packages_in_tree,\n                tree_bar,\n                level + 1,\n            )\n\n    def _display_tree(\n        self,\n        io: IO,\n        dependency: Dependency,\n        installed_packages: list[Package],\n        packages_in_tree: list[NormalizedName],\n        previous_tree_bar: str = \"├\",\n        level: int = 1,\n    ) -> None:\n        previous_tree_bar = previous_tree_bar.replace(\"├\", \"│\")\n\n        dependencies = []\n        for package in installed_packages:\n            if package.name == dependency.name:\n                dependencies = package.requires\n\n                break\n\n        dependencies = sorted(\n            dependencies,\n            key=lambda x: x.name,\n        )\n        tree_bar = previous_tree_bar + \"   ├\"\n        total = len(dependencies)\n        for i, dependency in enumerate(dependencies, 1):\n            current_tree = packages_in_tree\n            if i == total:\n                tree_bar = previous_tree_bar + \"   └\"\n\n            color_ident = level % len(self.colors)\n            color = self.colors[color_ident]\n\n            circular_warn = \"\"\n            if dependency.name in current_tree:\n                circular_warn = \"(circular dependency aborted here)\"\n\n            info = (\n                f\"{tree_bar}── <{color}>{dependency.name}</{color}>\"\n                f\" {dependency.pretty_constraint} {circular_warn}\"\n            )\n            self._write_tree_line(io, info)\n\n            tree_bar = tree_bar.replace(\"└\", \" \")\n\n            if dependency.name not in current_tree:\n                current_tree.append(dependency.name)\n\n                self._display_tree(\n                    io,\n                    dependency,\n                    installed_packages,\n                    current_tree,\n                    tree_bar,\n                    level + 1,\n                )\n\n    def _write_tree_line(self, io: IO, line: str) -> None:\n        if not io.output.supports_utf8():\n            line = line.replace(\"└\", \"`-\")\n            line = line.replace(\"├\", \"|-\")\n            line = line.replace(\"──\", \"-\")\n            line = line.replace(\"│\", \"|\")\n\n        io.write_line(line)\n\n    def init_styles(self, io: IO) -> None:\n        from cleo.formatters.style import Style\n\n        for color in self.colors:\n            style = Style(color)\n            io.output.formatter.set_style(color, style)\n            io.error_output.formatter.set_style(color, style)\n\n    def find_latest_package(\n        self, package: Package, root: ProjectPackage\n    ) -> Package | None:\n        from cleo.io.null_io import NullIO\n\n        from poetry.puzzle.provider import Provider\n        from poetry.version.version_selector import VersionSelector\n\n        # find the latest version allowed in this pool\n        requires = root.all_requires\n        if package.is_direct_origin():\n            for dep in requires:\n                if dep.name == package.name and dep.source_type == package.source_type:\n                    provider = Provider(root, self.poetry.pool, NullIO())\n                    return provider.search_for_direct_origin_dependency(dep)\n\n        allow_prereleases: bool | None = None\n        for dep in requires:\n            if dep.name == package.name:\n                allow_prereleases = dep.allows_prereleases()\n                break\n\n        name = package.name\n        selector = VersionSelector(self.poetry.pool)\n\n        return selector.find_best_candidate(\n            name, f\">={package.pretty_version}\", allow_prereleases\n        )\n\n    def get_update_status(self, latest: Package, package: Package) -> str:\n        from poetry.core.constraints.version import parse_constraint\n\n        if latest.full_pretty_version == package.full_pretty_version:\n            return \"up-to-date\"\n\n        constraint = parse_constraint(\"^\" + package.pretty_version)\n\n        if constraint.allows(latest.version):\n            # It needs an immediate semver-compliant upgrade\n            return \"semver-safe-update\"\n\n        # it needs an upgrade but has potential BC breaks so is not urgent\n        return \"update-possible\"\n\n    def get_installed_status(\n        self, locked: Package, installed_packages: list[Package]\n    ) -> str:\n        for package in installed_packages:\n            if locked.name == package.name:\n                return \"installed\"\n\n        return \"not-installed\"\n"
  },
  {
    "path": "src/poetry/console/commands/source/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/commands/source/add.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom cleo.io.null_io import NullIO\nfrom tomlkit import table\nfrom tomlkit.items import AoT\n\nfrom poetry.config.source import Source\nfrom poetry.console.commands.command import Command\nfrom poetry.repositories.repository_pool import Priority\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n\nclass SourceAddCommand(Command):\n    name = \"source add\"\n    description = \"Add source configuration for project.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"name\",\n            \"Source repository name.\",\n        ),\n        argument(\n            \"url\",\n            \"Source repository URL.\"\n            \" Required, except for PyPI, for which it is not allowed.\",\n            optional=True,\n        ),\n    ]\n\n    options: ClassVar[list[Option]] = [\n        option(\n            \"priority\",\n            \"p\",\n            \"Set the priority of this source. One of:\"\n            f\" {', '.join(p.name.lower() for p in Priority)}. Defaults to\"\n            f\" {Priority.PRIMARY.name.lower()}, but will switch to \"\n            f\"{Priority.SUPPLEMENTAL.name.lower()} in a later release.\",\n            flag=False,\n        ),\n    ]\n\n    def handle(self) -> int:\n        from poetry.factory import Factory\n\n        name: str = self.argument(\"name\")\n        lower_name = name.lower()\n        url: str = self.argument(\"url\")\n        priority_str: str | None = self.option(\"priority\", None)\n\n        if lower_name == \"pypi\":\n            name = \"PyPI\"\n            if url:\n                self.line_error(\n                    \"<error>The URL of PyPI is fixed and cannot be set.</error>\"\n                )\n                return 1\n        elif not url:\n            self.line_error(\n                \"<error>A custom source cannot be added without a URL.</error>\"\n            )\n            return 1\n\n        if priority_str is None:\n            self.io.write_error_line(\n                f\"<warning>The default priority will change to <b>{Priority.SUPPLEMENTAL.name.lower()}</> \"\n                f\"in a future release.</>\"\n            )\n            priority = Priority.PRIMARY\n        else:\n            priority = Priority[priority_str.upper()]\n\n        sources = AoT([])\n        new_source = Source(name=name, url=url, priority=priority)\n        is_new_source = True\n\n        for source in self.poetry.get_sources():\n            if source.name.lower() == lower_name:\n                source = new_source\n                is_new_source = False\n\n            sources.append(source.to_toml_table())\n\n        if is_new_source:\n            self.line(f\"Adding source with name <c1>{name}</c1>.\")\n            sources.append(new_source.to_toml_table())\n        else:\n            self.line(f\"Source with name <c1>{name}</c1> already exists. Updating.\")\n\n        # ensure new source is valid. eg: invalid name etc.\n        try:\n            pool = Factory.create_pool(self.poetry.config, sources, NullIO())\n            pool.repository(name)\n        except ValueError as e:\n            self.line_error(\n                f\"<error>Failed to validate addition of <c1>{name}</c1>: {e}</error>\"\n            )\n            return 1\n\n        # tomlkit types are awkward to work with, treat content as a mostly untyped\n        # dictionary.\n        content: dict[str, Any] = self.poetry.pyproject.data\n        if \"tool\" not in content:\n            content[\"tool\"] = table()\n        if \"poetry\" not in content[\"tool\"]:\n            content[\"tool\"][\"poetry\"] = table()\n        self.poetry.pyproject.poetry_config[\"source\"] = sources\n        self.poetry.pyproject.save()\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/source/remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom tomlkit.items import AoT\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n\n\nclass SourceRemoveCommand(Command):\n    name = \"source remove\"\n    description = \"Remove source configured for the project.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"name\",\n            \"Source repository name.\",\n        ),\n    ]\n\n    def handle(self) -> int:\n        name = self.argument(\"name\")\n        lower_name = name.lower()\n\n        sources = AoT([])\n        removed = False\n\n        for source in self.poetry.get_sources():\n            if source.name.lower() == lower_name:\n                self.line(f\"Removing source with name <c1>{source.name}</c1>.\")\n                removed = True\n                continue\n            sources.append(source.to_toml_table())\n\n        if not removed:\n            self.line_error(\n                f\"<error>Source with name <c1>{name}</c1> was not found.</error>\"\n            )\n            return 1\n\n        self.poetry.pyproject.poetry_config[\"source\"] = sources\n        self.poetry.pyproject.save()\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/source/show.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.ui.table import Rows\n\n\nclass SourceShowCommand(Command):\n    name = \"source show\"\n    description = \"Show information about sources configured for the project.\"\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"source\",\n            \"Source(s) to show information for. Defaults to showing all sources.\",\n            optional=True,\n            multiple=True,\n        ),\n    ]\n\n    def notify_implicit_pypi(self) -> None:\n        if not self.poetry.pool.has_repository(\"pypi\"):\n            return\n\n        self.line(\n            \"<c1><b>PyPI</> is implicitly enabled as a <b>primary</> source. \"\n            \"If you wish to disable it, or alter its priority please refer to \"\n            \"<b>https://python-poetry.org/docs/repositories/#package-sources</>.</>\"\n        )\n        self.line(\"\")\n\n    def handle(self) -> int:\n        sources = self.poetry.get_sources()\n        names = self.argument(\"source\")\n        lower_names = [name.lower() for name in names]\n\n        if not sources:\n            self.line(\"No sources configured for this project.\\n\")\n            self.notify_implicit_pypi()\n            return 0\n\n        if names and not any(s.name.lower() in lower_names for s in sources):\n            self.line_error(\n                f\"No source found with name(s): {', '.join(names)}\",\n                style=\"error\",\n            )\n            return 1\n\n        is_pypi_implicit = True\n\n        for source in sources:\n            if names and source.name.lower() not in lower_names:\n                continue\n\n            if source.name.lower() == \"pypi\":\n                is_pypi_implicit = False\n\n            table = self.table(style=\"compact\")\n            rows: Rows = [[\"<info>name</>\", f\" : <c1>{source.name}</>\"]]\n            if source.url:\n                rows.append([\"<info>url</>\", f\" : {source.url}\"])\n            rows.append([\"<info>priority</>\", f\" : {source.priority.name.lower()}\"])\n            table.add_rows(rows)\n            table.render()\n            self.line(\"\")\n\n        if not names and is_pypi_implicit:\n            self.notify_implicit_pypi()\n\n        return 0\n"
  },
  {
    "path": "src/poetry/console/commands/sync.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.console.commands.install import InstallCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.option import Option\n\n\nclass SyncCommand(InstallCommand):\n    name = \"sync\"\n    description = \"Update the project's environment according to the lockfile.\"\n\n    options: ClassVar[list[Option]] = [\n        opt for opt in InstallCommand.options if opt.name != \"sync\"\n    ]\n\n    help = \"\"\"\\\nThe <info>sync</info> command makes sure that the project's environment is in sync with\nthe <comment>poetry.lock</> file.\nIt is equivalent to running <info>poetry install --sync</info>.\n\n<info>poetry sync</info>\n\nBy default, the above command will also install the current project. To install only the\ndependencies and not including the current project, run the command with the\n<info>--no-root</info> option like below:\n\n<info> poetry sync --no-root</info>\n\nIf you want to use Poetry only for dependency management but not for packaging,\nyou can set the \"package-mode\" to false in your pyproject.toml file.\n\"\"\"\n\n    @property\n    def _with_synchronization(self) -> bool:\n        return True\n"
  },
  {
    "path": "src/poetry/console/commands/update.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.console.commands.installer_command import InstallerCommand\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n\n_VERSION_SPECIFIER_RE = re.compile(r\"[><=!~]\")\n\n\nclass UpdateCommand(InstallerCommand):\n    name = \"update\"\n    description = (\n        \"Update the dependencies as according to the <comment>pyproject.toml</> file.\"\n    )\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\"packages\", \"The packages to update\", optional=True, multiple=True)\n    ]\n    options: ClassVar[list[Option]] = [\n        *InstallerCommand._group_dependency_options(),\n        option(\n            \"sync\",\n            None,\n            \"Synchronize the environment with the locked packages and the specified\"\n            \" groups.\",\n        ),\n        option(\n            \"dry-run\",\n            None,\n            \"Output the operations but do not execute anything \"\n            \"(implicitly enables --verbose).\",\n        ),\n        option(\"lock\", None, \"Do not perform operations (only update the lockfile).\"),\n    ]\n\n    loggers: ClassVar[list[str]] = [\"poetry.repositories.pypi_repository\"]\n\n    def handle(self) -> int:\n        packages = self.argument(\"packages\")\n        if packages:\n            # Detect version specifiers in package arguments — poetry update\n            # only accepts bare package names, not requirement strings.\n            packages_with_specifiers = [\n                p for p in packages if _VERSION_SPECIFIER_RE.search(p)\n            ]\n            if packages_with_specifiers:\n                self.line_error(\n                    \"<error>Version specifiers are not allowed in\"\n                    \" <c1>poetry update</c1>.</error>\"\n                )\n                for pkg in packages_with_specifiers:\n                    self.line_error(f\"  - {pkg}\")\n                self.line_error(\n                    \"Use <c1>poetry add</c1> to change version constraints.\"\n                )\n                return 1\n\n            # Validate that all specified packages are declared dependencies\n            all_dependencies = {dep.name for dep in self.poetry.package.all_requires}\n\n            invalid_packages = [\n                p for p in packages if canonicalize_name(p) not in all_dependencies\n            ]\n\n            if invalid_packages:\n                self.line_error(\n                    \"<error>The following packages are not dependencies\"\n                    f\" of this project: {', '.join(invalid_packages)}</error>\"\n                )\n                return 1\n\n            self.installer.whitelist(dict.fromkeys(packages, \"*\"))\n\n        self.installer.only_groups(self.activated_groups)\n        self.installer.dry_run(self.option(\"dry-run\"))\n        self.installer.requires_synchronization(self.option(\"sync\"))\n        self.installer.execute_operations(not self.option(\"lock\"))\n\n        # Force update\n        self.installer.update(True)\n\n        return self.installer.run()\n"
  },
  {
    "path": "src/poetry/console/commands/version.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\n\nfrom cleo.helpers import argument\nfrom cleo.helpers import option\nfrom poetry.core.version.exceptions import InvalidVersionError\nfrom tomlkit.toml_document import TOMLDocument\n\nfrom poetry.console.commands.command import Command\n\n\nif TYPE_CHECKING:\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from poetry.core.constraints.version import Version\n\n\nclass VersionCommand(Command):\n    name = \"version\"\n    description = (\n        \"Shows the version of the project or bumps it when a valid \"\n        \"bump rule is provided.\"\n    )\n\n    arguments: ClassVar[list[Argument]] = [\n        argument(\n            \"version\",\n            \"The version number or the rule to update the version.\",\n            optional=True,\n        ),\n    ]\n    options: ClassVar[list[Option]] = [\n        option(\"short\", \"s\", \"Output the version number only\"),\n        option(\n            \"dry-run\",\n            None,\n            \"Do not update pyproject.toml file\",\n        ),\n        option(\"next-phase\", None, \"Increment the phase of the current version\"),\n    ]\n\n    help = \"\"\"\\\nThe version command shows the current version of the project or bumps the version of\nthe project and writes the new version back to <comment>pyproject.toml</> if a valid\nbump rule is provided.\n\nThe new version should ideally be a valid semver string or a valid bump rule:\npatch, minor, major, prepatch, preminor, premajor, prerelease.\n\"\"\"\n\n    def handle(self) -> int:\n        version = self.argument(\"version\")\n\n        if version:\n            version = self.increment_version(\n                self.poetry.package.pretty_version, version, self.option(\"next-phase\")\n            )\n\n            if self.option(\"short\"):\n                self.line(version.to_string())\n            else:\n                self.line(\n                    f\"Bumping version from <b>{self.poetry.package.pretty_version}</>\"\n                    f\" to <fg=green>{version}</>\"\n                )\n\n            if not self.option(\"dry-run\"):\n                content: dict[str, Any] = self.poetry.file.read()\n                project_content = content.get(\"project\", {})\n                if \"version\" in project_content:\n                    project_content[\"version\"] = version.text\n                poetry_content = content.get(\"tool\", {}).get(\"poetry\", {})\n                if \"version\" in poetry_content:\n                    poetry_content[\"version\"] = version.text\n\n                assert isinstance(content, TOMLDocument)\n                self.poetry.file.write(content)\n        else:\n            if self.option(\"short\"):\n                self.line(self.poetry.package.pretty_version)\n            else:\n                self.line(\n                    f\"<comment>{self.poetry.package.pretty_name}</>\"\n                    f\" <info>{self.poetry.package.pretty_version}</>\"\n                )\n\n        return 0\n\n    def increment_version(\n        self, version: str, rule: str, next_phase: bool = False\n    ) -> Version:\n        from poetry.core.constraints.version import Version\n\n        try:\n            parsed = Version.parse(version)\n        except InvalidVersionError:\n            raise ValueError(\"The project's version doesn't seem to follow semver\")\n\n        if rule in {\"major\", \"premajor\"}:\n            new = parsed.next_major()\n            if rule == \"premajor\":\n                new = new.first_prerelease()\n        elif rule in {\"minor\", \"preminor\"}:\n            new = parsed.next_minor()\n            if rule == \"preminor\":\n                new = new.first_prerelease()\n        elif rule in {\"patch\", \"prepatch\"}:\n            new = parsed.next_patch()\n            if rule == \"prepatch\":\n                new = new.first_prerelease()\n        elif rule == \"prerelease\":\n            if parsed.is_unstable():\n                pre = parsed.pre\n                assert pre is not None\n                pre = pre.next_phase() if next_phase else pre.next()\n                new = Version(parsed.epoch, parsed.release, pre)\n            else:\n                new = parsed.next_patch().first_prerelease()\n        else:\n            new = Version.parse(rule)\n\n        return new\n"
  },
  {
    "path": "src/poetry/console/events/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/events/console_events.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/exceptions.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport shlex\n\nfrom dataclasses import InitVar\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\n\nfrom cleo.exceptions import CleoError\n\nfrom poetry.utils._compat import decode\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n\nclass PoetryConsoleError(CleoError):\n    pass\n\n\nclass GroupNotFoundError(PoetryConsoleError):\n    pass\n\n\n@dataclasses.dataclass\nclass ConsoleMessage:\n    \"\"\"\n    Representation of a console message, providing utilities for formatting text\n    with tags, indentation, and sections.\n\n    The ConsoleMessage class is designed to represent text messages that might be\n    displayed in a console or terminal output. It provides features for managing\n    formatted text, such as stripping tags, wrapping text with specific tags,\n    indenting, and creating structured message sections.\n    \"\"\"\n\n    text: str\n    debug: bool = False\n\n    @property\n    def stripped(self) -> str:\n        from cleo._utils import strip_tags\n\n        return strip_tags(self.text)\n\n    def wrap(self, tag: str) -> ConsoleMessage:\n        if self.text:\n            self.text = f\"<{tag}>{self.text}</>\"\n        return self\n\n    def indent(self, indent: str) -> ConsoleMessage:\n        if self.text:\n            self.text = f\"\\n{indent}\".join(self.text.splitlines()).strip()\n            self.text = f\"{indent}{self.text}\"\n        return self\n\n    def make_section(\n        self,\n        title: str,\n        indent: str = \"\",\n    ) -> ConsoleMessage:\n        if not self.text:\n            return self\n\n        if self.text:\n            section = [f\"<b>{title}:</>\"] if title else []\n            section.extend(self.text.splitlines())\n            self.text = f\"\\n{indent}\".join(section).strip()\n\n        return self\n\n\n@dataclasses.dataclass\nclass PrettyCalledProcessError:\n    \"\"\"\n    Represents a formatted and decorated error object for a subprocess call.\n\n    This class is used to encapsulate information about a `CalledProcessError`,\n    providing additional context such as command output, errors, and helpful\n    debugging messages. It is particularly useful for wrapping and decorating\n    subprocess-related exceptions in a more user-friendly format.\n\n    Attributes:\n        message: A string representation of the exception.\n        output: A section formatted representation of the exception stdout.\n        errors: A section formatted representation of the exception stderr.\n        command_message: Formatted message including a hint on retrying the original command.\n        command: A `shelex` quoted string representation of the original command.\n        exception: The original `CalledProcessError` instance.\n        indent: Indent prefix to use for inner content per section.\n    \"\"\"\n\n    message: ConsoleMessage = dataclasses.field(init=False)\n    output: ConsoleMessage = dataclasses.field(init=False)\n    errors: ConsoleMessage = dataclasses.field(init=False)\n    command_message: ConsoleMessage = dataclasses.field(init=False)\n    command: str = dataclasses.field(init=False)\n    exception: InitVar[CalledProcessError] = dataclasses.field(init=True)\n    indent: InitVar[str] = dataclasses.field(default=\"\")\n\n    def __post_init__(self, exception: CalledProcessError, indent: str) -> None:\n        self.message = ConsoleMessage(str(exception).strip(), debug=True).make_section(\n            \"Exception\", indent\n        )\n        self.output = ConsoleMessage(decode(exception.stdout), debug=True).make_section(\n            \"Output\", indent\n        )\n        self.errors = ConsoleMessage(decode(exception.stderr), debug=True).make_section(\n            \"Errors\", indent\n        )\n        self.command = (\n            shlex.join(exception.cmd)\n            if isinstance(exception.cmd, list)\n            else exception.cmd\n        )\n        self.command_message = ConsoleMessage(\n            f\"You can test the failed command by executing:\\n\\n    <c1>{self.command}</c1>\",\n            debug=False,\n        )\n\n\nclass PoetryRuntimeError(PoetryConsoleError):\n    \"\"\"\n    Represents a runtime error in the Poetry console application.\n    \"\"\"\n\n    def __init__(\n        self,\n        reason: str,\n        messages: list[ConsoleMessage] | None = None,\n        exit_code: int = 1,\n    ) -> None:\n        super().__init__(reason)\n        self.exit_code = exit_code\n        self._messages = messages or []\n        self._messages.insert(0, ConsoleMessage(reason))\n\n    def write(self, io: IO) -> None:\n        \"\"\"\n        Write the error text to the provided IO iff there is any text\n        to write.\n        \"\"\"\n        if text := self.get_text(debug=io.is_verbose(), strip=False):\n            io.write_error_line(text)\n\n    def get_text(\n        self, debug: bool = False, indent: str = \"\", strip: bool = False\n    ) -> str:\n        \"\"\"\n        Convert the error messages to a formatted string. All empty messages\n        are ignored along with debug level messages if `debug` is `False`.\n        \"\"\"\n        text = \"\"\n        has_skipped_debug = False\n\n        for message in self._messages:\n            if message.debug and not debug:\n                has_skipped_debug = True\n                continue\n\n            message_text = message.stripped if strip else message.text\n            if not message_text:\n                continue\n\n            if indent:\n                message_text = f\"\\n{indent}\".join(message_text.splitlines())\n\n            text += f\"{indent}{message_text}\\n{indent}\\n\"\n\n        if has_skipped_debug:\n            message = ConsoleMessage(\n                f\"{indent}You can also run your <c1>poetry</> command with <c1>-v</> to see more information.\\n{indent}\\n\"\n            )\n            text += message.stripped if strip else message.text\n\n        return text.rstrip(f\"{indent}\\n\")\n\n    def __str__(self) -> str:\n        return self._messages[0].stripped.strip()\n\n    @classmethod\n    def create(\n        cls,\n        reason: str,\n        exception: CalledProcessError | Exception | None = None,\n        info: list[str] | str | None = None,\n    ) -> PoetryRuntimeError:\n        \"\"\"\n        Create an instance of this class using the provided reason. If\n        an exception is provided, this is also injected as a debug\n        `ConsoleMessage`.\n\n        There is specific handling for known exception types. For example,\n        if exception is of type `subprocess.CalledProcessError`, the following\n        sections are additionally added when available - stdout, stderr and\n        command for testing.\n        \"\"\"\n        if isinstance(info, str):\n            info = [info]\n\n        messages: list[ConsoleMessage] = [\n            ConsoleMessage(\n                \"\\n\".join(info or []),\n                debug=False,\n            ).wrap(\"info\"),\n        ]\n\n        if isinstance(exception, CalledProcessError):\n            error = PrettyCalledProcessError(exception, indent=\"    | \")\n            messages = [\n                error.message.wrap(\"warning\"),\n                error.output.wrap(\"warning\"),\n                error.errors.wrap(\"warning\"),\n                *messages,\n                error.command_message,\n            ]\n        elif exception is not None and isinstance(exception, Exception):\n            messages.insert(\n                0,\n                ConsoleMessage(str(exception), debug=True).make_section(\n                    \"Exception\", indent=\"    | \"\n                ),\n            )\n\n        return cls(reason, messages)\n\n    def append(self, message: str | ConsoleMessage) -> PoetryRuntimeError:\n        if isinstance(message, str):\n            message = ConsoleMessage(message)\n        self._messages.append(message)\n        return self\n"
  },
  {
    "path": "src/poetry/console/logging/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/console/logging/filters.py",
    "content": "from __future__ import annotations\n\nimport logging\n\n\nPOETRY_FILTER = logging.Filter(name=\"poetry\")\n"
  },
  {
    "path": "src/poetry/console/logging/formatters/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.console.logging.formatters.builder_formatter import BuilderLogFormatter\n\n\nFORMATTERS = {\n    \"poetry.core.masonry.builders.builder\": BuilderLogFormatter(),\n    \"poetry.core.masonry.builders.sdist\": BuilderLogFormatter(),\n    \"poetry.core.masonry.builders.wheel\": BuilderLogFormatter(),\n}\n"
  },
  {
    "path": "src/poetry/console/logging/formatters/builder_formatter.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom poetry.console.logging.formatters.formatter import Formatter\n\n\nclass BuilderLogFormatter(Formatter):\n    def format(self, msg: str) -> str:\n        if msg.startswith(\"Building \"):\n            msg = re.sub(\"Building (.+)\", \"  - Building <info>\\\\1</info>\", msg)\n        elif msg.startswith(\"Built \"):\n            msg = re.sub(\"Built (.+)\", \"  - Built <success>\\\\1</success>\", msg)\n        elif msg.startswith(\"Adding: \"):\n            msg = re.sub(\"Adding: (.+)\", \"  - Adding: <b>\\\\1</b>\", msg)\n        elif msg.startswith(\"Executing build script: \"):\n            msg = re.sub(\n                \"Executing build script: (.+)\",\n                \"  - Executing build script: <b>\\\\1</b>\",\n                msg,\n            )\n\n        return msg\n"
  },
  {
    "path": "src/poetry/console/logging/formatters/formatter.py",
    "content": "from __future__ import annotations\n\nfrom abc import ABC\nfrom abc import abstractmethod\n\n\nclass Formatter(ABC):\n    @abstractmethod\n    def format(self, msg: str) -> str: ...\n"
  },
  {
    "path": "src/poetry/console/logging/io_formatter.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport sys\nimport textwrap\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.console.logging.filters import POETRY_FILTER\nfrom poetry.console.logging.formatters import FORMATTERS\n\n\nif TYPE_CHECKING:\n    from logging import LogRecord\n\n\nclass IOFormatter(logging.Formatter):\n    _colors: ClassVar[dict[str, str]] = {\n        \"error\": \"fg=red\",\n        \"warning\": \"fg=yellow\",\n        \"debug\": \"debug\",\n        \"info\": \"fg=blue\",\n    }\n\n    def format(self, record: LogRecord) -> str:\n        if not record.exc_info:\n            level = record.levelname.lower()\n            msg = record.msg\n\n            if record.name in FORMATTERS:\n                msg = FORMATTERS[record.name].format(msg)\n            elif level in self._colors:\n                msg = f\"<{self._colors[level]}>{msg}</>\"\n\n            record.msg = msg\n\n        formatted = super().format(record)\n\n        if not POETRY_FILTER.filter(record):\n            # prefix all lines from third-party packages for easier debugging\n            formatted = textwrap.indent(\n                formatted, f\"[{_log_prefix(record)}] \", lambda line: True\n            )\n\n        return formatted\n\n\ndef _log_prefix(record: LogRecord) -> str:\n    prefix = _path_to_package(Path(record.pathname)) or record.module\n    if record.name != \"root\":\n        prefix = \":\".join([prefix, record.name])\n    return prefix\n\n\ndef _path_to_package(path: Path) -> str | None:\n    \"\"\"Return main package name from the LogRecord.pathname.\"\"\"\n    prefix: Path | None = None\n    # Find the most specific prefix in sys.path.\n    # We have to search the entire sys.path because a subsequent path might be\n    # a sub path of the first match and thereby a better match.\n    for syspath in sys.path:\n        if (\n            prefix and prefix in (p := Path(syspath)).parents and p in path.parents\n        ) or (not prefix and (p := Path(syspath)) in path.parents):\n            prefix = p\n    if not prefix:\n        # this is unexpected, but let's play it safe\n        return None\n    path = path.relative_to(prefix)\n    return path.parts[0]  # main package name\n"
  },
  {
    "path": "src/poetry/console/logging/io_handler.py",
    "content": "from __future__ import annotations\n\nimport logging\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from logging import LogRecord\n\n    from cleo.io.io import IO\n\n\nclass IOHandler(logging.Handler):\n    def __init__(self, io: IO) -> None:\n        self._io = io\n\n        super().__init__()\n\n    def emit(self, record: LogRecord) -> None:\n        try:\n            msg = self.format(record)\n            level = record.levelname.lower()\n            err = level in (\"warning\", \"error\", \"exception\", \"critical\")\n            if err:\n                self._io.write_error_line(msg)\n            else:\n                self._io.write_line(msg)\n        except Exception:\n            self.handleError(record)\n"
  },
  {
    "path": "src/poetry/exceptions.py",
    "content": "from __future__ import annotations\n\n\nclass PoetryError(Exception):\n    pass\n"
  },
  {
    "path": "src/poetry/factory.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport logging\nimport re\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import cast\n\nfrom cleo.io.null_io import NullIO\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.factory import Factory as BaseFactory\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\n\nfrom poetry.__version__ import __version__\nfrom poetry.config.config import Config\nfrom poetry.exceptions import PoetryError\nfrom poetry.json import validate_object\nfrom poetry.packages.locker import Locker\nfrom poetry.plugins.plugin import Plugin\nfrom poetry.plugins.plugin_manager import PluginManager\nfrom poetry.poetry import Poetry\nfrom poetry.pyproject.toml import PyProjectTOML\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils.isolated_build import CONSTRAINTS_GROUP_NAME\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n    from pathlib import Path\n\n    from cleo.io.io import IO\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from tomlkit.toml_document import TOMLDocument\n\n    from poetry.repositories import RepositoryPool\n    from poetry.repositories.http_repository import HTTPRepository\n    from poetry.utils.dependency_specification import DependencySpec\n\nlogger = logging.getLogger(__name__)\n\n\nclass Factory(BaseFactory):\n    \"\"\"\n    Factory class to create various elements needed by Poetry.\n    \"\"\"\n\n    def _ensure_valid_poetry_version(self, cwd: Path | None) -> None:\n        poetry_file = self.locate(cwd)\n        pyproject = PyProjectTOML(path=poetry_file)\n        poetry_config = pyproject.data.get(\"tool\", {}).get(\"poetry\", {})\n\n        if version_str := poetry_config.get(\"requires-poetry\"):\n            version_constraint = parse_constraint(version_str)\n            version = Version.parse(__version__)\n            if not version_constraint.allows(version):\n                raise PoetryError(\n                    f\"This project requires Poetry {version_constraint},\"\n                    f\" but you are using Poetry {version}\"\n                )\n\n    def create_poetry(\n        self,\n        cwd: Path | None = None,\n        with_groups: bool = True,\n        io: IO | None = None,\n        disable_plugins: bool = False,\n        disable_cache: bool = False,\n    ) -> Poetry:\n        if io is None:\n            io = NullIO()\n\n        self._ensure_valid_poetry_version(cwd)\n\n        base_poetry = super().create_poetry(cwd=cwd, with_groups=with_groups)\n\n        build_constraints: dict[NormalizedName, list[Dependency]] = {}\n        for name, constraints in base_poetry.local_config.get(\n            \"build-constraints\", {}\n        ).items():\n            name = canonicalize_name(name)\n            build_constraints[name] = []\n            for dep_name, constraint in constraints.items():\n                _constraints = (\n                    constraint if isinstance(constraint, list) else [constraint]\n                )\n                for _constraint in _constraints:\n                    build_constraints[name].append(\n                        Factory.create_dependency(\n                            dep_name, _constraint, groups=[CONSTRAINTS_GROUP_NAME]\n                        )\n                    )\n\n        poetry_file = base_poetry.pyproject_path\n        locker = Locker(poetry_file.parent / \"poetry.lock\", base_poetry.pyproject.data)\n\n        # Loading global configuration\n        config = Config.create()\n\n        # Loading local configuration\n        local_config_file = TOMLFile(poetry_file.parent / \"poetry.toml\")\n        if local_config_file.exists():\n            if io.is_debug():\n                io.write_line(f\"Loading configuration file {local_config_file.path}\")\n\n            config.merge(local_config_file.read())\n\n        # Load local sources\n        repositories = {}\n        existing_repositories = config.get(\"repositories\", {})\n        for source in base_poetry.local_config.get(\"source\", []):\n            name = source.get(\"name\")\n            url = source.get(\"url\")\n            if name and url and name not in existing_repositories:\n                repositories[name] = {\"url\": url}\n\n        config.merge({\"repositories\": repositories})\n\n        poetry = Poetry(\n            poetry_file,\n            base_poetry.local_config,\n            base_poetry.package,\n            locker,\n            config,\n            disable_cache=disable_cache,\n            build_constraints=build_constraints,\n        )\n\n        poetry.set_pool(\n            self.create_pool(\n                config,\n                poetry.local_config.get(\"source\", []),\n                io,\n                disable_cache=disable_cache,\n            )\n        )\n\n        if not disable_plugins:\n            plugin_manager = PluginManager(Plugin.group)\n            plugin_manager.load_plugins()\n            plugin_manager.activate(poetry, io)\n\n        return poetry\n\n    @classmethod\n    def create_pool(\n        cls,\n        config: Config,\n        sources: Iterable[dict[str, Any]] = (),\n        io: IO | None = None,\n        disable_cache: bool = False,\n    ) -> RepositoryPool:\n        from poetry.repositories import RepositoryPool\n        from poetry.repositories.repository_pool import Priority\n\n        if io is None:\n            io = NullIO()\n\n        if disable_cache:\n            logger.debug(\"Disabling source caches\")\n\n        pool = RepositoryPool(config=config)\n\n        explicit_pypi = False\n        for source in sources:\n            repository = cls.create_package_source(\n                source, config, disable_cache=disable_cache\n            )\n            priority = Priority[source.get(\"priority\", Priority.PRIMARY.name).upper()]\n\n            if io.is_debug():\n                io.write_line(\n                    f\"Adding repository {repository.name} ({repository.url})\"\n                    f\" and setting it as {priority.name.lower()}\"\n                )\n\n            pool.add_repository(repository, priority=priority)\n            if repository.name.lower() == \"pypi\":\n                explicit_pypi = True\n\n        # Only add PyPI if no primary repository is configured\n        if not explicit_pypi:\n            if pool.has_primary_repositories():\n                if io.is_debug():\n                    io.write_line(\"Deactivating the PyPI repository\")\n            else:\n                pool.add_repository(\n                    cls.create_package_source(\n                        {\"name\": \"pypi\"}, config, disable_cache=disable_cache\n                    ),\n                    priority=Priority.PRIMARY,\n                )\n\n        if not pool.repositories:\n            raise PoetryError(\n                \"At least one source must not be configured as 'explicit'.\"\n            )\n\n        return pool\n\n    @classmethod\n    def create_package_source(\n        cls, source: dict[str, str], config: Config, disable_cache: bool = False\n    ) -> HTTPRepository:\n        from poetry.repositories.exceptions import InvalidSourceError\n        from poetry.repositories.legacy_repository import LegacyRepository\n        from poetry.repositories.pypi_repository import PyPiRepository\n        from poetry.repositories.single_page_repository import SinglePageRepository\n\n        try:\n            name = source[\"name\"]\n        except KeyError:\n            raise InvalidSourceError(\"Missing [name] in source.\")\n\n        pool_size = config.installer_max_workers\n\n        if name.lower() == \"pypi\":\n            if \"url\" in source:\n                raise InvalidSourceError(\n                    \"The PyPI repository cannot be configured with a custom url.\"\n                )\n            return PyPiRepository(\n                config=config,\n                disable_cache=disable_cache,\n                pool_size=pool_size,\n            )\n\n        try:\n            url = source[\"url\"]\n        except KeyError:\n            raise InvalidSourceError(f\"Missing [url] in source {name!r}.\")\n\n        repository_class = LegacyRepository\n\n        if re.match(r\".*\\.(htm|html)$\", url):\n            repository_class = SinglePageRepository\n\n        return repository_class(\n            name,\n            url,\n            config=config,\n            disable_cache=disable_cache,\n            pool_size=pool_size,\n        )\n\n    @classmethod\n    def create_legacy_pyproject_from_package(cls, package: Package) -> TOMLDocument:\n        import tomlkit\n\n        from poetry.utils.dependency_specification import dependency_to_specification\n\n        pyproject: dict[str, Any] = tomlkit.document()\n\n        pyproject[\"tool\"] = tomlkit.table(is_super_table=True)\n\n        content: dict[str, Any] = tomlkit.table()\n        pyproject[\"tool\"][\"poetry\"] = content\n\n        content[\"name\"] = package.name\n        content[\"version\"] = package.version.text\n        content[\"description\"] = package.description\n        content[\"authors\"] = package.authors\n        content[\"license\"] = package.license.id if package.license else \"\"\n\n        if package.classifiers:\n            content[\"classifiers\"] = package.classifiers\n\n        if package.documentation_url:\n            content[\"documentation\"] = package.documentation_url\n\n        if package.repository_url:\n            content[\"repository\"] = package.repository_url\n\n        if package.homepage:\n            content[\"homepage\"] = package.homepage\n\n        if package.maintainers:\n            content[\"maintainers\"] = package.maintainers\n\n        if package.keywords:\n            content[\"keywords\"] = package.keywords\n\n        readmes = []\n\n        for readme in package.readmes:\n            readme_posix_path = readme.as_posix()\n\n            with contextlib.suppress(ValueError):\n                if package.root_dir:\n                    readme_posix_path = readme.relative_to(package.root_dir).as_posix()\n\n            readmes.append(readme_posix_path)\n\n        if readmes:\n            content[\"readme\"] = readmes\n\n        optional_dependencies = set()\n        extras_section = None\n\n        if package.extras:\n            extras_section = tomlkit.table()\n\n            for extra in package.extras:\n                _dependencies = []\n                for dependency in package.extras[extra]:\n                    _dependencies.append(dependency.name)\n                    optional_dependencies.add(dependency.name)\n\n                extras_section[extra] = _dependencies\n\n        optional_dependencies = set(optional_dependencies)\n        dependency_section = content[\"dependencies\"] = tomlkit.table()\n        dependency_section[\"python\"] = package.python_versions\n\n        for dep in package.all_requires:\n            constraint: DependencySpec | str = dependency_to_specification(\n                dep, tomlkit.inline_table()\n            )\n\n            if not isinstance(constraint, str):\n                if dep.name in optional_dependencies:\n                    constraint[\"optional\"] = True\n\n                if len(constraint) == 1 and \"version\" in constraint:\n                    assert isinstance(constraint[\"version\"], str)\n                    constraint = constraint[\"version\"]\n                elif not constraint:\n                    constraint = \"*\"\n\n            for group in dep.groups:\n                if group == MAIN_GROUP:\n                    dependency_section[dep.name] = constraint\n                else:\n                    if \"group\" not in content:\n                        content[\"group\"] = tomlkit.table(is_super_table=True)\n\n                    if group not in content[\"group\"]:\n                        content[\"group\"][group] = tomlkit.table(is_super_table=True)\n\n                    if \"dependencies\" not in content[\"group\"][group]:\n                        content[\"group\"][group][\"dependencies\"] = tomlkit.table()\n\n                    content[\"group\"][group][\"dependencies\"][dep.name] = constraint\n\n        if extras_section:\n            content[\"extras\"] = extras_section\n\n        pyproject = cast(\"TOMLDocument\", pyproject)\n\n        return pyproject\n\n    @classmethod\n    def validate(\n        cls, toml_data: dict[str, Any], strict: bool = False\n    ) -> dict[str, list[str]]:\n        results = super().validate(toml_data, strict)\n        poetry_config = toml_data[\"tool\"][\"poetry\"]\n\n        results[\"errors\"].extend(\n            [e.replace(\"data.\", \"tool.poetry.\") for e in validate_object(poetry_config)]\n        )\n\n        # A project should not depend on itself.\n        # TODO: consider [project.dependencies] and [project.optional-dependencies]\n        dependencies = set(poetry_config.get(\"dependencies\", {}).keys())\n        dependencies.update(poetry_config.get(\"dev-dependencies\", {}).keys())\n        groups = poetry_config.get(\"group\", {}).values()\n        for group in groups:\n            dependencies.update(group.get(\"dependencies\", {}).keys())\n\n        dependencies = {canonicalize_name(d) for d in dependencies}\n\n        project_name = toml_data.get(\"project\", {}).get(\"name\") or poetry_config.get(\n            \"name\"\n        )\n        if project_name is not None and canonicalize_name(project_name) in dependencies:\n            results[\"errors\"].append(\n                f\"Project name ({project_name}) is same as one of its dependencies\"\n            )\n\n        return results\n"
  },
  {
    "path": "src/poetry/inspection/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/inspection/info.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport functools\nimport glob\nimport logging\nimport tempfile\n\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pkginfo\n\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.factory import Factory\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.pyproject.toml import PyProjectTOML\nfrom poetry.core.utils.helpers import parse_requires\nfrom poetry.core.version.markers import InvalidMarkerError\nfrom poetry.core.version.requirements import InvalidRequirementError\n\nfrom poetry.utils.helpers import extractall\nfrom poetry.utils.isolated_build import IsolatedBuildBackendError\nfrom poetry.utils.isolated_build import isolated_builder\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from collections.abc import Sequence\n\n    from packaging.metadata import RawMetadata\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import PackageFile\n    from poetry.core.packages.project_package import ProjectPackage\n\n\nlogger = logging.getLogger(__name__)\n\nDYNAMIC_METADATA_VERSION = Version.parse(\"2.2\")\n\n\nclass PackageInfoError(ValueError):\n    def __init__(self, path: Path, *reasons: BaseException | str) -> None:\n        reasons = (f\"Unable to determine package info for path: {path!s}\", *reasons)\n        super().__init__(\"\\n\\n\".join(str(msg).strip() for msg in reasons if msg))\n\n\nclass PackageInfo:\n    def __init__(\n        self,\n        *,\n        name: str | None = None,\n        version: str | None = None,\n        summary: str | None = None,\n        requires_dist: list[str] | None = None,\n        requires_python: str | None = None,\n        files: Sequence[PackageFile] | None = None,\n        yanked: str | bool = False,\n        cache_version: str | None = None,\n    ) -> None:\n        self.name = name\n        self.version = version\n        self.summary = summary\n        self.requires_dist = requires_dist\n        self.requires_python = requires_python\n        self.files = files or []\n        self.yanked = yanked\n        self._cache_version = cache_version\n        self._source_type: str | None = None\n        self._source_url: str | None = None\n        self._source_reference: str | None = None\n\n    @property\n    def cache_version(self) -> str | None:\n        return self._cache_version\n\n    def update(self, other: PackageInfo) -> PackageInfo:\n        self.name = other.name or self.name\n        self.version = other.version or self.version\n        self.summary = other.summary or self.summary\n        self.requires_dist = other.requires_dist or self.requires_dist\n        self.requires_python = other.requires_python or self.requires_python\n        self.files = other.files or self.files\n        self._cache_version = other.cache_version or self._cache_version\n        return self\n\n    def asdict(self) -> dict[str, Any]:\n        \"\"\"\n        Helper method to convert package info into a dictionary used for caching.\n        \"\"\"\n        return {\n            \"name\": self.name,\n            \"version\": self.version,\n            \"summary\": self.summary,\n            \"requires_dist\": self.requires_dist,\n            \"requires_python\": self.requires_python,\n            \"files\": self.files,\n            \"yanked\": self.yanked,\n            \"_cache_version\": self._cache_version,\n        }\n\n    @classmethod\n    def load(cls, data: dict[str, Any]) -> PackageInfo:\n        \"\"\"\n        Helper method to load data from a dictionary produced by `PackageInfo.asdict()`.\n\n        :param data: Data to load. This is expected to be a `dict` object output by\n            `asdict()`.\n        \"\"\"\n        cache_version = data.pop(\"_cache_version\", None)\n        return cls(cache_version=cache_version, **data)\n\n    def to_package(\n        self, name: str | None = None, root_dir: Path | None = None\n    ) -> Package:\n        \"\"\"\n        Create a new `poetry.core.packages.package.Package` instance using metadata from\n        this instance.\n\n        :param name: Name to use for the package, if not specified name from this\n            instance is used.\n        :param extras: Extras to activate for this package.\n        :param root_dir:  Optional root directory to use for the package. If set,\n            dependency strings will be parsed relative to this directory.\n        \"\"\"\n        name = name or self.name\n\n        if not name:\n            raise RuntimeError(f\"Unable to create package with no name for {root_dir}\")\n\n        if not self.version:\n            # The version could not be determined, so we raise an error since it is\n            # mandatory.\n            raise RuntimeError(f\"Unable to retrieve the package version for {name}\")\n\n        package = Package(\n            name=name,\n            version=self.version,\n            source_type=self._source_type,\n            source_url=self._source_url,\n            source_reference=self._source_reference,\n            yanked=self.yanked,\n        )\n        if self.summary is not None:\n            package.description = self.summary\n        package.root_dir = root_dir\n        package.python_versions = self.requires_python or \"*\"\n        package.files = self.files\n\n        # If this is a local poetry project, we can extract \"richer\" requirement\n        # information, eg: development requirements etc.\n        if root_dir is not None:\n            path = root_dir\n        elif self._source_type == \"directory\" and self._source_url is not None:\n            path = Path(self._source_url)\n        else:\n            path = None\n\n        if path is not None:\n            poetry_package = self._get_poetry_package(path=path)\n            if poetry_package:\n                package.extras = poetry_package.extras\n                for dependency in poetry_package.requires:\n                    package.add_dependency(dependency)\n\n                return package\n\n        seen_requirements = set()\n\n        package_extras: dict[NormalizedName, list[Dependency]] = {}\n        for req in self.requires_dist or []:\n            try:\n                # Attempt to parse the PEP-508 requirement string\n                dependency = Dependency.create_from_pep_508(req, relative_to=root_dir)\n            except InvalidMarkerError:\n                # Invalid marker, We strip the markers hoping for the best\n                logger.warning(\n                    \"Stripping invalid marker (%s) found in %s-%s dependencies\",\n                    req,\n                    package.name,\n                    package.version,\n                )\n                req = req.split(\";\")[0]\n                dependency = Dependency.create_from_pep_508(req, relative_to=root_dir)\n            except InvalidRequirementError:\n                # Unable to parse requirement so we skip it\n                logger.warning(\n                    \"Invalid requirement (%s) found in %s-%s dependencies, skipping\",\n                    req,\n                    package.name,\n                    package.version,\n                )\n                continue\n\n            if dependency.in_extras:\n                # this dependency is required by an extra package\n                for extra in dependency.in_extras:\n                    if extra not in package_extras:\n                        # this is the first time we encounter this extra for this\n                        # package\n                        package_extras[extra] = []\n\n                    package_extras[extra].append(dependency)\n\n            req = dependency.to_pep_508(with_extras=True)\n\n            if req not in seen_requirements:\n                package.add_dependency(dependency)\n                seen_requirements.add(req)\n\n        package.extras = package_extras\n\n        return package\n\n    @classmethod\n    def _requirements_from_distribution(\n        cls,\n        dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel,\n    ) -> list[str] | None:\n        \"\"\"\n        Helper method to extract package requirements from a `pkginfo.Distribution`\n        instance.\n\n        :param dist: The distribution instance to extract requirements from.\n        \"\"\"\n        # If the distribution lists requirements, we use those.\n        #\n        # If the distribution does not list requirements, but the metadata is new enough\n        # to specify that this is because there definitely are none: then we return an\n        # empty list.\n        #\n        # If there is a requires.txt, we use that.\n        if dist.requires_dist:\n            return list(dist.requires_dist)\n\n        if dist.metadata_version is not None:\n            metadata_version = Version.parse(dist.metadata_version)\n            if (\n                metadata_version >= DYNAMIC_METADATA_VERSION\n                and \"Requires-Dist\" not in dist.dynamic\n            ):\n                return []\n\n        requires = Path(dist.filename) / \"requires.txt\"\n        if requires.exists():\n            text = requires.read_text(encoding=\"utf-8\")\n            requirements = parse_requires(text)\n            return requirements\n\n        return None\n\n    @classmethod\n    def _from_distribution(\n        cls, dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel\n    ) -> PackageInfo:\n        \"\"\"\n        Helper method to parse package information from a `pkginfo.Distribution`\n        instance.\n\n        :param dist: The distribution instance to parse information from.\n        \"\"\"\n        # If the METADATA version is greater than the highest supported version,\n        # pkginfo prints a warning and tries to parse the fields from the highest\n        # known version. Assuming that METADATA versions adhere to semver,\n        # this should be safe for minor updates.\n        if not dist.metadata_version or dist.metadata_version.split(\".\")[0] not in {\n            v.split(\".\")[0] for v in pkginfo.distribution.HEADER_ATTRS\n        }:\n            raise ValueError(f\"Unknown metadata version: {dist.metadata_version}\")\n\n        requirements = cls._requirements_from_distribution(dist)\n\n        info = cls(\n            name=dist.name,\n            version=dist.version,\n            summary=dist.summary,\n            requires_dist=requirements,\n            requires_python=dist.requires_python,\n        )\n\n        info._source_type = \"file\"\n        info._source_url = Path(dist.filename).resolve().as_posix()\n\n        return info\n\n    @classmethod\n    def _from_sdist_file(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Helper method to parse package information from an sdist file. We attempt to\n        first inspect the file using `pkginfo.SDist`. If this does not provide us with\n        package requirements, we extract the source and handle it as a directory.\n\n        :param path: The sdist file to parse information from.\n        \"\"\"\n        info = None\n\n        with contextlib.suppress(ValueError):\n            sdist = pkginfo.SDist(str(path))\n            info = cls._from_distribution(sdist)\n\n        if info is not None and info.requires_dist is not None:\n            # we successfully retrieved dependencies from sdist metadata\n            return info\n\n        # Still not dependencies found\n        # So, we unpack and introspect\n        suffix = path.suffix\n        zip = suffix == \".zip\"\n\n        if suffix == \".bz2\":\n            suffixes = path.suffixes\n            if len(suffixes) > 1 and suffixes[-2] == \".tar\":\n                suffix = \".tar.bz2\"\n        elif not zip:\n            suffix = \".tar.gz\"\n\n        with TemporaryDirectory(ignore_cleanup_errors=True) as tmp_str:\n            tmp = Path(tmp_str)\n            extractall(source=path, dest=tmp, zip=zip)\n\n            # a little bit of guess work to determine the directory we care about\n            elements = list(tmp.glob(\"*\"))\n\n            if len(elements) == 1 and elements[0].is_dir():\n                sdist_dir = elements[0]\n            else:\n                sdist_dir = tmp / path.name.rstrip(suffix)\n                if not sdist_dir.is_dir():\n                    sdist_dir = tmp\n\n            # now this is an unpacked directory we know how to deal with\n            new_info = cls.from_directory(path=sdist_dir)\n            new_info._source_type = \"file\"\n            new_info._source_url = path.resolve().as_posix()\n\n        if not info:\n            return new_info\n\n        return info.update(new_info)\n\n    @staticmethod\n    def _find_dist_info(path: Path) -> Iterator[Path]:\n        \"\"\"\n        Discover all `*.*-info` directories in a given path.\n\n        :param path: Path to search.\n        \"\"\"\n        pattern = \"**/*.*-info\"\n        # Sometimes pathlib will fail on recursive symbolic links, so we need to work\n        # around it and use the glob module instead. Note that this does not happen with\n        # pathlib2 so it's safe to use it for Python < 3.4.\n        directories = glob.iglob(path.joinpath(pattern).as_posix(), recursive=True)\n\n        for d in directories:\n            yield Path(d)\n\n    @classmethod\n    def from_metadata(cls, metadata: RawMetadata) -> PackageInfo:\n        \"\"\"\n        Create package information from core metadata.\n\n        :param metadata: raw metadata\n        \"\"\"\n        return cls(\n            name=metadata.get(\"name\"),\n            version=metadata.get(\"version\"),\n            summary=metadata.get(\"summary\"),\n            requires_dist=metadata.get(\"requires_dist\"),\n            requires_python=metadata.get(\"requires_python\"),\n        )\n\n    @classmethod\n    def from_metadata_directory(cls, path: Path) -> PackageInfo | None:\n        \"\"\"\n        Helper method to parse package information from an unpacked metadata directory.\n\n        :param path: The metadata directory to parse information from.\n        \"\"\"\n        if path.suffix in {\".dist-info\", \".egg-info\"}:\n            directories = [path]\n        else:\n            directories = list(cls._find_dist_info(path=path))\n\n        dist: pkginfo.BDist | pkginfo.SDist | pkginfo.Wheel\n        for directory in directories:\n            try:\n                if directory.suffix == \".egg-info\":\n                    dist = pkginfo.UnpackedSDist(directory.as_posix())\n                elif directory.suffix == \".dist-info\":\n                    dist = pkginfo.Wheel(directory.as_posix())\n                else:\n                    continue\n                break\n            except ValueError:\n                continue\n        else:\n            try:\n                # handle PKG-INFO in unpacked sdist root\n                dist = pkginfo.UnpackedSDist(path.as_posix())\n            except ValueError:\n                return None\n\n        return cls._from_distribution(dist=dist)\n\n    @classmethod\n    def from_package(cls, package: Package) -> PackageInfo:\n        \"\"\"\n        Helper method to inspect a `Package` object, in order to generate package info.\n\n        :param package: This must be a poetry package instance.\n        \"\"\"\n        requires = {dependency.to_pep_508() for dependency in package.requires}\n\n        for extra_requires in package.extras.values():\n            for dependency in extra_requires:\n                requires.add(dependency.to_pep_508())\n\n        return cls(\n            name=package.name,\n            version=str(package.version),\n            summary=package.description,\n            requires_dist=list(requires),\n            requires_python=package.python_versions,\n            files=package.files,\n            yanked=package.yanked_reason if package.yanked else False,\n        )\n\n    @staticmethod\n    def _get_poetry_package(path: Path) -> ProjectPackage | None:\n        # Note: we ignore any setup.py file at this step\n        # TODO: add support for handling non-poetry PEP-517 builds\n        if PyProjectTOML(path.joinpath(\"pyproject.toml\")).is_poetry_project():\n            with contextlib.suppress(RuntimeError):\n                return Factory().create_poetry(path).package\n\n        return None\n\n    @classmethod\n    def from_directory(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Generate package information from a package source directory. If introspection\n        of all available metadata fails, the package is attempted to be built in an\n        isolated environment so as to generate required metadata.\n\n        :param path: Path to generate package information from.\n        \"\"\"\n        project_package = cls._get_poetry_package(path)\n        info: PackageInfo | None\n        if project_package:\n            info = cls.from_package(project_package)\n        else:\n            info = cls.from_metadata_directory(path)\n\n            if not info or info.requires_dist is None:\n                try:\n                    info = get_pep517_metadata(path)\n                except PackageInfoError:\n                    if not info:\n                        raise\n\n                    # we discovered PkgInfo but no requirements were listed\n\n        info._source_type = \"directory\"\n        info._source_url = path.as_posix()\n\n        return info\n\n    @classmethod\n    def from_sdist(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Gather package information from an sdist file, packed or unpacked.\n\n        :param path: Path to an sdist file or unpacked directory.\n        \"\"\"\n        if path.is_file():\n            return cls._from_sdist_file(path=path)\n\n        # if we get here then it is neither an sdist instance nor a file\n        # so, we assume this is an directory\n        return cls.from_directory(path=path)\n\n    @classmethod\n    def from_wheel(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Gather package information from a wheel.\n\n        :param path: Path to wheel.\n        \"\"\"\n        try:\n            wheel = pkginfo.Wheel(str(path))\n            return cls._from_distribution(wheel)\n        except ValueError as e:\n            raise PackageInfoError(path, e)\n\n    @classmethod\n    def from_bdist(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Gather package information from a bdist (wheel etc.).\n\n        :param path: Path to bdist.\n        \"\"\"\n        if path.suffix == \".whl\":\n            return cls.from_wheel(path=path)\n\n        try:\n            bdist = pkginfo.BDist(str(path))\n            return cls._from_distribution(bdist)\n        except ValueError as e:\n            raise PackageInfoError(path, e)\n\n    @classmethod\n    def from_path(cls, path: Path) -> PackageInfo:\n        \"\"\"\n        Gather package information from a given path (bdist, sdist, directory).\n\n        :param path: Path to inspect.\n        \"\"\"\n        try:\n            return cls.from_bdist(path=path)\n        except PackageInfoError:\n            return cls.from_sdist(path=path)\n\n\n@functools.cache\ndef get_pep517_metadata(path: Path) -> PackageInfo:\n    \"\"\"\n    Helper method to use PEP-517 library to build and read package metadata.\n\n    :param path: Path to package source to build and read metadata for.\n    \"\"\"\n    info = None\n\n    with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as dist:\n        try:\n            dest = Path(dist)\n\n            with isolated_builder(path, \"wheel\") as builder:\n                builder.metadata_path(dest)\n\n            info = PackageInfo.from_metadata_directory(dest)\n        except IsolatedBuildBackendError as e:\n            raise PackageInfoError(path, str(e)) from None\n\n    if info:\n        return info\n\n    # if we reach here, everything has failed and all hope is lost\n    raise PackageInfoError(path, \"Exhausted all core metadata sources.\")\n"
  },
  {
    "path": "src/poetry/inspection/lazy_wheel.py",
    "content": "\"\"\"Lazy ZIP over HTTP\"\"\"\n\nfrom __future__ import annotations\n\nimport io\nimport logging\nimport re\n\nfrom bisect import bisect_left\nfrom bisect import bisect_right\nfrom contextlib import contextmanager\nfrom tempfile import NamedTemporaryFile\nfrom typing import IO\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom urllib.parse import urlparse\nfrom zipfile import BadZipFile\nfrom zipfile import ZipFile\n\nfrom packaging.metadata import parse_email\nfrom requests.models import CONTENT_CHUNK_SIZE\nfrom requests.models import HTTPError\nfrom requests.models import Response\nfrom requests.status_codes import codes\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n    from collections.abc import Iterator\n    from types import TracebackType\n\n    from packaging.metadata import RawMetadata\n    from requests import Session\n    from typing_extensions import Self\n\n    from poetry.utils.authenticator import Authenticator\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass LazyWheelUnsupportedError(Exception):\n    \"\"\"Raised when a lazy wheel is unsupported.\"\"\"\n\n\nclass HTTPRangeRequestUnsupportedError(LazyWheelUnsupportedError):\n    \"\"\"Raised when the remote server appears unable to support byte ranges.\"\"\"\n\n\nclass HTTPRangeRequestNotRespectedError(LazyWheelUnsupportedError):\n    \"\"\"Raised when the remote server tells us that it supports byte ranges\n    but does not respect a respective request.\"\"\"\n\n\nclass UnsupportedWheelError(LazyWheelUnsupportedError):\n    \"\"\"Unsupported wheel.\"\"\"\n\n\nclass InvalidWheelError(LazyWheelUnsupportedError):\n    \"\"\"Invalid (e.g. corrupt) wheel.\"\"\"\n\n    def __init__(self, location: str, name: str) -> None:\n        self.location = location\n        self.name = name\n\n    def __str__(self) -> str:\n        return f\"Wheel {self.name} located at {self.location} is invalid.\"\n\n\ndef metadata_from_wheel_url(\n    name: str, url: str, session: Session | Authenticator\n) -> RawMetadata:\n    \"\"\"Fetch metadata from the given wheel URL.\n\n    This uses HTTP range requests to only fetch the portion of the wheel\n    containing metadata, just enough for the object to be constructed.\n\n    :raises HTTPRangeRequestUnsupportedError: if range requests are unsupported for ``url``.\n    :raises InvalidWheelError: if the zip file contents could not be parsed.\n    \"\"\"\n    try:\n        # After context manager exit, wheel.name will point to a deleted file path.\n        # Add `delete_backing_file=False` to disable this for debugging.\n        with LazyWheelOverHTTP(url, session) as lazy_file:\n            metadata_bytes = lazy_file.read_metadata(name)\n\n        metadata, _ = parse_email(metadata_bytes)\n        return metadata\n\n    except (BadZipFile, UnsupportedWheelError):\n        # We assume that these errors have occurred because the wheel contents\n        # themselves are invalid, not because we've messed up our bookkeeping\n        # and produced an invalid file.\n        raise InvalidWheelError(url, name)\n    except Exception as e:\n        if isinstance(e, LazyWheelUnsupportedError):\n            # this is expected when the code handles issues with lazy wheel metadata retrieval correctly\n            raise e\n\n        logger.debug(\n            \"There was an unexpected %s when handling lazy wheel metadata retrieval for %s from %s: %s\",\n            type(e).__name__,\n            name,\n            url,\n            e,\n        )\n\n        # Catch all exception to handle any issues that may have occurred during\n        # attempts to use Lazy Wheel.\n        raise LazyWheelUnsupportedError(\n            f\"Attempts to use lazy wheel metadata retrieval for {name} from {url} failed\"\n        ) from e\n\n\nclass MergeIntervals:\n    \"\"\"Stateful bookkeeping to merge interval graphs.\"\"\"\n\n    def __init__(self, *, left: Iterable[int] = (), right: Iterable[int] = ()) -> None:\n        self._left = list(left)\n        self._right = list(right)\n\n    def __repr__(self) -> str:\n        return (\n            f\"{type(self).__name__}\"\n            f\"(left={tuple(self._left)}, right={tuple(self._right)})\"\n        )\n\n    def _merge(\n        self, start: int, end: int, left: int, right: int\n    ) -> Iterator[tuple[int, int]]:\n        \"\"\"Return an iterator of intervals to be fetched.\n\n        Args:\n            start: Start of needed interval\n            end: End of needed interval\n            left: Index of first overlapping downloaded data\n            right: Index after last overlapping downloaded data\n        \"\"\"\n        lslice, rslice = self._left[left:right], self._right[left:right]\n        i = start = min([start, *lslice[:1]])\n        end = max([end, *rslice[-1:]])\n        for j, k in zip(lslice, rslice):\n            if j > i:\n                yield i, j - 1\n            i = k + 1\n        if i <= end:\n            yield i, end\n        self._left[left:right], self._right[left:right] = [start], [end]\n\n    def minimal_intervals_covering(\n        self, start: int, end: int\n    ) -> Iterator[tuple[int, int]]:\n        \"\"\"Provide the intervals needed to cover from ``start <= x <= end``.\n\n        This method mutates internal state so that later calls only return intervals not\n        covered by prior calls. The first call to this method will always return exactly\n        one interval, which was exactly the one requested. Later requests for\n        intervals overlapping that first requested interval will yield only the ranges\n        not previously covered (which may be empty, e.g. if the same interval is\n        requested twice).\n\n        This may be used e.g. to download substrings of remote files on demand.\n        \"\"\"\n        left = bisect_left(self._right, start)\n        right = bisect_right(self._left, end)\n        yield from self._merge(start, end, left, right)\n\n\nclass ReadOnlyIOWrapper(IO[bytes]):\n    \"\"\"Implement read-side ``IO[bytes]`` methods wrapping an inner ``IO[bytes]``.\n\n    This wrapper is useful because Python currently does not distinguish read-only\n    streams at the type level.\n    \"\"\"\n\n    def __init__(self, inner: IO[bytes]) -> None:\n        self._file = inner\n\n    def __enter__(self) -> Self:\n        self._file.__enter__()\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self._file.__exit__(exc_type, exc_value, traceback)\n\n    def __iter__(self) -> Iterator[bytes]:\n        raise NotImplementedError\n\n    def __next__(self) -> bytes:\n        raise NotImplementedError\n\n    @property\n    def mode(self) -> str:\n        \"\"\"Opening mode, which is always rb.\"\"\"\n        return \"rb\"\n\n    @property\n    def name(self) -> str:\n        \"\"\"Path to the underlying file.\"\"\"\n        return self._file.name\n\n    def seekable(self) -> bool:\n        \"\"\"Return whether random access is supported, which is True.\"\"\"\n        return True\n\n    def close(self) -> None:\n        \"\"\"Close the file.\"\"\"\n        self._file.close()\n\n    @property\n    def closed(self) -> bool:\n        \"\"\"Whether the file is closed.\"\"\"\n        return self._file.closed\n\n    def fileno(self) -> int:\n        return self._file.fileno()\n\n    def flush(self) -> None:\n        self._file.flush()\n\n    def isatty(self) -> bool:\n        return False\n\n    def readable(self) -> bool:\n        \"\"\"Return whether the file is readable, which is True.\"\"\"\n        return True\n\n    def read(self, size: int = -1) -> bytes:\n        \"\"\"Read up to size bytes from the object and return them.\n\n        As a convenience, if size is unspecified or -1,\n        all bytes until EOF are returned.  Fewer than\n        size bytes may be returned if EOF is reached.\n        \"\"\"\n        return self._file.read(size)\n\n    def readline(self, limit: int = -1) -> bytes:\n        # Explicit impl needed to satisfy mypy.\n        raise NotImplementedError\n\n    def readlines(self, hint: int = -1) -> list[bytes]:\n        raise NotImplementedError\n\n    def seek(self, offset: int, whence: int = 0) -> int:\n        \"\"\"Change stream position and return the new absolute position.\n\n        Seek to offset relative position indicated by whence:\n        * 0: Start of stream (the default).  pos should be >= 0;\n        * 1: Current position - pos may be negative;\n        * 2: End of stream - pos usually negative.\n        \"\"\"\n        return self._file.seek(offset, whence)\n\n    def tell(self) -> int:\n        \"\"\"Return the current position.\"\"\"\n        return self._file.tell()\n\n    def truncate(self, size: int | None = None) -> int:\n        \"\"\"Resize the stream to the given size in bytes.\n\n        If size is unspecified resize to the current position.\n        The current stream position isn't changed.\n\n        Return the new file size.\n        \"\"\"\n        return self._file.truncate(size)\n\n    def writable(self) -> bool:\n        \"\"\"Return False.\"\"\"\n        return False\n\n    def write(self, s: Any) -> int:\n        raise NotImplementedError\n\n    def writelines(self, lines: Iterable[Any]) -> None:\n        raise NotImplementedError\n\n\nclass LazyFileOverHTTP(ReadOnlyIOWrapper):\n    \"\"\"File-like object representing a fixed-length file over HTTP.\n\n    This uses HTTP range requests to lazily fetch the file's content into a temporary\n    file. If such requests are not supported by the server, raises\n    ``HTTPRangeRequestUnsupportedError`` in the ``__enter__`` method.\"\"\"\n\n    def __init__(\n        self,\n        url: str,\n        session: Session | Authenticator,\n        delete_backing_file: bool = True,\n    ) -> None:\n        inner = NamedTemporaryFile(delete=delete_backing_file)  # noqa: SIM115\n        super().__init__(inner)\n\n        self._merge_intervals: MergeIntervals | None = None\n        self._length: int | None = None\n\n        self._request_count = 0\n        self._session = session\n        self._url = url\n\n    def __enter__(self) -> Self:\n        super().__enter__()\n        self._setup_content()\n        return self\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        traceback: TracebackType | None,\n    ) -> None:\n        self._reset_content()\n        super().__exit__(exc_type, exc_value, traceback)\n\n    def read(self, size: int = -1) -> bytes:\n        \"\"\"Read up to size bytes from the object and return them.\n\n        As a convenience, if size is unspecified or -1,\n        all bytes until EOF are returned.  Fewer than\n        size bytes may be returned if EOF is reached.\n\n        :raises ValueError: if ``__enter__`` was not called beforehand.\n        \"\"\"\n        if self._length is None:\n            raise ValueError(\".__enter__() must be called to set up content length\")\n        cur = self.tell()\n        logger.debug(\"read size %d at %d from lazy file %s\", size, cur, self.name)\n        if size < 0:\n            assert cur <= self._length\n            download_size = self._length - cur\n        elif size == 0:\n            return b\"\"\n        else:\n            download_size = size\n        stop = min(cur + download_size, self._length)\n        self._ensure_downloaded(cur, stop)\n        return super().read(download_size)\n\n    @classmethod\n    def _uncached_headers(cls) -> dict[str, str]:\n        \"\"\"HTTP headers to bypass any HTTP caching.\n\n        The requests we perform in this file are intentionally small, and any caching\n        should be done at a higher level.\n\n        Further, caching partial requests might cause issues:\n        https://github.com/pypa/pip/pull/8716\n        \"\"\"\n        # \"no-cache\" is the correct value for \"up to date every time\", so this will also\n        # ensure we get the most recent value from the server:\n        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#provide_up-to-date_content_every_time\n        return {\"Accept-Encoding\": \"identity\", \"Cache-Control\": \"no-cache\"}\n\n    def _setup_content(self) -> None:\n        \"\"\"Initialize the internal length field and other bookkeeping.\n\n        Ensure ``self._merge_intervals`` is initialized.\n\n        After parsing the remote file length with ``self._fetch_content_length()``,\n        this method will truncate the underlying file from parent abstract class\n        ``ReadOnlyIOWrapper`` to that size in order to support seek operations against\n        ``io.SEEK_END`` in ``self.read()``.\n\n        Called in ``__enter__``, and should make recursive invocations into a no-op.\n        Subclasses may override this method.\"\"\"\n        if self._merge_intervals is None:\n            self._merge_intervals = MergeIntervals()\n\n        if self._length is None:\n            logger.debug(\"begin fetching content length\")\n            self._length = self._fetch_content_length()\n            logger.debug(\"done fetching content length (is: %d)\", self._length)\n            # Enable us to seek and write anywhere in the backing file up to this\n            # known length.\n            self.truncate(self._length)\n        else:\n            logger.debug(\"content length already fetched (is: %d)\", self._length)\n\n    def _reset_content(self) -> None:\n        \"\"\"Unset the internal length field and merge intervals.\n\n        Called in ``__exit__``, and should make recursive invocations into a no-op.\n        Subclasses may override this method.\"\"\"\n        if self._merge_intervals is not None:\n            logger.debug(\n                \"unsetting merge intervals (were: %s)\", repr(self._merge_intervals)\n            )\n            self._merge_intervals = None\n\n        if self._length is not None:\n            logger.debug(\"unsetting content length (was: %d)\", self._length)\n            self._length = None\n\n    def _content_length_from_head(self) -> int:\n        \"\"\"Performs a HEAD request to extract the Content-Length.\n\n        :raises HTTPRangeRequestUnsupportedError: if the response fails to indicate support\n                                             for \"bytes\" ranges.\"\"\"\n        self._request_count += 1\n        head = self._session.head(\n            self._url, headers=self._uncached_headers(), allow_redirects=True\n        )\n        head.raise_for_status()\n        assert head.status_code == codes.ok\n        accepted_range = head.headers.get(\"Accept-Ranges\", None)\n        if accepted_range != \"bytes\":\n            raise HTTPRangeRequestUnsupportedError(\n                f\"server does not support byte ranges: header was '{accepted_range}'\"\n            )\n        return int(head.headers[\"Content-Length\"])\n\n    def _fetch_content_length(self) -> int:\n        \"\"\"Get the remote file's length.\"\"\"\n        # NB: This is currently dead code, as _fetch_content_length() is overridden\n        #     again in LazyWheelOverHTTP.\n        return self._content_length_from_head()\n\n    def _stream_response(self, start: int, end: int) -> Response:\n        \"\"\"Return streaming HTTP response to a range request from start to end.\"\"\"\n        headers = self._uncached_headers()\n        headers[\"Range\"] = f\"bytes={start}-{end}\"\n        logger.debug(\"streamed bytes request: %s\", headers[\"Range\"])\n        self._request_count += 1\n\n        response = self._session.get(self._url, headers=headers, stream=True)\n        try:\n            response.raise_for_status()\n            if int(response.headers[\"Content-Length\"]) != (end - start + 1):\n                raise HTTPRangeRequestNotRespectedError(\n                    f\"server did not respect byte range request: \"\n                    f\"requested {end - start + 1} bytes, got \"\n                    f\"{response.headers['Content-Length']} bytes\"\n                )\n            return response\n        except BaseException:\n            response.close()\n            raise\n\n    def _fetch_content_range(self, start: int, end: int) -> Iterator[bytes]:\n        \"\"\"Perform a series of HTTP range requests to cover the specified byte range.\n\n        NB: For compatibility with HTTP range requests, the range provided to this\n        method must *include* the byte indexed at argument ``end`` (so e.g. ``0-1`` is 2\n        bytes long, and the range can never be empty).\n        \"\"\"\n        with self._stream_response(start, end) as response:\n            yield from response.iter_content(CONTENT_CHUNK_SIZE)\n\n    @contextmanager\n    def _stay(self) -> Iterator[None]:\n        \"\"\"Return a context manager keeping the position.\n\n        At the end of the block, seek back to original position.\n        \"\"\"\n        pos = self.tell()\n        try:\n            yield\n        finally:\n            self.seek(pos)\n\n    def _ensure_downloaded(self, start: int, end: int) -> None:\n        \"\"\"Ensures bytes start to end (inclusive) have been downloaded and written to\n        the backing file.\n\n        :raises ValueError: if ``__enter__`` was not called beforehand.\n        \"\"\"\n        if self._merge_intervals is None:\n            raise ValueError(\".__enter__() must be called to set up merge intervals\")\n        # Reducing by 1 to get an inclusive end range.\n        end -= 1\n        with self._stay():\n            for (\n                range_start,\n                range_end,\n            ) in self._merge_intervals.minimal_intervals_covering(start, end):\n                self.seek(start)\n                for chunk in self._fetch_content_range(range_start, range_end):\n                    self._file.write(chunk)\n\n\nclass LazyWheelOverHTTP(LazyFileOverHTTP):\n    \"\"\"File-like object mapped to a ZIP file over HTTP.\n\n    This uses HTTP range requests to lazily fetch the file's content, which should be\n    provided as the first argument to a ``ZipFile``.\n    \"\"\"\n\n    # Cache this on the type to avoid trying and failing our initial lazy wheel request\n    # multiple times in the same invocation against an index without this support.\n    _domains_without_negative_range: ClassVar[set[str]] = set()\n\n    _metadata_regex = re.compile(r\"^[^/]*\\.dist-info/METADATA$\")\n\n    def read_metadata(self, name: str) -> bytes:\n        \"\"\"Download and read the METADATA file from the remote wheel.\"\"\"\n        with ZipFile(self) as zf:\n            # prefetch metadata to reduce the number of range requests\n            filename = self._prefetch_metadata(name)\n            return zf.read(filename)\n\n    @classmethod\n    def _initial_chunk_length(cls) -> int:\n        \"\"\"Return the size of the chunk (in bytes) to download from the end of the file.\n\n        This method is called in ``self._fetch_content_length()``. As noted in that\n        method's docstring, this should be set high enough to cover the central\n        directory sizes of the *average* wheels you expect to see, in order to avoid\n        further requests before being able to process the zip file's contents at all.\n        If we choose a small number, we need one more range request for larger wheels.\n        If we choose a big number, we download unnecessary data from smaller wheels.\n        If the chunk size from this method is larger than the size of an entire wheel,\n        that may raise an HTTP error, but this is gracefully handled in\n        ``self._fetch_content_length()`` with a small performance penalty.\n        \"\"\"\n        return 10_000\n\n    def _fetch_content_length(self) -> int:\n        \"\"\"Get the total remote file length, but also download a chunk from the end.\n\n        This method is called within ``__enter__``. In an attempt to reduce\n        the total number of requests needed to populate this lazy file's contents, this\n        method will also attempt to fetch a chunk of the file's actual content. This\n        chunk will be ``self._initial_chunk_length()`` bytes in size, or just the remote\n        file's length if that's smaller, and the chunk will come from the *end* of\n        the file.\n\n        This method will first attempt to download with a negative byte range request,\n        i.e. a GET with the headers ``Range: bytes=-N`` for ``N`` equal to\n        ``self._initial_chunk_length()``. If negative offsets are unsupported, it will\n        instead fall back to making a HEAD request first to extract the length, followed\n        by a GET request with the double-ended range header ``Range: bytes=X-Y`` to\n        extract the final ``N`` bytes from the remote resource.\n        \"\"\"\n        initial_chunk_size = self._initial_chunk_length()\n        ret_length, tail = self._extract_content_length(initial_chunk_size)\n\n        # Need to explicitly truncate here in order to perform the write and seek\n        # operations below when we write the chunk of file contents to disk.\n        self.truncate(ret_length)\n\n        if tail is None:\n            # If we could not download any file contents yet (e.g. if negative byte\n            # ranges were not supported, or the requested range was larger than the file\n            # size), then download all of this at once, hopefully pulling in the entire\n            # central directory.\n            initial_start = max(0, ret_length - initial_chunk_size)\n            self._ensure_downloaded(initial_start, ret_length)\n        else:\n            # If we *could* download some file contents, then write them to the end of\n            # the file and set up our bisect boundaries by hand.\n            with self._stay(), tail:\n                response_length = int(tail.headers[\"Content-Length\"])\n                assert response_length == min(initial_chunk_size, ret_length)\n                self.seek(-response_length, io.SEEK_END)\n                # Default initial chunk size is currently 1MB, but streaming content\n                # here allows it to be set arbitrarily large.\n                for chunk in tail.iter_content(CONTENT_CHUNK_SIZE):\n                    self._file.write(chunk)\n\n                # We now need to update our bookkeeping to cover the interval we just\n                # wrote to file so we know not to do it in later read()s.\n                init_chunk_start = ret_length - response_length\n                # MergeIntervals uses inclusive boundaries i.e. start <= x <= end.\n                init_chunk_end = ret_length - 1\n                assert self._merge_intervals is not None\n                assert ((init_chunk_start, init_chunk_end),) == tuple(\n                    # NB: We expect LazyRemoteResource to reset `self._merge_intervals`\n                    # just before it calls the current method, so our assertion here\n                    # checks that indeed no prior overlapping intervals have\n                    # been covered.\n                    self._merge_intervals.minimal_intervals_covering(\n                        init_chunk_start, init_chunk_end\n                    )\n                )\n        return ret_length\n\n    @staticmethod\n    def _parse_full_length_from_content_range(arg: str) -> int:\n        \"\"\"Parse the file's full underlying length from the Content-Range header.\n\n        This supports both * and numeric ranges, from success or error responses:\n        https://www.rfc-editor.org/rfc/rfc9110#field.content-range.\n        \"\"\"\n        m = re.match(r\"bytes [^/]+/([0-9]+)\", arg)\n        if m is None:\n            raise HTTPRangeRequestUnsupportedError(\n                f\"could not parse Content-Range: '{arg}'\"\n            )\n        return int(m.group(1))\n\n    def _try_initial_chunk_request(\n        self, initial_chunk_size: int\n    ) -> tuple[int, Response]:\n        \"\"\"Attempt to fetch a chunk from the end of the file with a negative offset.\"\"\"\n        headers = self._uncached_headers()\n        # Perform a negative range index, which is not supported by some servers.\n        headers[\"Range\"] = f\"bytes=-{initial_chunk_size}\"\n        logger.debug(\"initial bytes request: %s\", headers[\"Range\"])\n\n        self._request_count += 1\n        tail = self._session.get(self._url, headers=headers, stream=True)\n        try:\n            tail.raise_for_status()\n\n            code = tail.status_code\n            if code != codes.partial_content:\n                # According to\n                # https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests,\n                # a 200 OK implies that range requests are not supported,\n                # regardless of the requested size.\n                # However, some servers that support negative range requests also return a\n                # 200 OK if the requested range from the end was larger than the file size.\n                if code == codes.ok:\n                    accept_ranges = tail.headers.get(\"Accept-Ranges\", None)\n                    content_length = int(tail.headers[\"Content-Length\"])\n                    if (\n                        accept_ranges == \"bytes\"\n                        and content_length <= initial_chunk_size\n                    ):\n                        return content_length, tail\n\n                raise HTTPRangeRequestUnsupportedError(\n                    f\"did not receive partial content: got code {code}\"\n                )\n\n            if \"Content-Range\" not in tail.headers:\n                raise LazyWheelUnsupportedError(\n                    f\"file length cannot be determined for {self._url}, \"\n                    f\"did not receive content range header from server\"\n                )\n\n            file_length = self._parse_full_length_from_content_range(\n                tail.headers[\"Content-Range\"]\n            )\n            return (file_length, tail)\n        except BaseException:\n            tail.close()\n            raise\n\n    def _extract_content_length(\n        self, initial_chunk_size: int\n    ) -> tuple[int, Response | None]:\n        \"\"\"Get the Content-Length of the remote file, and possibly a chunk of it.\"\"\"\n        domain = urlparse(self._url).netloc\n        if domain in self._domains_without_negative_range:\n            return (self._content_length_from_head(), None)\n\n        tail: Response | None\n        try:\n            # Initial range request for just the end of the file.\n            file_length, tail = self._try_initial_chunk_request(initial_chunk_size)\n        except HTTPError as e:\n            # Our initial request using a negative byte range was not supported.\n            resp = e.response\n            code = resp.status_code if resp is not None else None\n\n            # This indicates that the requested range from the end was larger than the\n            # actual file size: https://www.rfc-editor.org/rfc/rfc9110#status.416.\n            if (\n                code == codes.requested_range_not_satisfiable\n                and resp is not None\n                and \"Content-Range\" in resp.headers\n            ):\n                # In this case, we don't have any file content yet, but we do know the\n                # size the file will be, so we can return that and exit here.\n                file_length = self._parse_full_length_from_content_range(\n                    resp.headers[\"Content-Range\"]\n                )\n                return file_length, None\n\n            # pypi notably does not support negative byte ranges: see\n            # https://github.com/pypi/warehouse/issues/12823.\n            logger.debug(\n                \"Negative byte range not supported for domain '%s': \"\n                \"using HEAD request before lazy wheel from now on (code: %s)\",\n                domain,\n                code,\n            )\n            # Avoid trying a negative byte range request against this domain for the\n            # rest of the resolve.\n            self._domains_without_negative_range.add(domain)\n            # Apply a HEAD request to get the real size, and nothing else for now.\n            return self._content_length_from_head(), None\n\n        # Some servers that do not support negative offsets,\n        # handle a negative offset like \"-10\" as \"0-10\"...\n        # ... or behave even more strangely, see\n        # https://github.com/python-poetry/poetry/issues/9056#issuecomment-1973273721\n        if int(tail.headers[\"Content-Length\"]) > initial_chunk_size or tail.headers.get(\n            \"Content-Range\", \"\"\n        ).startswith(\"bytes -\"):\n            tail.close()\n            tail = None\n            self._domains_without_negative_range.add(domain)\n        return file_length, tail\n\n    def _prefetch_metadata(self, name: str) -> str:\n        \"\"\"Locate the *.dist-info/METADATA entry from a temporary ``ZipFile`` wrapper,\n        and download it.\n\n        This method assumes that the *.dist-info directory (containing e.g. METADATA) is\n        contained in a single contiguous section of the zip file in order to ensure it\n        can be downloaded in a single ranged GET request.\"\"\"\n        logger.debug(\"begin prefetching METADATA for %s\", name)\n\n        start: int | None = None\n        end: int | None = None\n\n        # This may perform further requests if __init__() did not pull in the entire\n        # central directory at the end of the file (although _initial_chunk_length()\n        # should be set large enough to avoid this).\n        zf = ZipFile(self)\n\n        filename = \"\"\n        for info in zf.infolist():\n            if start is None:\n                if self._metadata_regex.search(info.filename):\n                    filename = info.filename\n                    start = info.header_offset\n                    continue\n            else:\n                # The last .dist-info/ entry may be before the end of the file if the\n                # wheel's entries are sorted lexicographically (which is unusual).\n                if not self._metadata_regex.search(info.filename):\n                    end = info.header_offset\n                    break\n        if start is None:\n            raise UnsupportedWheelError(\n                f\"no {self._metadata_regex!r} found for {name} in {self.name}\"\n            )\n        # If it is the last entry of the zip, then give us everything\n        # until the start of the central directory.\n        if end is None:\n            end = zf.start_dir\n        logger.debug(f\"fetch {filename}\")\n        self._ensure_downloaded(start, end)\n        logger.debug(\"done prefetching METADATA for %s\", name)\n\n        return filename\n"
  },
  {
    "path": "src/poetry/installation/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.installation.installer import Installer\n\n\n__all__ = [\"Installer\"]\n"
  },
  {
    "path": "src/poetry/installation/chef.py",
    "content": "from __future__ import annotations\n\nimport tempfile\n\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import TYPE_CHECKING\n\nfrom poetry.utils.helpers import extractall\nfrom poetry.utils.isolated_build import isolated_builder\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n    from collections.abc import Sequence\n\n    from build import DistributionType\n    from poetry.core.packages.dependency import Dependency\n\n    from poetry.repositories import RepositoryPool\n    from poetry.utils.cache import ArtifactCache\n    from poetry.utils.env import Env\n\n\nclass ChefError(Exception): ...\n\n\nclass Chef:\n    def __init__(\n        self, artifact_cache: ArtifactCache, env: Env, pool: RepositoryPool\n    ) -> None:\n        self._env = env\n        self._pool = pool\n        self._artifact_cache = artifact_cache\n\n    def prepare(\n        self,\n        archive: Path,\n        output_dir: Path | None = None,\n        *,\n        editable: bool = False,\n        config_settings: Mapping[str, str | Sequence[str]] | None = None,\n        build_constraints: list[Dependency] | None = None,\n    ) -> Path:\n        if not self._should_prepare(archive):\n            return archive\n\n        if archive.is_dir():\n            destination = output_dir or Path(tempfile.mkdtemp(prefix=\"poetry-chef-\"))\n            return self._prepare(\n                archive,\n                destination=destination,\n                editable=editable,\n                config_settings=config_settings,\n                build_constraints=build_constraints,\n            )\n\n        return self._prepare_sdist(\n            archive,\n            destination=output_dir,\n            config_settings=config_settings,\n            build_constraints=build_constraints,\n        )\n\n    def _prepare(\n        self,\n        directory: Path,\n        destination: Path,\n        *,\n        editable: bool = False,\n        config_settings: Mapping[str, str | Sequence[str]] | None = None,\n        build_constraints: list[Dependency] | None = None,\n    ) -> Path:\n        distribution: DistributionType = \"editable\" if editable else \"wheel\"\n        with isolated_builder(\n            source=directory,\n            distribution=distribution,\n            python_executable=self._env.python,\n            pool=self._pool,\n            build_constraints=build_constraints,\n        ) as builder:\n            return Path(\n                builder.build(\n                    distribution,\n                    destination.as_posix(),\n                    config_settings=config_settings,\n                )\n            )\n\n    def _prepare_sdist(\n        self,\n        archive: Path,\n        destination: Path | None = None,\n        config_settings: Mapping[str, str | Sequence[str]] | None = None,\n        build_constraints: list[Dependency] | None = None,\n    ) -> Path:\n        from poetry.core.packages.utils.link import Link\n\n        suffix = archive.suffix\n        zip = suffix == \".zip\"\n\n        with TemporaryDirectory(ignore_cleanup_errors=True) as tmp_dir:\n            archive_dir = Path(tmp_dir)\n            extractall(source=archive, dest=archive_dir, zip=zip)\n\n            elements = list(archive_dir.glob(\"*\"))\n\n            if len(elements) == 1 and elements[0].is_dir():\n                sdist_dir = elements[0]\n            else:\n                sdist_dir = archive_dir / archive.name.rstrip(suffix)\n                if not sdist_dir.is_dir():\n                    sdist_dir = archive_dir\n\n            if destination is None:\n                destination = self._artifact_cache.get_cache_directory_for_link(\n                    Link(archive.as_uri())\n                )\n\n            destination.mkdir(parents=True, exist_ok=True)\n\n            return self._prepare(\n                sdist_dir,\n                destination,\n                config_settings=config_settings,\n                build_constraints=build_constraints,\n            )\n\n    def _should_prepare(self, archive: Path) -> bool:\n        return archive.is_dir() or not self._is_wheel(archive)\n\n    @classmethod\n    def _is_wheel(cls, archive: Path) -> bool:\n        return archive.suffix == \".whl\"\n"
  },
  {
    "path": "src/poetry/installation/chooser.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport re\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.config.config import Config\nfrom poetry.config.config import PackageFilterPolicy\nfrom poetry.console.exceptions import ConsoleMessage\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.repositories.http_repository import HTTPRepository\nfrom poetry.utils.helpers import get_highest_priority_hash_type\nfrom poetry.utils.wheel import Wheel\n\n\nif TYPE_CHECKING:\n    from poetry.core.constraints.version import Version\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.utils.link import Link\n\n    from poetry.repositories.repository_pool import RepositoryPool\n    from poetry.utils.env import Env\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass Chooser:\n    \"\"\"\n    A Chooser chooses an appropriate release archive for packages.\n    \"\"\"\n\n    def __init__(\n        self, pool: RepositoryPool, env: Env, config: Config | None = None\n    ) -> None:\n        self._pool = pool\n        self._env = env\n        self._config = config or Config.create()\n        self._no_binary_policy: PackageFilterPolicy = PackageFilterPolicy(\n            self._config.get(\"installer.no-binary\", [])\n        )\n        self._only_binary_policy: PackageFilterPolicy = PackageFilterPolicy(\n            self._config.get(\"installer.only-binary\", [])\n        )\n\n    def choose_for(self, package: Package) -> Link:\n        \"\"\"\n        Return the url of the selected archive for a given package.\n        \"\"\"\n        links = []\n\n        # these are used only for providing insightful errors to the user\n        unsupported_wheels = set()\n        links_seen = 0\n        wheels_skipped = 0\n        sdists_skipped = 0\n\n        for link in self._get_links(package):\n            links_seen += 1\n\n            if link.is_wheel:\n                if (\n                    # exact package name must reject wheel, even if `only-binary` includes it\n                    self._no_binary_policy.has_exact_package(package.name)\n                    # `:all:` reject wheel only if `only-binary` does not include it\n                    or (\n                        not self._no_binary_policy.allows(package.name)\n                        and not self._only_binary_policy.has_exact_package(package.name)\n                    )\n                ):\n                    logger.debug(\n                        \"Skipping wheel for %s as requested in no binary policy for\"\n                        \" package (%s)\",\n                        link.filename,\n                        package.name,\n                    )\n                    wheels_skipped += 1\n                    continue\n\n                if not Wheel(link.filename).is_supported_by_environment(self._env):\n                    logger.debug(\n                        \"Skipping wheel %s as this is not supported by the current\"\n                        \" environment\",\n                        link.filename,\n                    )\n                    unsupported_wheels.add(link.filename)\n                    continue\n\n            if link.ext in {\".egg\", \".exe\", \".msi\", \".rpm\", \".srpm\"}:\n                logger.debug(\"Skipping unsupported distribution %s\", link.filename)\n                continue\n\n            if link.is_sdist and (\n                # exact package name must reject sdist, even if `no-binary` includes it\n                self._only_binary_policy.has_exact_package(package.name)\n                # `:all:` reject sdist only if `no-binary` does not include it\n                or (\n                    not self._only_binary_policy.allows(package.name)\n                    and not self._no_binary_policy.has_exact_package(package.name)\n                )\n            ):\n                logger.debug(\n                    \"Skipping source distribution for %s as requested in only binary policy for\"\n                    \" package (%s)\",\n                    link.filename,\n                    package.name,\n                )\n                sdists_skipped += 1\n                continue\n\n            links.append(link)\n\n        if not links:\n            raise self._no_links_found_error(\n                package, links_seen, wheels_skipped, sdists_skipped, unsupported_wheels\n            )\n\n        # Get the best link\n        chosen = max(links, key=lambda link: self._sort_key(package, link))\n\n        return chosen\n\n    def _no_links_found_error(\n        self,\n        package: Package,\n        links_seen: int,\n        wheels_skipped: int,\n        sdists_skipped: int,\n        unsupported_wheels: set[str],\n    ) -> PoetryRuntimeError:\n        messages = []\n        info = (\n            f\"This is likely not a Poetry issue.\\n\\n\"\n            f\"  - {links_seen} candidate(s) were identified for the package\\n\"\n        )\n\n        if wheels_skipped > 0:\n            info += f\"  - {wheels_skipped} wheel(s) were skipped due to your <c1>installer.no-binary</> policy\\n\"\n\n        if sdists_skipped > 0:\n            info += f\"  - {sdists_skipped} source distribution(s) were skipped due to your <c1>installer.only-binary</> policy\\n\"\n\n        if unsupported_wheels:\n            info += (\n                f\"  - {len(unsupported_wheels)} wheel(s) were skipped as your project's environment does not support \"\n                f\"the identified abi tags\\n\"\n            )\n\n        messages.append(ConsoleMessage(info.strip()))\n\n        if unsupported_wheels:\n            messages += [\n                ConsoleMessage(\n                    \"The following wheel(s) were skipped as the current project environment does not support them \"\n                    \"due to abi compatibility issues.\",\n                    debug=True,\n                ),\n                ConsoleMessage(\"\\n\".join(unsupported_wheels), debug=True)\n                .indent(\"  - \")\n                .wrap(\"warning\"),\n                ConsoleMessage(\n                    \"If you would like to see the supported tags in your project environment, you can execute \"\n                    \"the following command:\\n\\n\"\n                    \"    <c1>poetry debug tags</>\",\n                    debug=True,\n                ),\n            ]\n\n        source_hint = \"\"\n        if package.source_type and package.source_reference:\n            source_hint += f\" ({package.source_reference})\"\n\n        messages.append(\n            ConsoleMessage(\n                f\"Make sure the lockfile is up-to-date. You can try one of the following;\\n\\n\"\n                f\"    1. <b>Regenerate lockfile: </><fg=yellow>poetry lock --no-cache --regenerate</>\\n\"\n                f\"    2. <b>Update package     : </><fg=yellow>poetry update --no-cache {package.name}</>\\n\\n\"\n                # FIXME: In the future, it would be better to suggest a more targeted\n                # cache clear command for just the package in question. E.g.\n                # `poetry cache clear {package.source_reference}:{package.name}:{package.version}`\n                # but `package.source_reference` currently resolves to `None` because\n                # repository names are case sensitive at the moment (`PyPI` vs `pypi`).\n                f\"If any of those solutions worked, you will have to clear your caches using (<c1>poetry cache clear --all</>).\\n\\n\"\n                f\"If neither works, please first check to verify that the {package.name} has published wheels \"\n                f\"available from your configured source{source_hint} that are compatible with your environment\"\n                f\"- ie. operating system, architecture (x86_64, arm64 etc.), python interpreter.\"\n            )\n            .make_section(\"Solutions\")\n            .wrap(\"info\")\n        )\n\n        return PoetryRuntimeError(\n            reason=f\"Unable to find installation candidates for {package}\",\n            messages=messages,\n        )\n\n    def _get_links(self, package: Package) -> list[Link]:\n        if package.source_type:\n            assert package.source_reference is not None\n            repository = self._pool.repository(package.source_reference)\n\n        elif not self._pool.has_repository(\"pypi\"):\n            repository = self._pool.repositories[0]\n        else:\n            repository = self._pool.repository(\"pypi\")\n        links = repository.find_links_for_package(package)\n\n        locked_hashes = {f[\"hash\"] for f in package.files}\n        if not locked_hashes:\n            return links\n\n        selected_links = []\n        skipped = []\n        locked_hash_names = {h.split(\":\")[0] for h in locked_hashes}\n        for link in links:\n            if not link.hashes:\n                selected_links.append(link)\n                continue\n\n            link_hash: str | None = None\n            if (candidates := locked_hash_names.intersection(link.hashes.keys())) and (\n                hash_name := get_highest_priority_hash_type(candidates, link.filename)\n            ):\n                link_hash = f\"{hash_name}:{link.hashes[hash_name]}\"\n\n            elif isinstance(repository, HTTPRepository):\n                link_hash = repository.calculate_sha256(link)\n\n            if link_hash not in locked_hashes:\n                skipped.append((link.filename, link_hash))\n                logger.debug(\n                    \"Skipping %s as %s checksum does not match expected value\",\n                    link.filename,\n                    link_hash,\n                )\n                continue\n\n            selected_links.append(link)\n\n        if links and not selected_links:\n            reason = f\"Downloaded distributions for <b>{package.pretty_name} ({package.pretty_version})</> did not match any known checksums in your lock file.\"\n            link_hashes = \"\\n\".join(f\"  - {link}({h})\" for link, h in skipped)\n            known_hashes = \"\\n\".join(f\"  - {h}\" for h in locked_hashes)\n            messages = [\n                ConsoleMessage(\n                    \"<options=bold>Causes:</>\\n\"\n                    \"  - invalid or corrupt cache either during locking or installation\\n\"\n                    \"  - network interruptions or errors causing corrupted downloads\\n\\n\"\n                    \"<b>Solutions:</>\\n\"\n                    \"  1. Try running your command again using the <c1>--no-cache</> global option enabled.\\n\"\n                    \"  2. Try regenerating your lock file using (<c1>poetry lock --no-cache --regenerate</>).\\n\\n\"\n                    \"If any of those solutions worked, you will have to clear your caches using (<c1>poetry cache clear --all CACHE_NAME</>).\"\n                ),\n                ConsoleMessage(\n                    f\"Poetry retrieved the following links:\\n\"\n                    f\"{link_hashes}\\n\\n\"\n                    f\"The lockfile contained only the following hashes:\\n\"\n                    f\"{known_hashes}\",\n                    debug=True,\n                ),\n            ]\n            raise PoetryRuntimeError(reason, messages)\n\n        return selected_links\n\n    def _sort_key(\n        self, package: Package, link: Link\n    ) -> tuple[int, int, int, Version, tuple[Any, ...], int]:\n        \"\"\"\n        Function to pass as the `key` argument to a call to sorted() to sort\n        InstallationCandidates by preference.\n        Returns a tuple such that tuples sorting as greater using Python's\n        default comparison operator are more preferred.\n        The preference is as follows:\n        First and foremost, candidates with allowed (matching) hashes are\n        always preferred over candidates without matching hashes. This is\n        because e.g. if the only candidate with an allowed hash is yanked,\n        we still want to use that candidate.\n        Second, excepting hash considerations, candidates that have been\n        yanked (in the sense of PEP 592) are always less preferred than\n        candidates that haven't been yanked. Then:\n        If not finding wheels, they are sorted by version only.\n        If finding wheels, then the sort order is by version, then:\n          1. existing installs\n          2. wheels ordered via Wheel.support_index_min(self._supported_tags)\n          3. source archives\n        If prefer_binary was set, then all wheels are sorted above sources.\n        Note: it was considered to embed this logic into the Link\n              comparison operators, but then different sdist links\n              with the same version, would have to be considered equal\n        \"\"\"\n        build_tag: tuple[Any, ...] = ()\n        binary_preference = 0\n        if link.is_wheel:\n            wheel = Wheel(link.filename)\n            if not wheel.is_supported_by_environment(self._env):\n                raise RuntimeError(\n                    f\"{wheel.filename} is not a supported wheel for this platform. It \"\n                    \"can't be sorted.\"\n                )\n\n            # TODO: Binary preference\n            pri = -(wheel.get_minimum_supported_index(self._env.supported_tags) or 0)\n            if wheel.build_tag is not None:\n                match = re.match(r\"^(\\d+)(.*)$\", wheel.build_tag)\n                if not match:\n                    raise ValueError(f\"Unable to parse build tag: {wheel.build_tag}\")\n                build_tag_groups = match.groups()\n                build_tag = (int(build_tag_groups[0]), build_tag_groups[1])\n        else:  # sdist\n            support_num = len(self._env.supported_tags)\n            pri = -support_num\n\n        has_allowed_hash = int(self._is_link_hash_allowed_for_package(link, package))\n\n        yank_value = int(not link.yanked)\n\n        return (\n            has_allowed_hash,\n            yank_value,\n            binary_preference,\n            package.version,\n            build_tag,\n            pri,\n        )\n\n    def _is_link_hash_allowed_for_package(self, link: Link, package: Package) -> bool:\n        if not link.hashes:\n            return True\n\n        link_hashes = {f\"{name}:{h}\" for name, h in link.hashes.items()}\n        locked_hashes = {f[\"hash\"] for f in package.files}\n\n        return bool(link_hashes & locked_hashes)\n"
  },
  {
    "path": "src/poetry/installation/executor.py",
    "content": "from __future__ import annotations\n\nimport csv\nimport functools\nimport itertools\nimport json\nimport threading\n\nfrom collections import defaultdict\nfrom concurrent.futures import ThreadPoolExecutor\nfrom concurrent.futures import wait\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.installation.chef import Chef\nfrom poetry.installation.chooser import Chooser\nfrom poetry.installation.operations import Install\nfrom poetry.installation.operations import Uninstall\nfrom poetry.installation.operations import Update\nfrom poetry.installation.wheel_installer import WheelInstaller\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.utils._compat import decode\nfrom poetry.utils.authenticator import Authenticator\nfrom poetry.utils.env import EnvCommandError\nfrom poetry.utils.helpers import Downloader\nfrom poetry.utils.helpers import get_file_hash\nfrom poetry.utils.helpers import get_highest_priority_hash_type\nfrom poetry.utils.helpers import pluralize\nfrom poetry.utils.helpers import remove_directory\nfrom poetry.utils.isolated_build import IsolatedBuildBackendError\nfrom poetry.utils.isolated_build import IsolatedBuildInstallError\nfrom poetry.utils.log_utils import format_build_wheel_log\nfrom poetry.vcs.git import Git\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n    from collections.abc import Sequence\n\n    from cleo.io.io import IO\n    from cleo.io.outputs.section_output import SectionOutput\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n    from poetry.config.config import Config\n    from poetry.installation.operations.operation import Operation\n    from poetry.repositories import RepositoryPool\n    from poetry.utils.env import Env\n\n\ndef _package_get_name(package: Package) -> str | None:\n    if url := package.repository_url:\n        return Git.get_name_from_source_url(url)\n    return None\n\n\nclass Executor:\n    def __init__(\n        self,\n        env: Env,\n        pool: RepositoryPool,\n        config: Config,\n        io: IO,\n        parallel: bool | None = None,\n        disable_cache: bool = False,\n        *,\n        build_constraints: Mapping[NormalizedName, list[Dependency]] | None = None,\n    ) -> None:\n        self._env = env\n        self._io = io\n        self._dry_run = False\n        self._enabled = True\n        self._verbose = False\n        self._wheel_installer = WheelInstaller(self._env)\n        self._build_constraints = build_constraints or {}\n\n        if parallel is None:\n            parallel = config.get(\"installer.parallel\", True)\n\n        if parallel:\n            self._max_workers = config.installer_max_workers\n        else:\n            self._max_workers = 1\n\n        self._artifact_cache = pool.artifact_cache\n        self._authenticator = Authenticator(\n            config, self._io, disable_cache=disable_cache, pool_size=self._max_workers\n        )\n        self._chef = Chef(self._artifact_cache, self._env, pool)\n        self._chooser = Chooser(pool, self._env, config)\n\n        self._executor = ThreadPoolExecutor(max_workers=self._max_workers)\n        self._executed = {\"install\": 0, \"update\": 0, \"uninstall\": 0}\n        self._skipped = {\"install\": 0, \"update\": 0, \"uninstall\": 0}\n        self._sections: dict[int, SectionOutput] = {}\n        self._yanked_warnings: list[str] = []\n        self._lock = threading.Lock()\n        self._shutdown = False\n        self._hashes: dict[str, str] = {}\n\n        # Cache whether decorated output is supported.\n        # https://github.com/python-poetry/cleo/issues/423\n        self._decorated_output: bool = self._io.output.is_decorated()\n        self._max_retries = config.get(\"requests.max-retries\", 0)\n\n        # sdist build config settings\n        self._build_config_settings: Mapping[\n            NormalizedName, Mapping[str, str | Sequence[str]]\n        ] = config.get(\"installer.build-config-settings\")\n\n    @property\n    def installations_count(self) -> int:\n        return self._executed[\"install\"]\n\n    @property\n    def updates_count(self) -> int:\n        return self._executed[\"update\"]\n\n    @property\n    def removals_count(self) -> int:\n        return self._executed[\"uninstall\"]\n\n    @property\n    def enabled(self) -> bool:\n        return self._enabled\n\n    def supports_fancy_output(self) -> bool:\n        return self._decorated_output and not self._dry_run\n\n    def disable(self) -> Executor:\n        self._enabled = False\n\n        return self\n\n    def dry_run(self, dry_run: bool = True) -> Executor:\n        self._dry_run = dry_run\n\n        return self\n\n    def verbose(self, verbose: bool = True) -> Executor:\n        self._verbose = verbose\n\n        return self\n\n    def enable_bytecode_compilation(self, enable: bool = True) -> None:\n        self._wheel_installer.enable_bytecode_compilation(enable)\n\n    def execute(self, operations: list[Operation]) -> int:\n        for job_type in self._executed:\n            self._executed[job_type] = 0\n            self._skipped[job_type] = 0\n\n        if operations and (self._enabled or self._dry_run):\n            self._display_summary(operations)\n\n        self._sections = {}\n        self._yanked_warnings = []\n\n        # pip has to be installed/updated first without parallelism\n        # because we still need it for uninstalls\n        for i, op in enumerate(operations):\n            if op.package.name == \"pip\":\n                wait([self._executor.submit(self._execute_operation, op)])\n                del operations[i]\n                break\n\n        # We group operations by priority\n        groups = itertools.groupby(operations, key=lambda o: -o.priority)\n        for _, group in groups:\n            tasks = []\n            serial_operations = []\n            serial_git_operations = defaultdict(list)\n            for operation in group:\n                if self._shutdown:\n                    break\n\n                # Some operations are unsafe, we must execute them serially in a group\n                # https://github.com/python-poetry/poetry/issues/3086\n                # https://github.com/python-poetry/poetry/issues/2658\n                #\n                # We need to explicitly check source type here, see:\n                # https://github.com/python-poetry/poetry-core/pull/98\n                is_parallel_unsafe = operation.job_type == \"uninstall\" or (\n                    operation.package.develop\n                    and operation.package.source_type in {\"directory\", \"git\"}\n                )\n                # Skipped operations are safe to execute in parallel\n                if operation.skipped:\n                    is_parallel_unsafe = False\n\n                if is_parallel_unsafe:\n                    serial_operations.append(operation)\n                elif operation.package.source_type == \"git\":\n                    # Serially execute git operations that get cloned to the same directory,\n                    # to prevent multiple parallel git operations in the same repo.\n                    serial_git_operations[_package_get_name(operation.package)].append(\n                        operation\n                    )\n                else:\n                    tasks.append(\n                        self._executor.submit(self._execute_operation, operation)\n                    )\n\n            def _serialize(\n                repository_serial_operations: list[Operation],\n            ) -> None:\n                for operation in repository_serial_operations:\n                    self._execute_operation(operation)\n\n            # For each git repository, execute all operations serially\n            for repository_git_operations in serial_git_operations.values():\n                tasks.append(\n                    self._executor.submit(\n                        _serialize,\n                        repository_serial_operations=repository_git_operations,\n                    )\n                )\n\n            try:\n                wait(tasks)\n\n                for operation in serial_operations:\n                    self._execute_operation(operation)\n\n            except KeyboardInterrupt:\n                self._shutdown = True\n\n            if self._shutdown:\n                self._executor.shutdown(wait=True, cancel_futures=True)\n                break\n\n        for warning in self._yanked_warnings:\n            self._io.write_error_line(f\"<warning>Warning: {warning}</warning>\")\n        for path, issues in self._wheel_installer.invalid_wheels.items():\n            formatted_issues = \"\\n\".join(issues)\n            warning = (\n                f\"Validation of the RECORD file of {path.name} failed.\"\n                \" Please report to the maintainers of that package so they can fix\"\n                f\" their build process. Details:\\n{formatted_issues}\\n\"\n            )\n            self._io.write_error_line(f\"<warning>Warning: {warning}</warning>\")\n\n        return 1 if self._shutdown else 0\n\n    def _write(self, operation: Operation, line: str) -> None:\n        if not self.supports_fancy_output() or not self._should_write_operation(\n            operation\n        ):\n            return\n\n        if self._io.is_debug():\n            with self._lock:\n                section = self._sections[id(operation)]\n                section.write_line(line)\n\n            return\n\n        with self._lock:\n            section = self._sections[id(operation)]\n            section.clear()\n            section.write(line)\n\n    def _execute_operation(self, operation: Operation) -> None:\n        try:\n            op_message = self.get_operation_message(operation)\n            if self.supports_fancy_output():\n                if id(operation) not in self._sections and self._should_write_operation(\n                    operation\n                ):\n                    with self._lock:\n                        self._sections[id(operation)] = self._io.section()\n                        self._sections[id(operation)].write_line(\n                            f\"  <fg=blue;options=bold>-</> {op_message}:\"\n                            \" <fg=blue>Pending...</>\"\n                        )\n            else:\n                if self._should_write_operation(operation):\n                    if not operation.skipped:\n                        self._io.write_line(\n                            f\"  <fg=blue;options=bold>-</> {op_message}\"\n                        )\n                    else:\n                        self._io.write_line(\n                            f\"  <fg=default;options=bold,dark>-</> {op_message}: \"\n                            \"<fg=default;options=bold,dark>Skipped</> \"\n                            \"<fg=default;options=dark>for the following reason:</> \"\n                            f\"<fg=default;options=bold,dark>{operation.skip_reason}</>\"\n                        )\n\n            try:\n                result = self._do_execute_operation(operation)\n            except EnvCommandError as e:\n                if e.e.returncode == -2:\n                    result = -2\n                else:\n                    raise\n\n            # If we have a result of -2 it means a KeyboardInterrupt\n            # in the any python subprocess, so we raise a KeyboardInterrupt\n            # error to be picked up by the error handler.\n            if result == -2:\n                raise KeyboardInterrupt\n        except Exception as e:\n            try:\n                from cleo.ui.exception_trace import ExceptionTrace\n\n                io: IO | SectionOutput\n                if not self.supports_fancy_output():\n                    io = self._io\n                else:\n                    message = (\n                        \"  <error>-</error>\"\n                        f\" {self.get_operation_message(operation, error=True)}:\"\n                        \" <error>Failed</error>\"\n                    )\n                    self._write(operation, message)\n                    io = self._sections.get(id(operation), self._io)\n\n                with self._lock:\n                    pkg = operation.package\n                    with_trace = True\n\n                    if isinstance(e, IsolatedBuildBackendError):\n                        # TODO: Revisit once upstream fix is available https://github.com/python-poetry/cleo/issues/454\n                        # we disable trace here explicitly to workaround incorrect context detection by crashtest\n                        with_trace = False\n                        pip_command = \"pip wheel --no-cache-dir --use-pep517\"\n                        if pkg.develop:\n                            if pkg.source_type == \"git\":\n                                git_url_parts = (\n                                    pkg.to_dependency()\n                                    .to_pep_508()\n                                    .split(\";\", 1)[0]\n                                    .split(\"@\", 1)[-1]\n                                    .strip()\n                                ).split(\"#\", 1)\n                                requirement = f\"{git_url_parts[0]}#egg={pkg.name}\"\n                                if len(git_url_parts) > 1:\n                                    requirement += f\"&{git_url_parts[1]}\"\n                            else:\n                                assert pkg.source_url\n                                requirement = pkg.source_url\n                            pip_command += \" --editable\"\n                        else:\n                            requirement = (\n                                pkg.to_dependency().to_pep_508().split(\";\")[0].strip()\n                            )\n\n                        if config_settings := self._build_config_settings.get(pkg.name):\n                            for setting in config_settings:\n                                for setting_value in config_settings[setting]:\n                                    pip_command += f\" --config-settings='{setting}={setting_value}'\"\n\n                        message = e.generate_message(\n                            source_string=f\"{pkg.pretty_name} ({pkg.full_pretty_version})\",\n                            build_command=f'{pip_command} \"{requirement}\"',\n                        )\n                    elif isinstance(e, IsolatedBuildInstallError):\n                        message = (\n                            \"<error>\"\n                            \"Cannot install build-system.requires\"\n                            f\" for {pkg.pretty_name}.\"\n                            \"</error>\"\n                        )\n                    elif isinstance(e, SolverProblemError):\n                        message = (\n                            \"<error>\"\n                            \"Cannot resolve build-system.requires\"\n                            f\" for {pkg.pretty_name}.\"\n                            \"</error>\"\n                        )\n                    elif isinstance(e, PoetryRuntimeError):\n                        message = e.get_text(io.is_verbose(), indent=\"    | \").rstrip()\n                        message = f\"<warning>{message}</>\"\n                        with_trace = False\n                    else:\n                        message = f\"<error>Cannot install {pkg.pretty_name}.</error>\"\n\n                    if with_trace:\n                        ExceptionTrace(e).render(io)\n\n                    io.write_line(\"\")\n                    io.write_line(message)\n                    io.write_line(\"\")\n            finally:\n                with self._lock:\n                    self._shutdown = True\n\n        except KeyboardInterrupt:\n            try:\n                message = (\n                    \"  <warning>-</warning>\"\n                    f\" {self.get_operation_message(operation, warning=True)}:\"\n                    \" <warning>Cancelled</warning>\"\n                )\n                if not self.supports_fancy_output():\n                    self._io.write_line(message)\n                else:\n                    self._write(operation, message)\n            finally:\n                with self._lock:\n                    self._shutdown = True\n\n    def _do_execute_operation(self, operation: Operation) -> int:\n        method = operation.job_type\n\n        operation_message = self.get_operation_message(operation)\n        if operation.skipped:\n            if self.supports_fancy_output():\n                self._write(\n                    operation,\n                    f\"  <fg=default;options=bold,dark>-</> {operation_message}: \"\n                    \"<fg=default;options=bold,dark>Skipped</> \"\n                    \"<fg=default;options=dark>for the following reason:</> \"\n                    f\"<fg=default;options=bold,dark>{operation.skip_reason}</>\",\n                )\n\n            self._skipped[operation.job_type] += 1\n\n            return 0\n\n        if not self._enabled or self._dry_run:\n            return 0\n\n        result: int = getattr(self, f\"_execute_{method}\")(operation)\n\n        if result != 0:\n            return result\n\n        operation_message = self.get_operation_message(operation, done=True)\n        message = f\"  <fg=green;options=bold>-</> {operation_message}\"\n        self._write(operation, message)\n\n        self._increment_operations_count(operation, True)\n\n        return result\n\n    def _increment_operations_count(self, operation: Operation, executed: bool) -> None:\n        with self._lock:\n            if executed:\n                self._executed[operation.job_type] += 1\n            else:\n                self._skipped[operation.job_type] += 1\n\n    def run_pip(self, *args: Any, **kwargs: Any) -> int:\n        try:\n            self._env.run_pip(*args, **kwargs)\n        except EnvCommandError as e:\n            output = decode(e.e.output)\n            if (\n                \"KeyboardInterrupt\" in output\n                or \"ERROR: Operation cancelled by user\" in output\n            ):\n                return -2\n\n            raise\n\n        return 0\n\n    def get_operation_message(\n        self,\n        operation: Operation,\n        done: bool = False,\n        error: bool = False,\n        warning: bool = False,\n    ) -> str:\n        base_tag = \"fg=default\"\n        operation_color = \"c2\"\n        source_operation_color = \"c2\"\n        package_color = \"c1\"\n\n        if error:\n            operation_color = \"error\"\n        elif warning:\n            operation_color = \"warning\"\n        elif done:\n            operation_color = \"success\"\n\n        if operation.skipped:\n            base_tag = \"fg=default;options=dark\"\n            operation_color += \"_dark\"\n            source_operation_color += \"_dark\"\n            package_color += \"_dark\"\n\n        if isinstance(operation, Install):\n            return (\n                f\"<{base_tag}>Installing\"\n                f\" <{package_color}>{operation.package.name}</{package_color}>\"\n                f\" (<{operation_color}>{operation.package.full_pretty_version}</>)</>\"\n            )\n\n        if isinstance(operation, Uninstall):\n            return (\n                f\"<{base_tag}>Removing\"\n                f\" <{package_color}>{operation.package.name}</{package_color}>\"\n                f\" (<{operation_color}>{operation.package.full_pretty_version}</>)</>\"\n            )\n\n        if isinstance(operation, Update):\n            initial_version = (initial_pkg := operation.initial_package).version\n            target_version = (target_pkg := operation.target_package).version\n            update_kind = (\n                \"Updating\" if target_version >= initial_version else \"Downgrading\"\n            )\n            return (\n                f\"<{base_tag}>{update_kind}\"\n                f\" <{package_color}>{initial_pkg.name}</{package_color}> \"\n                f\"(<{source_operation_color}>\"\n                f\"{initial_pkg.full_pretty_version}\"\n                f\"</{source_operation_color}> -> <{operation_color}>\"\n                f\"{target_pkg.full_pretty_version}</>)</>\"\n            )\n        return \"\"\n\n    def _display_summary(self, operations: list[Operation]) -> None:\n        installs = 0\n        updates = 0\n        uninstalls = 0\n        skipped = 0\n        for op in operations:\n            if op.skipped:\n                skipped += 1\n                continue\n\n            if op.job_type == \"install\":\n                installs += 1\n            elif op.job_type == \"update\":\n                updates += 1\n            elif op.job_type == \"uninstall\":\n                uninstalls += 1\n\n        if not installs and not updates and not uninstalls and not self._verbose:\n            self._io.write_line(\"\")\n            self._io.write_line(\"No dependencies to install or update\")\n\n            return\n\n        self._io.write_line(\"\")\n        self._io.write(\"<b>Package operations</b>: \")\n        self._io.write(f\"<info>{installs}</> install{pluralize(installs)}, \")\n        self._io.write(f\"<info>{updates}</> update{pluralize(updates)}, \")\n        self._io.write(f\"<info>{uninstalls}</> removal{pluralize(uninstalls)}\")\n        if skipped and self._verbose:\n            self._io.write(f\", <info>{skipped}</> skipped\")\n        self._io.write_line(\"\")\n        self._io.write_line(\"\")\n\n    def _execute_install(self, operation: Install | Update) -> int:\n        status_code = self._install(operation)\n\n        self._save_url_reference(operation)\n\n        return status_code\n\n    def _execute_update(self, operation: Install | Update) -> int:\n        status_code = self._update(operation)\n\n        self._save_url_reference(operation)\n\n        return status_code\n\n    def _execute_uninstall(self, operation: Uninstall) -> int:\n        op_msg = self.get_operation_message(operation)\n        message = f\"  <fg=blue;options=bold>-</> {op_msg}: <info>Removing...</info>\"\n        self._write(operation, message)\n\n        return self._remove(operation.package)\n\n    def _install(self, operation: Install | Update) -> int:\n        package = operation.package\n\n        cleanup_archive: bool = False\n        if package.source_type == \"git\":\n            archive = self._prepare_git_archive(operation)\n            cleanup_archive = operation.package.develop\n        elif package.source_type == \"file\":\n            archive = self._prepare_archive(operation)\n        elif package.source_type == \"directory\":\n            archive = self._prepare_archive(operation)\n            cleanup_archive = True\n        elif package.source_type == \"url\":\n            assert package.source_url is not None\n            archive = self._download_link(operation, Link(package.source_url))\n        else:\n            archive = self._download(operation)\n\n        operation_message = self.get_operation_message(operation)\n        message = (\n            f\"  <fg=blue;options=bold>-</> {operation_message}:\"\n            \" <info>Installing...</info>\"\n        )\n        self._write(operation, message)\n\n        try:\n            if operation.job_type == \"update\":\n                # Uninstall first\n                # TODO: Make an uninstaller and find a way to rollback in case\n                # the new package can't be installed\n                assert isinstance(operation, Update)\n                self._remove(operation.initial_package)\n\n            self._wheel_installer.install(archive)\n        finally:\n            if cleanup_archive:\n                archive.unlink()\n\n        return 0\n\n    def _update(self, operation: Install | Update) -> int:\n        return self._install(operation)\n\n    def _remove(self, package: Package) -> int:\n        # If we have a VCS package, remove its source directory\n        if package.source_type == \"git\":\n            src_dir = self._env.path / \"src\" / package.name\n            if src_dir.exists():\n                remove_directory(src_dir, force=True)\n\n        try:\n            return self.run_pip(\"uninstall\", package.name, \"-y\")\n        except EnvCommandError as e:\n            if \"not installed\" in str(e):\n                return 0\n\n            raise\n\n    def _prepare_archive(\n        self, operation: Install | Update, *, output_dir: Path | None = None\n    ) -> Path:\n        package = operation.package\n        operation_message = self.get_operation_message(operation)\n\n        message = (\n            f\"  <fg=blue;options=bold>-</> {operation_message}:\"\n            f\"{format_build_wheel_log(package, self._env)}\"\n        )\n        self._write(operation, message)\n\n        assert package.source_url is not None\n        archive = Path(package.source_url)\n        if package.source_subdirectory:\n            archive = archive / package.source_subdirectory\n        if not Path(package.source_url).is_absolute() and package.root_dir:\n            archive = package.root_dir / archive\n\n        self._populate_hashes_dict(archive, package)\n\n        name = operation.package.name\n        return self._chef.prepare(\n            archive,\n            editable=package.develop,\n            output_dir=output_dir,\n            config_settings=self._build_config_settings.get(name),\n            build_constraints=self._build_constraints.get(name),\n        )\n\n    def _prepare_git_archive(self, operation: Install | Update) -> Path:\n        package = operation.package\n        assert package.source_url is not None\n\n        if package.source_resolved_reference and not package.develop:\n            # Only cache git archives when we know precise reference hash,\n            # otherwise we might get stale archives\n            cached_archive = self._artifact_cache.get_cached_archive_for_git(\n                package.source_url,\n                package.source_resolved_reference,\n                package.source_subdirectory,\n                env=self._env,\n            )\n            if cached_archive is not None:\n                return cached_archive\n\n        operation_message = self.get_operation_message(operation)\n\n        message = (\n            f\"  <fg=blue;options=bold>-</> {operation_message}: <info>Cloning...</info>\"\n        )\n        self._write(operation, message)\n\n        source = Git.clone(\n            url=package.source_url,\n            source_root=self._env.path / \"src\",\n            revision=package.source_resolved_reference or package.source_reference,\n        )\n\n        # Now we just need to install from the source directory\n        original_url = package.source_url\n        package._source_url = str(source.path)\n\n        output_dir = None\n        if package.source_resolved_reference and not package.develop:\n            output_dir = self._artifact_cache.get_cache_directory_for_git(\n                original_url,\n                package.source_resolved_reference,\n                package.source_subdirectory,\n            )\n\n        try:\n            archive = self._prepare_archive(operation, output_dir=output_dir)\n        except Exception:\n            # always reset source_url in case of an error for correct output\n            package._source_url = original_url\n            raise\n        if not package.develop:\n            package._source_url = original_url\n\n        if output_dir is not None and output_dir.is_dir():\n            # Mark directories with cached git packages, to distinguish from\n            # \"normal\" cache\n            (output_dir / \".created_from_git_dependency\").touch()\n\n        return archive\n\n    def _download(self, operation: Install | Update) -> Path:\n        link = self._chooser.choose_for(operation.package)\n\n        if link.yanked:\n            # Store yanked warnings in a list and print after installing, so they can't\n            # be overlooked. Further, printing them in the concerning section would have\n            # the risk of overwriting the warning, so it is only briefly visible.\n            message = (\n                f\"The file chosen for install of {operation.package.pretty_name} \"\n                f\"{operation.package.pretty_version} ({link.show_url}) is yanked.\"\n            )\n            if link.yanked_reason:\n                message += f\" Reason for being yanked: {link.yanked_reason}\"\n            self._yanked_warnings.append(message)\n\n        return self._download_link(operation, link)\n\n    def _download_link(self, operation: Install | Update, link: Link) -> Path:\n        package = operation.package\n\n        # Get original package for the link provided\n        download_func = functools.partial(self._download_archive, operation)\n        original_archive = self._artifact_cache.get_cached_archive_for_link(\n            link, strict=True, download_func=download_func\n        )\n\n        # Get potential higher prioritized cached archive, otherwise it will fall back\n        # to the original archive.\n        archive = self._artifact_cache.get_cached_archive_for_link(\n            link,\n            strict=False,\n            env=self._env,\n        )\n        if archive is None:\n            # Since we previously downloaded an archive, we now should have\n            # something cached that we can use here. The only case in which\n            # archive is None is if the original archive is not valid for the\n            # current environment.\n            raise RuntimeError(\n                f\"Package {link.url} cannot be installed in the current environment\"\n                f\" {self._env.marker_env}\"\n            )\n\n        if archive.suffix != \".whl\":\n            message = (\n                f\"  <fg=blue;options=bold>-</> {self.get_operation_message(operation)}:\"\n                f\"{format_build_wheel_log(package, self._env)}\"\n            )\n            self._write(operation, message)\n\n            name = operation.package.name\n            archive = self._chef.prepare(\n                archive,\n                output_dir=original_archive.parent,\n                config_settings=self._build_config_settings.get(name),\n                build_constraints=self._build_constraints.get(name),\n            )\n\n        # Use the original archive to provide the correct hash.\n        self._populate_hashes_dict(original_archive, package)\n\n        return archive\n\n    def _populate_hashes_dict(self, archive: Path, package: Package) -> None:\n        if package.files and archive.name in {f[\"file\"] for f in package.files}:\n            archive_hash = self._validate_archive_hash(archive, package)\n            self._hashes[package.name] = archive_hash\n\n    @staticmethod\n    def _validate_archive_hash(archive: Path, package: Package) -> str:\n        known_hashes = {f[\"hash\"] for f in package.files if f[\"file\"] == archive.name}\n        hash_types = {t.split(\":\")[0] for t in known_hashes}\n        hash_type = get_highest_priority_hash_type(hash_types, archive.name)\n\n        if hash_type is None:\n            raise RuntimeError(\n                f\"No usable hash type(s) for {package} from archive\"\n                f\" {archive.name} found (known hashes: {known_hashes!s})\"\n            )\n\n        archive_hash = f\"{hash_type}:{get_file_hash(archive, hash_type)}\"\n\n        if archive_hash not in known_hashes:\n            raise RuntimeError(\n                f\"Hash for {package} from archive {archive.name} not found in\"\n                f\" known hashes (was: {archive_hash})\"\n            )\n\n        return archive_hash\n\n    def _download_archive(\n        self,\n        operation: Install | Update,\n        url: str,\n        dest: Path,\n    ) -> None:\n        downloader = Downloader(\n            url, dest, self._authenticator, max_retries=self._max_retries\n        )\n        wheel_size = downloader.total_size\n\n        operation_message = self.get_operation_message(operation)\n        message = (\n            f\"  <fg=blue;options=bold>-</> {operation_message}: <info>Downloading...</>\"\n        )\n        progress = None\n        if self.supports_fancy_output():\n            if wheel_size is None:\n                self._write(operation, message)\n            else:\n                from cleo.ui.progress_bar import ProgressBar\n\n                progress = ProgressBar(\n                    self._sections[id(operation)], max=int(wheel_size)\n                )\n                progress.set_format(message + \" <b>%percent%%</b>\")\n\n        if progress:\n            with self._lock:\n                self._sections[id(operation)].clear()\n                progress.start()\n\n        for fetched_size in downloader.download_with_progress(chunk_size=4096):\n            if progress:\n                with self._lock:\n                    progress.set_progress(fetched_size)\n\n        if progress:\n            with self._lock:\n                progress.finish()\n\n    def _should_write_operation(self, operation: Operation) -> bool:\n        return (\n            not operation.skipped or self._dry_run or self._verbose or not self._enabled\n        )\n\n    def _save_url_reference(self, operation: Operation) -> None:\n        \"\"\"\n        Create and store a PEP-610 `direct_url.json` file, if needed.\n        \"\"\"\n        if operation.job_type not in {\"install\", \"update\"}:\n            return\n\n        package = operation.package\n\n        if not package.source_url or package.source_type == \"legacy\":\n            return\n\n        url_reference: dict[str, Any] | None = None\n\n        if package.source_type == \"git\" and not package.develop:\n            url_reference = self._create_git_url_reference(package)\n        elif package.source_type in (\"directory\", \"git\"):\n            url_reference = self._create_directory_url_reference(package)\n        elif package.source_type == \"url\":\n            url_reference = self._create_url_url_reference(package)\n        elif package.source_type == \"file\":\n            url_reference = self._create_file_url_reference(package)\n\n        if url_reference:\n            for dist in self._env.site_packages.distributions(\n                name=package.name, writable_only=True\n            ):\n                dist_path = dist._path  # type: ignore[attr-defined]\n                assert isinstance(dist_path, Path)\n                url = dist_path / \"direct_url.json\"\n                url.write_text(json.dumps(url_reference), encoding=\"utf-8\")\n\n                record = dist_path / \"RECORD\"\n                if record.exists():\n                    with record.open(mode=\"a\", encoding=\"utf-8\", newline=\"\") as f:\n                        writer = csv.writer(f)\n                        path = url.relative_to(record.parent.parent)\n                        writer.writerow([str(path), \"\", \"\"])\n\n    def _create_git_url_reference(self, package: Package) -> dict[str, Any]:\n        reference = {\n            \"url\": package.source_url,\n            \"vcs_info\": {\n                \"vcs\": \"git\",\n                \"requested_revision\": package.source_reference,\n                \"commit_id\": package.source_resolved_reference,\n            },\n        }\n        if package.source_subdirectory:\n            reference[\"subdirectory\"] = package.source_subdirectory\n\n        return reference\n\n    def _create_url_url_reference(self, package: Package) -> dict[str, Any]:\n        archive_info = self._get_archive_info(package)\n\n        return {\"url\": package.source_url, \"archive_info\": archive_info}\n\n    def _create_file_url_reference(self, package: Package) -> dict[str, Any]:\n        archive_info = self._get_archive_info(package)\n\n        assert package.source_url is not None\n        return {\n            \"url\": Path(package.source_url).as_uri(),\n            \"archive_info\": archive_info,\n        }\n\n    def _create_directory_url_reference(self, package: Package) -> dict[str, Any]:\n        dir_info = {}\n\n        if package.develop:\n            dir_info[\"editable\"] = True\n\n        assert package.source_url is not None\n        return {\n            \"url\": Path(package.source_url).as_uri(),\n            \"dir_info\": dir_info,\n        }\n\n    def _get_archive_info(self, package: Package) -> dict[str, Any]:\n        \"\"\"\n        Create dictionary `archive_info` for file `direct_url.json`.\n\n        Specification: https://packaging.python.org/en/latest/specifications/direct-url\n        (it supersedes PEP 610)\n\n        :param package: This must be a poetry package instance.\n        \"\"\"\n        archive_info = {}\n\n        if package.name in self._hashes:\n            algorithm, value = self._hashes[package.name].split(\":\")\n            archive_info[\"hashes\"] = {algorithm: value}\n\n        return archive_info\n"
  },
  {
    "path": "src/poetry/installation/installer.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import cast\n\nfrom cleo.io.null_io import NullIO\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.installation.executor import Executor\nfrom poetry.puzzle.transaction import Transaction\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.repositories.lockfile_repository import LockfileRepository\nfrom poetry.utils.constants import POETRY_SYSTEM_PROJECT_NAME\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n    from collections.abc import Mapping\n\n    from cleo.io.io import IO\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.path_dependency import PathDependency\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.config.config import Config\n    from poetry.installation.operations.operation import Operation\n    from poetry.packages import Locker\n    from poetry.packages.transitive_package_info import TransitivePackageInfo\n    from poetry.utils.env import Env\n\n\nclass Installer:\n    def __init__(\n        self,\n        io: IO,\n        env: Env,\n        package: ProjectPackage,\n        locker: Locker,\n        pool: RepositoryPool,\n        config: Config,\n        installed: InstalledRepository | None = None,\n        executor: Executor | None = None,\n        disable_cache: bool = False,\n        *,\n        build_constraints: Mapping[NormalizedName, list[Dependency]] | None = None,\n    ) -> None:\n        self._io = io\n        self._env = env\n        self._package = package\n        self._locker = locker\n        self._pool = pool\n        self._config = config\n\n        self._dry_run = False\n        self._requires_synchronization = False\n        self._update = False\n        self._verbose = False\n        self._groups: Iterable[NormalizedName] | None = None\n        self._skip_directory = False\n        self._lock = False\n\n        self._whitelist: list[NormalizedName] = []\n\n        self._extras: list[NormalizedName] = []\n\n        if executor is None:\n            executor = Executor(\n                self._env,\n                self._pool,\n                config,\n                self._io,\n                disable_cache=disable_cache,\n                build_constraints=build_constraints,\n            )\n\n        self._executor = executor\n\n        if installed is None:\n            installed = self._get_installed()\n\n        self._installed_repository = installed\n\n    @property\n    def executor(self) -> Executor:\n        return self._executor\n\n    def set_package(self, package: ProjectPackage) -> Installer:\n        self._package = package\n\n        return self\n\n    def set_locker(self, locker: Locker) -> Installer:\n        self._locker = locker\n\n        return self\n\n    def run(self) -> int:\n        # Check if refresh\n        if not self._update and self._lock and self._locker.is_locked():\n            return self._do_refresh()\n\n        # Force update if there is no lock file present\n        if not self._update and not self._locker.is_locked():\n            self._update = True\n\n        if self.is_dry_run():\n            self.verbose(True)\n\n        return self._do_install()\n\n    def dry_run(self, dry_run: bool = True) -> Installer:\n        self._dry_run = dry_run\n        self._executor.dry_run(dry_run)\n\n        return self\n\n    def is_dry_run(self) -> bool:\n        return self._dry_run\n\n    def requires_synchronization(\n        self, requires_synchronization: bool = True\n    ) -> Installer:\n        self._requires_synchronization = requires_synchronization\n\n        return self\n\n    def verbose(self, verbose: bool = True) -> Installer:\n        self._verbose = verbose\n        self._executor.verbose(verbose)\n\n        return self\n\n    def is_verbose(self) -> bool:\n        return self._verbose\n\n    def only_groups(self, groups: Iterable[NormalizedName]) -> Installer:\n        self._groups = groups\n\n        return self\n\n    def update(self, update: bool = True) -> Installer:\n        self._update = update\n\n        return self\n\n    def skip_directory(self, skip_directory: bool = False) -> Installer:\n        self._skip_directory = skip_directory\n\n        return self\n\n    def lock(self, update: bool = True) -> Installer:\n        \"\"\"\n        Prepare the installer for locking only.\n        \"\"\"\n        self.update(update=update)\n        self.execute_operations(False)\n        self._lock = True\n\n        return self\n\n    def is_updating(self) -> bool:\n        return self._update\n\n    def execute_operations(self, execute: bool = True) -> Installer:\n        if not execute:\n            self._executor.disable()\n\n        return self\n\n    def whitelist(self, packages: Iterable[str]) -> Installer:\n        self._whitelist = [canonicalize_name(p) for p in packages]\n\n        return self\n\n    def extras(self, extras: list[str]) -> Installer:\n        self._extras = [canonicalize_name(extra) for extra in extras]\n\n        return self\n\n    def _do_refresh(self) -> int:\n        from poetry.puzzle.solver import Solver\n\n        # Checking extras\n        for extra in self._extras:\n            if extra not in self._package.extras:\n                raise ValueError(f\"Extra [{extra}] is not specified.\")\n\n        locked_repository = self._locker.locked_repository()\n        solver = Solver(\n            self._package,\n            self._pool,\n            locked_repository.packages,\n            locked_repository.packages,\n            self._io,\n        )\n\n        # Always re-solve directory dependencies, otherwise we can't determine\n        # if anything has changed (and the lock file contains an invalid version).\n        use_latest = [\n            p.name for p in locked_repository.packages if p.source_type == \"directory\"\n        ]\n\n        with solver.provider.use_source_root(\n            source_root=self._env.path.joinpath(\"src\")\n        ):\n            solved_packages = solver.solve(use_latest=use_latest).get_solved_packages()\n\n        self._write_lock_file(solved_packages, force=True)\n\n        return 0\n\n    def _do_install(self) -> int:\n        from poetry.puzzle.solver import Solver\n\n        locked_repository = Repository(\"poetry-locked\")\n        reresolve = self._config.get(\"installer.re-resolve\", False)\n        solved_packages: dict[Package, TransitivePackageInfo] = {}\n        lockfile_repo = LockfileRepository()\n\n        if self._update:\n            if not self._lock and self._locker.is_locked():\n                locked_repository = self._locker.locked_repository()\n\n                # If no packages have been whitelisted (The ones we want to update),\n                # we whitelist every package in the lock file.\n                if not self._whitelist:\n                    for pkg in locked_repository.packages:\n                        self._whitelist.append(pkg.name)\n\n            # Checking extras\n            for extra in self._extras:\n                if extra not in self._package.extras:\n                    raise ValueError(f\"Extra [{extra}] is not specified.\")\n\n            self._io.write_line(\"<info>Updating dependencies</>\")\n            solver = Solver(\n                self._package,\n                self._pool,\n                self._installed_repository.packages,\n                locked_repository.packages,\n                self._io,\n            )\n\n            with solver.provider.use_source_root(\n                source_root=self._env.path.joinpath(\"src\")\n            ):\n                solved_packages = solver.solve(\n                    use_latest=self._whitelist\n                ).get_solved_packages()\n\n            if not self.executor.enabled:\n                # If we are only in lock mode, no need to go any further\n                self._write_lock_file(solved_packages)\n                return 0\n\n            for package in solved_packages:\n                if not lockfile_repo.has_package(package):\n                    lockfile_repo.add_package(package)\n\n        else:\n            self._io.write_line(\"<info>Installing dependencies from lock file</>\")\n\n            if not self._locker.is_fresh():\n                raise ValueError(\n                    \"pyproject.toml changed significantly since poetry.lock was last\"\n                    f\" generated. Run `{self._lock_fix_command()}` to fix the lock file.\"\n                )\n            if not (reresolve or self._locker.is_locked_groups_and_markers()):\n                if self._io.is_verbose():\n                    self._io.write_line(\n                        \"<info>Cannot install without re-resolving\"\n                        \" because the lock file is not at least version 2.1</>\"\n                    )\n                reresolve = True\n\n            locker_extras = {\n                canonicalize_name(extra)\n                for extra in self._locker.lock_data.get(\"extras\", {})\n            }\n            for extra in self._extras:\n                if extra not in locker_extras:\n                    raise ValueError(f\"Extra [{extra}] is not specified.\")\n\n            locked_repository = self._locker.locked_repository()\n            if reresolve:\n                lockfile_repo = locked_repository\n            else:\n                solved_packages = self._locker.locked_packages()\n\n        if self._io.is_verbose():\n            self._io.write_line(\"\")\n            self._io.write_line(\n                \"<info>Finding the necessary packages for the current system</>\"\n            )\n\n        if reresolve:\n            if self._groups is not None:\n                root = self._package.with_dependency_groups(\n                    list(self._groups), only=True\n                )\n            else:\n                root = self._package.without_optional_dependency_groups()\n\n            # We resolve again by only using the lock file\n            packages = lockfile_repo.packages + locked_repository.packages\n            pool = RepositoryPool.from_packages(packages, self._config)\n\n            solver = Solver(\n                root,\n                pool,\n                self._installed_repository.packages,\n                locked_repository.packages,\n                NullIO(),\n                active_root_extras=self._extras,\n            )\n            # Everything is resolved at this point, so we no longer need\n            # to load deferred dependencies (i.e. VCS, URL and path dependencies)\n            solver.provider.load_deferred(False)\n\n            with solver.use_environment(self._env):\n                transaction = solver.solve(use_latest=self._whitelist)\n\n        else:\n            if self._groups is None:\n                groups = self._package.dependency_group_names()\n            else:\n                groups = set(self._groups)\n            transaction = Transaction(\n                locked_repository.packages,\n                solved_packages,\n                self._installed_repository.packages,\n                self._package,\n                self._env.marker_env,\n                groups,\n            )\n\n        ops = transaction.calculate_operations(\n            with_uninstalls=(\n                self._requires_synchronization or (self._update and not reresolve)\n            ),\n            synchronize=self._requires_synchronization,\n            skip_directory=self._skip_directory,\n            extras=set(self._extras),\n            system_site_packages={\n                p.name for p in self._installed_repository.system_site_packages\n            },\n        )\n        if reresolve and not self._requires_synchronization:\n            # If no packages synchronisation has been requested we need\n            # to calculate the uninstall operations\n            transaction = Transaction(\n                locked_repository.packages,\n                lockfile_repo.packages,\n                installed_packages=self._installed_repository.packages,\n                root_package=root,\n            )\n\n            ops = [\n                op\n                for op in transaction.calculate_operations(with_uninstalls=True)\n                if op.job_type == \"uninstall\"\n            ] + ops\n\n        # Validate the dependencies\n        for op in ops:\n            dep = op.package.to_dependency()\n            if dep.is_file() or dep.is_directory():\n                dep = cast(\"PathDependency\", dep)\n                dep.validate(raise_error=not op.skipped)\n\n        # Execute operations\n        status = self._execute(ops)\n\n        if status == 0 and self._update:\n            # Only write lock file when installation is success\n            self._write_lock_file(solved_packages)\n\n        return status\n\n    def _lock_fix_command(self) -> str:\n        # `poetry self` commands operate on Poetry's own system project. When the lock\n        # file is outdated, users should run `poetry self lock` rather than `poetry lock`.\n        if self._package.name == POETRY_SYSTEM_PROJECT_NAME:\n            return \"poetry self lock\"\n\n        return \"poetry lock\"\n\n    def _write_lock_file(\n        self,\n        packages: dict[Package, TransitivePackageInfo],\n        force: bool = False,\n    ) -> None:\n        if not self.is_dry_run() and (force or self._update):\n            updated_lock = self._locker.set_lock_data(self._package, packages)\n\n            if updated_lock:\n                self._io.write_line(\"\")\n                self._io.write_line(\"<info>Writing lock file</>\")\n\n    def _execute(self, operations: list[Operation]) -> int:\n        return self._executor.execute(operations)\n\n    def _get_installed(self) -> InstalledRepository:\n        return InstalledRepository.load(self._env)\n"
  },
  {
    "path": "src/poetry/installation/operations/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.installation.operations.install import Install\nfrom poetry.installation.operations.uninstall import Uninstall\nfrom poetry.installation.operations.update import Update\n\n\n__all__ = [\"Install\", \"Uninstall\", \"Update\"]\n"
  },
  {
    "path": "src/poetry/installation/operations/install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.installation.operations.operation import Operation\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n\nclass Install(Operation):\n    def __init__(\n        self, package: Package, reason: str | None = None, priority: int = 0\n    ) -> None:\n        super().__init__(reason, priority=priority)\n\n        self._package = package\n\n    @property\n    def package(self) -> Package:\n        return self._package\n\n    @property\n    def job_type(self) -> str:\n        return \"install\"\n\n    def __str__(self) -> str:\n        return (\n            \"Installing\"\n            f\" {self.package.pretty_name} ({self.format_version(self.package)})\"\n        )\n\n    def __repr__(self) -> str:\n        return (\n            \"<Install\"\n            f\" {self.package.pretty_name} ({self.format_version(self.package)})>\"\n        )\n"
  },
  {
    "path": "src/poetry/installation/operations/operation.py",
    "content": "from __future__ import annotations\n\nfrom abc import ABC\nfrom abc import abstractmethod\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n    from typing_extensions import Self\n\n\nclass Operation(ABC):\n    def __init__(self, reason: str | None = None, priority: float = 0) -> None:\n        self._reason = reason\n\n        self._skipped = False\n        self._skip_reason: str | None = None\n        self._priority = priority\n\n    @property\n    @abstractmethod\n    def job_type(self) -> str: ...\n\n    @property\n    def reason(self) -> str | None:\n        return self._reason\n\n    @property\n    def skipped(self) -> bool:\n        return self._skipped\n\n    @property\n    def skip_reason(self) -> str | None:\n        return self._skip_reason\n\n    @property\n    def priority(self) -> float:\n        return self._priority\n\n    @property\n    @abstractmethod\n    def package(self) -> Package: ...\n\n    def format_version(self, package: Package) -> str:\n        version: str = package.full_pretty_version\n        return version\n\n    def skip(self, reason: str) -> Self:\n        self._skipped = True\n        self._skip_reason = reason\n\n        return self\n"
  },
  {
    "path": "src/poetry/installation/operations/uninstall.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.installation.operations.operation import Operation\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n\nclass Uninstall(Operation):\n    def __init__(\n        self,\n        package: Package,\n        reason: str | None = None,\n        priority: float = float(\"inf\"),\n    ) -> None:\n        super().__init__(reason, priority=priority)\n\n        self._package = package\n\n    @property\n    def package(self) -> Package:\n        return self._package\n\n    @property\n    def job_type(self) -> str:\n        return \"uninstall\"\n\n    def __str__(self) -> str:\n        return (\n            \"Uninstalling\"\n            f\" {self.package.pretty_name} ({self.format_version(self._package)})\"\n        )\n\n    def __repr__(self) -> str:\n        return (\n            \"<Uninstall\"\n            f\" {self.package.pretty_name} ({self.format_version(self.package)})>\"\n        )\n"
  },
  {
    "path": "src/poetry/installation/operations/update.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.installation.operations.operation import Operation\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n\nclass Update(Operation):\n    def __init__(\n        self,\n        initial: Package,\n        target: Package,\n        reason: str | None = None,\n        priority: int = 0,\n    ) -> None:\n        self._initial_package = initial\n        self._target_package = target\n\n        super().__init__(reason, priority=priority)\n\n    @property\n    def initial_package(self) -> Package:\n        return self._initial_package\n\n    @property\n    def target_package(self) -> Package:\n        return self._target_package\n\n    @property\n    def package(self) -> Package:\n        return self._target_package\n\n    @property\n    def job_type(self) -> str:\n        return \"update\"\n\n    def __str__(self) -> str:\n        init_version = self.format_version(self.initial_package)\n        target_version = self.format_version(self.target_package)\n        return (\n            f\"Updating {self.initial_package.pretty_name} ({init_version}) \"\n            f\"to {self.target_package.pretty_name} ({target_version})\"\n        )\n\n    def __repr__(self) -> str:\n        init_version = self.format_version(self.initial_package)\n        target_version = self.format_version(self.target_package)\n        return (\n            f\"<Update {self.initial_package.pretty_name} ({init_version}) \"\n            f\"to {self.target_package.pretty_name} ({target_version})>\"\n        )\n"
  },
  {
    "path": "src/poetry/installation/wheel_installer.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport platform\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom installer import install\nfrom installer.destinations import SchemeDictionaryDestination\nfrom installer.sources import WheelFile\nfrom installer.sources import _WheelFileValidationError\n\nfrom poetry.__version__ import __version__\nfrom poetry.utils._compat import WINDOWS\n\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n    from typing import BinaryIO\n\n    from installer.records import RecordEntry\n    from installer.scripts import LauncherKind\n    from installer.utils import Scheme\n\n    from poetry.utils.env import Env\n\n\nclass WheelDestination(SchemeDictionaryDestination):\n    \"\"\" \"\"\"\n\n    def write_to_fs(\n        self,\n        scheme: Scheme,\n        path: str,\n        stream: BinaryIO,\n        is_executable: bool,\n    ) -> RecordEntry:\n        from installer.records import Hash\n        from installer.records import RecordEntry\n        from installer.utils import copyfileobj_with_hashing\n        from installer.utils import make_file_executable\n\n        target_path = Path(self.scheme_dict[scheme]) / path\n        if target_path.exists():\n            # Contrary to the base library we don't raise an error here since it can\n            # break pkgutil-style and pkg_resource-style namespace packages.\n            logger.warning(f\"Installing {target_path} over existing file\")\n\n        parent_folder = target_path.parent\n        if not parent_folder.exists():\n            # Due to the parallel installation it can happen\n            # that two threads try to create the directory.\n            parent_folder.mkdir(parents=True, exist_ok=True)\n\n        with target_path.open(\"wb\") as f:\n            hash_, size = copyfileobj_with_hashing(stream, f, self.hash_algorithm)\n\n        if is_executable:\n            make_file_executable(target_path)\n\n        return RecordEntry(path, Hash(self.hash_algorithm, hash_), size)\n\n\nclass WheelInstaller:\n    def __init__(self, env: Env) -> None:\n        self._env = env\n\n        script_kind: LauncherKind\n        if not WINDOWS:\n            script_kind = \"posix\"\n        else:\n            if platform.uname()[4].startswith(\"arm\"):\n                script_kind = \"win-arm64\" if sys.maxsize > 2**32 else \"win-arm\"\n            else:\n                script_kind = \"win-amd64\" if sys.maxsize > 2**32 else \"win-ia32\"\n        self._script_kind = script_kind\n\n        self._bytecode_optimization_levels: Collection[int] = ()\n        self.invalid_wheels: dict[Path, list[str]] = {}\n\n    def enable_bytecode_compilation(self, enable: bool = True) -> None:\n        self._bytecode_optimization_levels = (-1,) if enable else ()\n\n    def install(self, wheel: Path) -> None:\n        with WheelFile.open(wheel) as source:\n            try:\n                # Content validation is temporarily disabled because of\n                # pypa/installer's out of memory issues with big wheels. See\n                # https://github.com/python-poetry/poetry/issues/7983\n                source.validate_record(validate_contents=False)\n            except _WheelFileValidationError as e:\n                self.invalid_wheels[wheel] = e.issues\n\n            scheme_dict = self._env.scheme_dict.copy()\n            scheme_dict[\"headers\"] = str(\n                Path(scheme_dict[\"include\"]) / source.distribution\n            )\n            destination = WheelDestination(\n                scheme_dict,\n                interpreter=str(self._env.python),\n                script_kind=self._script_kind,\n                bytecode_optimization_levels=self._bytecode_optimization_levels,\n            )\n\n            install(\n                source=source,\n                destination=destination,\n                # Additional metadata that is generated by the installation tool.\n                additional_metadata={\n                    \"INSTALLER\": f\"Poetry {__version__}\".encode(),\n                },\n            )\n"
  },
  {
    "path": "src/poetry/json/__init__.py",
    "content": "from __future__ import annotations\n\nimport json\n\nfrom importlib.resources import files\nfrom typing import Any\n\nimport fastjsonschema\n\nfrom fastjsonschema.exceptions import JsonSchemaValueException\n\n\ndef validate_object(obj: dict[str, Any]) -> list[str]:\n    schema = json.loads(\n        (files(__package__) / \"schemas\" / \"poetry.json\").read_text(encoding=\"utf-8\")\n    )\n\n    validate = fastjsonschema.compile(schema)\n\n    errors = []\n    try:\n        validate(obj)\n    except JsonSchemaValueException as e:\n        errors = [e.message]\n\n    core_schema = json.loads(\n        (files(\"poetry.core\") / \"json\" / \"schemas\" / \"poetry-schema.json\").read_text(\n            encoding=\"utf-8\"\n        )\n    )\n\n    properties = schema[\"properties\"].keys() | core_schema[\"properties\"].keys()\n    additional_properties = obj.keys() - properties\n    for key in additional_properties:\n        errors.append(f\"Additional properties are not allowed ('{key}' was unexpected)\")\n\n    return errors\n"
  },
  {
    "path": "src/poetry/json/schemas/poetry.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n  \"additionalProperties\": true,\n  \"type\": \"object\",\n  \"required\": [],\n  \"properties\": {\n    \"requires-poetry\": {\n      \"type\": \"string\",\n      \"description\": \"The version constraint for Poetry itself.\",\n      \"$ref\": \"#/definitions/dependency\"\n    },\n    \"requires-plugins\": {\n      \"type\": \"object\",\n      \"description\": \"Poetry plugins that are required for this project.\",\n      \"$ref\": \"#/definitions/dependencies\",\n      \"additionalProperties\": false\n    },\n    \"source\": {\n      \"type\": \"array\",\n      \"description\": \"A set of additional repositories where packages can be found.\",\n      \"additionalProperties\": {\n        \"$ref\": \"#/definitions/repository\"\n      },\n      \"items\": {\n        \"$ref\": \"#/definitions/repository\"\n      }\n    },\n    \"build-constraints\": {\n      \"type\": \"object\",\n      \"description\": \"This is a dict of package name (keys) and version constraints (values) to restrict build requirements for a package.\",\n      \"patternProperties\": {\n        \"^[a-zA-Z-_.0-9]+$\": {\n          \"$ref\": \"#/definitions/dependencies\"\n        }\n      }\n    }\n  },\n  \"definitions\": {\n    \"repository\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\n        \"name\"\n      ],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"The name of the repository.\"\n        },\n        \"url\": {\n          \"type\": \"string\",\n          \"description\": \"The url of the repository.\",\n          \"format\": \"uri\"\n        },\n        \"priority\": {\n          \"enum\": [\n            \"primary\",\n            \"supplemental\",\n            \"explicit\"\n          ],\n          \"description\": \"Declare the priority of this repository.\"\n        },\n        \"links\": {\n          \"type\": \"boolean\",\n          \"description\": \"Declare this as a link source. Links at uri/path can point to sdist or bdist archives.\"\n        },\n        \"indexed\": {\n          \"type\": \"boolean\",\n          \"description\": \"For PEP 503 simple API repositories, pre-fetch and index the available packages. (experimental)\"\n        }\n      }\n    },\n    \"dependencies\": {\n      \"type\": \"object\",\n      \"patternProperties\": {\n        \"^[a-zA-Z-_.0-9]+$\": {\n          \"oneOf\": [\n            {\n              \"$ref\": \"#/definitions/dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/long-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/git-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/file-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/path-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/url-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/multiple-constraints-dependency\"\n            },\n            {\n              \"$ref\": \"#/definitions/dependency-options\"\n            }\n          ]\n        }\n      }\n    },\n    \"dependency\": {\n      \"type\": \"string\",\n      \"description\": \"The constraint of the dependency.\"\n    },\n    \"long-dependency\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"version\"\n      ],\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"version\": {\n          \"type\": \"string\",\n          \"description\": \"The constraint of the dependency.\"\n        },\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"allow-prereleases\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency allows prereleases or not.\"\n        },\n        \"allows-prereleases\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency allows prereleases or not.\"\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency is optional or not.\"\n        },\n        \"extras\": {\n          \"type\": \"array\",\n          \"description\": \"The required extras for this dependency.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"source\": {\n          \"type\": \"string\",\n          \"description\": \"The exclusive source used to search for this dependency.\"\n        }\n      }\n    },\n    \"git-dependency\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"git\"\n      ],\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"git\": {\n          \"type\": \"string\",\n          \"description\": \"The url of the git repository.\"\n        },\n        \"branch\": {\n          \"type\": \"string\",\n          \"description\": \"The branch to checkout.\"\n        },\n        \"tag\": {\n          \"type\": \"string\",\n          \"description\": \"The tag to checkout.\"\n        },\n        \"rev\": {\n          \"type\": \"string\",\n          \"description\": \"The revision to checkout.\"\n        },\n        \"subdirectory\": {\n          \"type\": \"string\",\n          \"description\": \"The relative path to the directory where the package is located.\"\n        },\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"allow-prereleases\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency allows prereleases or not.\"\n        },\n        \"allows-prereleases\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency allows prereleases or not.\"\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency is optional or not.\"\n        },\n        \"extras\": {\n          \"type\": \"array\",\n          \"description\": \"The required extras for this dependency.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"develop\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether to install the dependency in development mode.\"\n        }\n      }\n    },\n    \"file-dependency\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"file\"\n      ],\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"file\": {\n          \"type\": \"string\",\n          \"description\": \"The path to the file.\"\n        },\n        \"subdirectory\": {\n          \"type\": \"string\",\n          \"description\": \"The relative path to the directory where the package is located.\"\n        },\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency is optional or not.\"\n        },\n        \"extras\": {\n          \"type\": \"array\",\n          \"description\": \"The required extras for this dependency.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"path-dependency\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"path\"\n      ],\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"path\": {\n          \"type\": \"string\",\n          \"description\": \"The path to the dependency.\"\n        },\n        \"subdirectory\": {\n          \"type\": \"string\",\n          \"description\": \"The relative path to the directory where the package is located.\"\n        },\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency is optional or not.\"\n        },\n        \"extras\": {\n          \"type\": \"array\",\n          \"description\": \"The required extras for this dependency.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"develop\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether to install the dependency in development mode.\"\n        }\n      }\n    },\n    \"url-dependency\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"url\"\n      ],\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"url\": {\n          \"type\": \"string\",\n          \"description\": \"The url to the file.\"\n        },\n        \"subdirectory\": {\n          \"type\": \"string\",\n          \"description\": \"The relative path to the directory where the package is located.\"\n        },\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"optional\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency is optional or not.\"\n        },\n        \"extras\": {\n          \"type\": \"array\",\n          \"description\": \"The required extras for this dependency.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"dependency-options\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"python\": {\n          \"type\": \"string\",\n          \"description\": \"The python versions for which the dependency should be installed.\"\n        },\n        \"platform\": {\n          \"type\": \"string\",\n          \"description\": \"The platform(s) for which the dependency should be installed.\"\n        },\n        \"markers\": {\n          \"type\": \"string\",\n          \"description\": \"The PEP 508 compliant environment markers for which the dependency should be installed.\"\n        },\n        \"allow-prereleases\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether the dependency allows prereleases or not.\"\n        },\n        \"source\": {\n          \"type\": \"string\",\n          \"description\": \"The exclusive source used to search for this dependency.\"\n        },\n        \"develop\": {\n          \"type\": \"boolean\",\n          \"description\": \"Whether to install the dependency in development mode.\"\n        }\n      }\n    },\n    \"multiple-constraints-dependency\": {\n      \"type\": \"array\",\n      \"minItems\": 1,\n      \"items\": {\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/definitions/dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/long-dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/git-dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/file-dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/path-dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/url-dependency\"\n          },\n          {\n            \"$ref\": \"#/definitions/dependency-options\"\n          }\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/poetry/layouts/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.layouts.layout import Layout\nfrom poetry.layouts.src import SrcLayout\n\n\n_LAYOUTS = {\"src\": SrcLayout, \"standard\": Layout}\n\n\ndef layout(name: str) -> type[Layout]:\n    if name not in _LAYOUTS:\n        raise ValueError(\"Invalid layout\")\n\n    return _LAYOUTS[name]\n"
  },
  {
    "path": "src/poetry/layouts/layout.py",
    "content": "from __future__ import annotations\n\nimport importlib.metadata\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.utils.helpers import module_name\nfrom poetry.core.utils.patterns import AUTHOR_REGEX\nfrom tomlkit import inline_table\nfrom tomlkit import loads\nfrom tomlkit import table\nfrom tomlkit.toml_document import TOMLDocument\n\nfrom poetry.factory import Factory\nfrom poetry.pyproject.toml import PyProjectTOML\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    from tomlkit.items import InlineTable\n\n\nPOETRY_DEFAULT = \"\"\"\\\n[project]\nname = \"\"\nversion = \"\"\ndescription = \"\"\nauthors = [\n]\nlicense = {}\nreadme = \"\"\nrequires-python = \"\"\ndependencies = [\n]\n\n[dependency-groups]\ndev = [\n]\n\n[tool.poetry]\npackages = []\n\"\"\"\n\npoetry_core_version = Version.parse(importlib.metadata.version(\"poetry-core\"))\n\nBUILD_SYSTEM_MIN_VERSION: str | None = Version.from_parts(\n    major=poetry_core_version.major,\n    minor=poetry_core_version.minor if poetry_core_version.major == 0 else 0,\n    patch=poetry_core_version.patch\n    if (poetry_core_version.major, poetry_core_version.minor) == (0, 0)\n    else 0,\n).to_string()\nBUILD_SYSTEM_MAX_VERSION: str | None = poetry_core_version.next_breaking().to_string()\n\n\nclass Layout:\n    def __init__(\n        self,\n        project: str,\n        version: str = \"0.1.0\",\n        description: str = \"\",\n        readme_format: str = \"md\",\n        author: str | None = None,\n        license: str | None = None,\n        python: str | None = None,\n        dependencies: Mapping[str, str | Mapping[str, Any]] | None = None,\n        dev_dependencies: Mapping[str, str | Mapping[str, Any]] | None = None,\n    ) -> None:\n        self._project = canonicalize_name(project)\n        self._package_path_relative = Path(\n            *(module_name(part) for part in project.split(\".\"))\n        )\n        self._package_name = \".\".join(self._package_path_relative.parts)\n        self._version = version\n        self._description = description\n\n        self._readme_format = readme_format.lower()\n\n        self._license = license\n        self._python = python\n        self._dependencies = dependencies or {}\n        self._dev_dependencies = dev_dependencies or {}\n\n        if not author:\n            author = \"Your Name <you@example.com>\"\n\n        self._author = author\n\n    @property\n    def basedir(self) -> Path:\n        return Path()\n\n    @property\n    def package_path(self) -> Path:\n        return self.basedir / self._package_path_relative\n\n    def get_package_include(self) -> InlineTable | None:\n        package = inline_table()\n\n        # If a project is created in the root directory (this is reasonable inside a\n        # docker container, eg <https://github.com/python-poetry/poetry/issues/5103>)\n        # then parts will be empty.\n        parts = self._package_path_relative.parts\n        if not parts:\n            return None\n\n        include = parts[0]\n        package.append(\"include\", include)\n\n        if self.basedir != Path():\n            package.append(\"from\", self.basedir.as_posix())\n        else:\n            if module_name(self._project) == include:\n                # package include and package name are the same,\n                # packages table is redundant here.\n                return None\n\n        return package\n\n    def create(\n        self, path: Path, with_tests: bool = True, with_pyproject: bool = True\n    ) -> None:\n        path.mkdir(parents=True, exist_ok=True)\n\n        self._create_default(path)\n        self._create_readme(path)\n\n        if with_tests:\n            self._create_tests(path)\n\n        if with_pyproject:\n            self._write_poetry(path)\n\n    def generate_project_content(\n        self, project_path: Path | None = None\n    ) -> TOMLDocument:\n        template = POETRY_DEFAULT\n\n        content: dict[str, Any] = loads(template)\n\n        project_content = content[\"project\"]\n        project_content[\"name\"] = self._project\n        project_content[\"version\"] = self._version\n        project_content[\"description\"] = self._description\n        m = AUTHOR_REGEX.match(self._author)\n        if m is None:\n            # This should not happen because author has been validated before.\n            raise ValueError(f\"Invalid author: {self._author}\")\n        else:\n            author = {\"name\": m.group(\"name\")}\n            if email := m.group(\"email\"):\n                author[\"email\"] = email\n            project_content[\"authors\"].append(author)\n\n        if self._license:\n            project_content[\"license\"][\"text\"] = self._license\n        else:\n            project_content.remove(\"license\")\n\n        if project_path:\n            project_dir = project_path / f\"README.{self._readme_format}\"\n            abs_path = project_dir.resolve()\n\n            if abs_path.exists():\n                project_content[\"readme\"] = f\"README.{self._readme_format}\"\n            else:\n                project_content.remove(\"readme\")\n\n        if self._python:\n            project_content[\"requires-python\"] = self._python\n        else:\n            project_content.remove(\"requires-python\")\n\n        for dep_name, dep_constraint in self._dependencies.items():\n            dependency = Factory.create_dependency(dep_name, dep_constraint)\n            project_content[\"dependencies\"].append(dependency.to_pep_508())\n\n        poetry_content = content[\"tool\"][\"poetry\"]\n\n        packages = self.get_package_include()\n        if packages:\n            poetry_content[\"packages\"].append(packages)\n        else:\n            poetry_content.remove(\"packages\")\n\n        if self._dev_dependencies:\n            for dep_name, dep_constraint in self._dev_dependencies.items():\n                dependency = Factory.create_dependency(dep_name, dep_constraint)\n                content[\"dependency-groups\"][\"dev\"].append(dependency.to_pep_508())\n        else:\n            del content[\"dependency-groups\"]\n\n        if not poetry_content:\n            del content[\"tool\"][\"poetry\"]\n\n        # Add build system\n        build_system = table()\n        build_system_version = \"\"\n\n        if BUILD_SYSTEM_MIN_VERSION is not None:\n            build_system_version = \">=\" + BUILD_SYSTEM_MIN_VERSION\n        if BUILD_SYSTEM_MAX_VERSION is not None:\n            if build_system_version:\n                build_system_version += \",\"\n            build_system_version += \"<\" + BUILD_SYSTEM_MAX_VERSION\n\n        build_system.add(\"requires\", [\"poetry-core\" + build_system_version])\n        build_system.add(\"build-backend\", \"poetry.core.masonry.api\")\n\n        assert isinstance(content, TOMLDocument)\n        content.add(\"build-system\", build_system)\n\n        return content\n\n    def _create_default(self, path: Path, src: bool = True) -> None:\n        package_path = path / self.package_path\n        package_path.mkdir(parents=True)\n\n        package_init = package_path / \"__init__.py\"\n        package_init.touch()\n\n    def _create_readme(self, path: Path) -> Path:\n        readme_file = path.joinpath(f\"README.{self._readme_format}\")\n        readme_file.touch()\n        return readme_file\n\n    @staticmethod\n    def _create_tests(path: Path) -> None:\n        tests = path / \"tests\"\n        tests.mkdir()\n\n        tests_init = tests / \"__init__.py\"\n        tests_init.touch(exist_ok=False)\n\n    def _write_poetry(self, path: Path) -> None:\n        pyproject = PyProjectTOML(path / \"pyproject.toml\")\n        content = self.generate_project_content()\n        for section, item in content.items():\n            pyproject.data.append(section, item)\n        pyproject.save()\n"
  },
  {
    "path": "src/poetry/layouts/src.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\n\nfrom poetry.layouts.layout import Layout\n\n\nclass SrcLayout(Layout):\n    @property\n    def basedir(self) -> Path:\n        return Path(\"src\")\n"
  },
  {
    "path": "src/poetry/locations.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom pathlib import Path\n\nfrom platformdirs import user_cache_path\nfrom platformdirs import user_config_path\nfrom platformdirs import user_data_path\n\n\n_APP_NAME = \"pypoetry\"\n\nDEFAULT_CACHE_DIR = user_cache_path(_APP_NAME, appauthor=False)\nCONFIG_DIR = Path(\n    os.getenv(\"POETRY_CONFIG_DIR\")\n    or user_config_path(_APP_NAME, appauthor=False, roaming=True)\n)\n\n\ndef data_dir() -> Path:\n    if poetry_home := os.getenv(\"POETRY_HOME\"):\n        return Path(poetry_home).expanduser()\n\n    return user_data_path(_APP_NAME, appauthor=False, roaming=True)\n"
  },
  {
    "path": "src/poetry/masonry/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/masonry/api.py",
    "content": "from __future__ import annotations\n\nfrom poetry.core.masonry.api import build_sdist\nfrom poetry.core.masonry.api import build_wheel\nfrom poetry.core.masonry.api import get_requires_for_build_sdist\nfrom poetry.core.masonry.api import get_requires_for_build_wheel\nfrom poetry.core.masonry.api import prepare_metadata_for_build_wheel\n\n\n__all__ = [\n    \"build_sdist\",\n    \"build_wheel\",\n    \"get_requires_for_build_sdist\",\n    \"get_requires_for_build_wheel\",\n    \"prepare_metadata_for_build_wheel\",\n]\n"
  },
  {
    "path": "src/poetry/masonry/builders/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.core.masonry.builders.sdist import SdistBuilder\nfrom poetry.core.masonry.builders.wheel import WheelBuilder\n\nfrom poetry.masonry.builders.editable import EditableBuilder\n\n\n__all__ = [\"BUILD_FORMATS\", \"EditableBuilder\"]\n\n\n# might be extended by plugins\nBUILD_FORMATS = {\n    \"sdist\": SdistBuilder,\n    \"wheel\": WheelBuilder,\n}\n"
  },
  {
    "path": "src/poetry/masonry/builders/editable.py",
    "content": "from __future__ import annotations\n\nimport csv\nimport hashlib\nimport json\nimport os\n\nfrom base64 import urlsafe_b64encode\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.masonry.builders.builder import Builder\nfrom poetry.core.masonry.builders.sdist import SdistBuilder\nfrom poetry.core.masonry.utils.package_include import PackageInclude\n\nfrom poetry.utils._compat import WINDOWS\nfrom poetry.utils._compat import decode\nfrom poetry.utils._compat import getencoding\nfrom poetry.utils.env import build_environment\nfrom poetry.utils.helpers import is_dir_writable\nfrom poetry.utils.pip import pip_install\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env import Env\n\nSCRIPT_TEMPLATE = \"\"\"\\\n#!{python}\nimport sys\nfrom {module} import {callable_holder}\n\nif __name__ == '__main__':\n    sys.exit({callable_}())\n\"\"\"\n\nWINDOWS_CMD_TEMPLATE = \"\"\"\\\n@echo off\\r\\n\"{python}\" \"%~dp0\\\\{script}\" %*\\r\\n\n\"\"\"\n\n\nclass EditableBuilder(Builder):\n    def __init__(self, poetry: Poetry, env: Env, io: IO) -> None:\n        self._poetry: Poetry\n        super().__init__(poetry)\n\n        self._env = env\n        self._io = io\n\n    def build(self, target_dir: Path | None = None) -> Path:\n        self._debug(\n            f\"  - Building package <c1>{self._package.name}</c1> in\"\n            \" <info>editable</info> mode\"\n        )\n\n        if self._package.build_script:\n            if self._package.build_should_generate_setup():\n                self._debug(\n                    \"  - <warning>Falling back on using a <b>setup.py</b></warning>\"\n                )\n                self._setup_build()\n                return self._path\n\n            self._run_build_script(self._package.build_script)\n\n        for removed in self._env.site_packages.remove_distribution_files(\n            distribution_name=self._package.name\n        ):\n            self._debug(\n                f\"  - Removed <c2>{removed.name}</c2> directory from\"\n                f\" <b>{removed.parent}</b>\"\n            )\n\n        added_files = []\n        added_files += self._add_pth()\n        added_files += self._add_scripts()\n        self._add_dist_info(added_files)\n\n        return self._path\n\n    def _run_build_script(self, build_script: str) -> None:\n        with build_environment(poetry=self._poetry, env=self._env, io=self._io) as env:\n            self._debug(f\"  - Executing build script: <b>{build_script}</b>\")\n            env.run(\"python\", str(self._path.joinpath(build_script)), call=True)\n\n    def _setup_build(self) -> None:\n        builder = SdistBuilder(self._poetry)\n        setup = self._path / \"setup.py\"\n        has_setup = setup.exists()\n\n        if has_setup:\n            self._io.write_error_line(\n                \"<warning>A setup.py file already exists. Using it.</warning>\"\n            )\n        else:\n            with setup.open(\"w\", encoding=\"utf-8\") as f:\n                f.write(decode(builder.build_setup()))\n\n        try:\n            pip_install(self._path, self._env, upgrade=True, editable=True)\n        finally:\n            if not has_setup:\n                os.remove(setup)\n\n    def _add_pth(self) -> list[Path]:\n        paths = {\n            include.base.resolve().as_posix()\n            for include in self._module.includes\n            if isinstance(include, PackageInclude)\n            and (include.is_module() or include.is_package())\n        }\n\n        content = \"\".join(decode(path + os.linesep) for path in paths)\n        pth_file = Path(self._module.name).with_suffix(\".pth\")\n\n        # remove any pre-existing pth files for this package\n        for file in self._env.site_packages.find(path=pth_file, writable_only=True):\n            self._debug(\n                f\"  - Removing existing <c2>{file.name}</c2> from <b>{file.parent}</b>\"\n                f\" for {self._poetry.file.path.parent}\"\n            )\n            file.unlink(missing_ok=True)\n\n        try:\n            pth_file = self._env.site_packages.write_text(\n                pth_file, content, encoding=getencoding()\n            )\n            self._debug(\n                f\"  - Adding <c2>{pth_file.name}</c2> to <b>{pth_file.parent}</b> for\"\n                f\" {self._poetry.file.path.parent}\"\n            )\n            return [pth_file]\n        except PermissionError:\n            self._io.write_error_line(\n                f\"  - Failed to create <c2>{pth_file.name}</c2> for\"\n                f\" {self._poetry.file.path.parent}\"\n            )\n            return []\n\n    def _add_scripts(self) -> list[Path]:\n        added = []\n        entry_points = self.convert_entry_points()\n\n        for scripts_path in self._env.script_dirs:\n            if is_dir_writable(path=scripts_path, create=True):\n                break\n        else:\n            self._io.write_error_line(\n                \"  - Failed to find a suitable script installation directory for\"\n                f\" {self._poetry.file.path.parent}\"\n            )\n            return []\n\n        scripts = entry_points.get(\"console_scripts\", [])\n        for script in scripts:\n            name, script_with_extras = script.split(\" = \")\n            script_without_extras = script_with_extras.split(\"[\")[0]\n            try:\n                module, callable_ = script_without_extras.split(\":\")\n            except ValueError as exc:\n                msg = (\n                    f\"Bad script ({name}): script needs to specify a function within a\"\n                    \" module like: module(.submodule):function\\nInstead got:\"\n                    f\" {script_with_extras}\"\n                )\n                if \"not enough values\" in str(exc):\n                    msg += (\n                        \"\\nHint: If the script depends on module-level code, try\"\n                        \" wrapping it in a main() function and modifying your script\"\n                        f' like:\\n{name} = \"{script_with_extras}:main\"'\n                    )\n                elif \"too many values\" in str(exc):\n                    msg += '\\nToo many \":\" found!'\n\n                raise ValueError(msg)\n\n            callable_holder = callable_.split(\".\", 1)[0]\n\n            script_file = scripts_path.joinpath(name)\n            self._debug(\n                f\"  - Adding the <c2>{name}</c2> script to <b>{scripts_path}</b>\"\n            )\n            with script_file.open(\"w\", encoding=\"utf-8\") as f:\n                f.write(\n                    decode(\n                        SCRIPT_TEMPLATE.format(\n                            python=self._env.python,\n                            module=module,\n                            callable_holder=callable_holder,\n                            callable_=callable_,\n                        )\n                    )\n                )\n\n            script_file.chmod(0o755)\n\n            added.append(script_file)\n\n            if WINDOWS:\n                cmd_script = script_file.with_suffix(\".cmd\")\n                cmd = WINDOWS_CMD_TEMPLATE.format(python=self._env.python, script=name)\n                self._debug(\n                    f\"  - Adding the <c2>{cmd_script.name}</c2> script wrapper to\"\n                    f\" <b>{scripts_path}</b>\"\n                )\n\n                with cmd_script.open(\"w\", encoding=\"utf-8\") as f:\n                    f.write(decode(cmd))\n\n                added.append(cmd_script)\n\n        return added\n\n    def _add_dist_info(self, added_files: list[Path]) -> None:\n        from poetry.core.masonry.builders.wheel import WheelBuilder\n\n        builder = WheelBuilder(self._poetry)\n        dist_info = self._env.site_packages.mkdir(Path(builder.dist_info))\n\n        self._debug(\n            f\"  - Adding the <c2>{dist_info.name}</c2> directory to\"\n            f\" <b>{dist_info.parent}</b>\"\n        )\n\n        builder.prepare_metadata(dist_info.parent)\n        for path in sorted(f for f in dist_info.rglob(\"*\") if f.is_file()):\n            added_files.append(path)\n\n        with dist_info.joinpath(\"INSTALLER\").open(\"w\", encoding=\"utf-8\") as f:\n            f.write(\"poetry\")\n\n        added_files.append(dist_info.joinpath(\"INSTALLER\"))\n\n        # write PEP 610 metadata\n        direct_url_json = dist_info.joinpath(\"direct_url.json\")\n        direct_url_json.write_text(\n            json.dumps(\n                {\n                    \"dir_info\": {\"editable\": True},\n                    \"url\": self._poetry.file.path.parent.absolute().as_uri(),\n                }\n            ),\n            encoding=\"utf-8\",\n        )\n        added_files.append(direct_url_json)\n\n        record = dist_info.joinpath(\"RECORD\")\n        with record.open(\"w\", encoding=\"utf-8\", newline=\"\") as f:\n            csv_writer = csv.writer(f)\n            for path in added_files:\n                hash = self._get_file_hash(path)\n                size = path.stat().st_size\n                csv_writer.writerow((path, f\"sha256={hash}\", size))\n\n            # RECORD itself is recorded with no hash or size\n            csv_writer.writerow((record, \"\", \"\"))\n\n    def _get_file_hash(self, filepath: Path) -> str:\n        hashsum = hashlib.sha256()\n        with filepath.open(\"rb\") as src:\n            while True:\n                buf = src.read(1024 * 8)\n                if not buf:\n                    break\n                hashsum.update(buf)\n\n            src.seek(0)\n\n        return urlsafe_b64encode(hashsum.digest()).decode(\"ascii\").rstrip(\"=\")\n\n    def _debug(self, msg: str) -> None:\n        if self._io.is_debug():\n            self._io.write_line(msg)\n"
  },
  {
    "path": "src/poetry/mixology/__init__.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.mixology.version_solver import VersionSolver\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.mixology.result import SolverResult\n    from poetry.puzzle.provider import Provider\n\n\ndef resolve_version(root: ProjectPackage, provider: Provider) -> SolverResult:\n    solver = VersionSolver(root, provider)\n\n    return solver.solve()\n"
  },
  {
    "path": "src/poetry/mixology/assignment.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.mixology.term import Term\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n    from poetry.mixology.incompatibility import Incompatibility\n\n\nclass Assignment(Term):\n    \"\"\"\n    A term in a PartialSolution that tracks some additional metadata.\n    \"\"\"\n\n    def __init__(\n        self,\n        dependency: Dependency,\n        is_positive: bool,\n        decision_level: int,\n        index: int,\n        cause: Incompatibility | None = None,\n    ) -> None:\n        super().__init__(dependency, is_positive)\n\n        self._decision_level = decision_level\n        self._index = index\n        self._cause = cause\n\n    @property\n    def decision_level(self) -> int:\n        return self._decision_level\n\n    @property\n    def index(self) -> int:\n        return self._index\n\n    @property\n    def cause(self) -> Incompatibility | None:\n        return self._cause\n\n    @classmethod\n    def decision(cls, package: Package, decision_level: int, index: int) -> Assignment:\n        return cls(package.to_dependency(), True, decision_level, index)\n\n    @classmethod\n    def derivation(\n        cls,\n        dependency: Dependency,\n        is_positive: bool,\n        cause: Incompatibility,\n        decision_level: int,\n        index: int,\n    ) -> Assignment:\n        return cls(dependency, is_positive, decision_level, index, cause)\n\n    def is_decision(self) -> bool:\n        return self._cause is None\n"
  },
  {
    "path": "src/poetry/mixology/failure.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.constraints.version import parse_constraint\n\nfrom poetry.mixology.incompatibility_cause import ConflictCauseError\nfrom poetry.mixology.incompatibility_cause import PythonCauseError\n\n\nif TYPE_CHECKING:\n    from poetry.mixology.incompatibility import Incompatibility\n\n\nclass SolveFailureError(Exception):\n    def __init__(self, incompatibility: Incompatibility) -> None:\n        self._incompatibility = incompatibility\n\n    @property\n    def message(self) -> str:\n        return str(self)\n\n    def __str__(self) -> str:\n        return _Writer(self._incompatibility).write()\n\n\nclass _Writer:\n    def __init__(self, root: Incompatibility) -> None:\n        self._root = root\n        self._derivations: dict[Incompatibility, int] = {}\n        self._lines: list[tuple[str, int | None]] = []\n        self._line_numbers: dict[Incompatibility, int] = {}\n\n        self._count_derivations(self._root)\n\n    def write(self) -> str:\n        buffer = []\n        version_solutions = []\n        required_python_version_notification = False\n        for incompatibility in self._root.external_incompatibilities:\n            if isinstance(incompatibility.cause, PythonCauseError):\n                root_constraint = parse_constraint(\n                    incompatibility.cause.root_python_version\n                )\n                constraint = parse_constraint(incompatibility.cause.python_version)\n\n                version_solutions.append(\n                    \"For <fg=default;options=bold>\"\n                    f\"{incompatibility.terms[0].dependency.name}</>,\"\n                    \" a possible solution would be to set the\"\n                    \" `<fg=default;options=bold>python</>` property to\"\n                    f' <fg=yellow>\"{root_constraint.intersect(constraint)}\"</>'\n                )\n                if not required_python_version_notification:\n                    buffer.append(\n                        \"The current project's supported Python range\"\n                        f\" ({incompatibility.cause.root_python_version}) is not\"\n                        \" compatible with some of the required packages Python\"\n                        \" requirement:\"\n                    )\n                    required_python_version_notification = True\n\n                root_constraint = parse_constraint(\n                    incompatibility.cause.root_python_version\n                )\n                constraint = parse_constraint(incompatibility.cause.python_version)\n                buffer.append(\n                    f\"  - {incompatibility.terms[0].dependency.name} requires Python\"\n                    f\" {incompatibility.cause.python_version}, so it will not be\"\n                    f\" installable for Python {root_constraint.difference(constraint)}\"\n                )\n\n        if required_python_version_notification:\n            buffer.append(\"\")\n\n        if isinstance(self._root.cause, ConflictCauseError):\n            self._visit(self._root)\n        else:\n            self._write(self._root, f\"Because {self._root}, version solving failed.\")\n\n        padding = (\n            0\n            if not self._line_numbers\n            else len(f\"({list(self._line_numbers.values())[-1]}) \")\n        )\n\n        last_was_empty = False\n        for line in self._lines:\n            message = line[0]\n            if not message:\n                if not last_was_empty:\n                    buffer.append(\"\")\n\n                last_was_empty = True\n                continue\n\n            last_was_empty = False\n\n            number = line[-1]\n            if number is not None:\n                message = f\"({number})\".ljust(padding) + message\n            else:\n                message = \" \" * padding + message\n\n            buffer.append(message)\n        if required_python_version_notification:\n            # Add suggested solution\n            links = \",\".join(\n                f\"\\n    <fg=blue>https://python-poetry.org/docs/dependency-specification/#{section}</>\"\n                for section in [\n                    \"python-restricted-dependencies\",\n                    \"using-environment-markers\",\n                ]\n            )\n\n            description = (\n                \"The Python requirement can be specified via the\"\n                \" `<fg=default;options=bold>python</>` or\"\n                \" `<fg=default;options=bold>markers</>` properties\"\n            )\n            if version_solutions:\n                description += \"\\n\\n    \" + \"\\n\".join(version_solutions)\n\n            description = description.strip(\" \")\n\n            buffer.append(\n                f\"\\n  <fg=blue;options=bold>* </>\"\n                f\"<fg=default;options=bold>Check your dependencies Python requirement</>:\"\n                f\" {description}\\n{links}\\n\",\n            )\n        return \"\\n\".join(buffer)\n\n    def _write(\n        self, incompatibility: Incompatibility, message: str, numbered: bool = False\n    ) -> None:\n        if numbered:\n            number = len(self._line_numbers) + 1\n            self._line_numbers[incompatibility] = number\n            self._lines.append((message, number))\n        else:\n            self._lines.append((message, None))\n\n    def _visit(\n        self,\n        incompatibility: Incompatibility,\n        conclusion: bool = False,\n    ) -> None:\n        numbered = conclusion or self._derivations[incompatibility] > 1\n        conjunction = \"So,\" if conclusion or incompatibility == self._root else \"And\"\n        incompatibility_string = str(incompatibility)\n\n        cause = incompatibility.cause\n        assert isinstance(cause, ConflictCauseError)\n\n        if isinstance(cause.conflict.cause, ConflictCauseError) and isinstance(\n            cause.other.cause, ConflictCauseError\n        ):\n            conflict_line = self._line_numbers.get(cause.conflict)\n            other_line = self._line_numbers.get(cause.other)\n\n            if conflict_line is not None and other_line is not None:\n                reason = cause.conflict.and_to_string(\n                    cause.other, conflict_line, other_line\n                )\n                self._write(\n                    incompatibility,\n                    f\"Because {reason}, {incompatibility_string}.\",\n                    numbered=numbered,\n                )\n            elif conflict_line is not None or other_line is not None:\n                if conflict_line is not None:\n                    with_line = cause.conflict\n                    without_line = cause.other\n                    line = conflict_line\n                elif other_line is not None:\n                    with_line = cause.other\n                    without_line = cause.conflict\n                    line = other_line\n\n                self._visit(without_line)\n                self._write(\n                    incompatibility,\n                    f\"{conjunction} because {with_line!s} ({line}),\"\n                    f\" {incompatibility_string}.\",\n                    numbered=numbered,\n                )\n            else:\n                single_line_conflict = self._is_single_line(cause.conflict.cause)\n                single_line_other = self._is_single_line(cause.other.cause)\n\n                if single_line_other or single_line_conflict:\n                    first = cause.conflict if single_line_other else cause.other\n                    second = cause.other if single_line_other else cause.conflict\n                    self._visit(first)\n                    self._visit(second)\n                    self._write(\n                        incompatibility,\n                        f\"Thus, {incompatibility_string}.\",\n                        numbered=numbered,\n                    )\n                else:\n                    self._visit(cause.conflict, conclusion=True)\n                    self._lines.append((\"\", None))\n\n                    self._visit(cause.other)\n\n                    self._write(\n                        incompatibility,\n                        f\"{conjunction} because {cause.conflict!s}\"\n                        f\" ({self._line_numbers[cause.conflict]}),\"\n                        f\" {incompatibility_string}\",\n                        numbered=numbered,\n                    )\n        elif isinstance(cause.conflict.cause, ConflictCauseError) or isinstance(\n            cause.other.cause, ConflictCauseError\n        ):\n            derived = (\n                cause.conflict\n                if isinstance(cause.conflict.cause, ConflictCauseError)\n                else cause.other\n            )\n            ext = (\n                cause.other\n                if isinstance(cause.conflict.cause, ConflictCauseError)\n                else cause.conflict\n            )\n\n            derived_line = self._line_numbers.get(derived)\n            if derived_line is not None:\n                reason = ext.and_to_string(derived, None, derived_line)\n                self._write(\n                    incompatibility,\n                    f\"Because {reason}, {incompatibility_string}.\",\n                    numbered=numbered,\n                )\n            elif self._is_collapsible(derived):\n                derived_cause = derived.cause\n                assert isinstance(derived_cause, ConflictCauseError)\n                if isinstance(derived_cause.conflict.cause, ConflictCauseError):\n                    collapsed_derived = derived_cause.conflict\n                    collapsed_ext = derived_cause.other\n                else:\n                    collapsed_derived = derived_cause.other\n\n                    collapsed_ext = derived_cause.conflict\n\n                self._visit(collapsed_derived)\n                reason = collapsed_ext.and_to_string(ext, None, None)\n                self._write(\n                    incompatibility,\n                    f\"{conjunction} because {reason}, {incompatibility_string}.\",\n                    numbered=numbered,\n                )\n            else:\n                self._visit(derived)\n                self._write(\n                    incompatibility,\n                    f\"{conjunction} because {ext!s}, {incompatibility_string}.\",\n                    numbered=numbered,\n                )\n        else:\n            reason = cause.conflict.and_to_string(cause.other, None, None)\n            self._write(\n                incompatibility,\n                f\"Because {reason}, {incompatibility_string}.\",\n                numbered=numbered,\n            )\n\n    def _is_collapsible(self, incompatibility: Incompatibility) -> bool:\n        if self._derivations[incompatibility] > 1:\n            return False\n\n        cause = incompatibility.cause\n        assert isinstance(cause, ConflictCauseError)\n        if isinstance(cause.conflict.cause, ConflictCauseError) and isinstance(\n            cause.other.cause, ConflictCauseError\n        ):\n            return False\n\n        if not isinstance(cause.conflict.cause, ConflictCauseError) and not isinstance(\n            cause.other.cause, ConflictCauseError\n        ):\n            return False\n\n        complex = (\n            cause.conflict\n            if isinstance(cause.conflict.cause, ConflictCauseError)\n            else cause.other\n        )\n\n        return complex not in self._line_numbers\n\n    def _is_single_line(self, cause: ConflictCauseError) -> bool:\n        return not isinstance(\n            cause.conflict.cause, ConflictCauseError\n        ) and not isinstance(cause.other.cause, ConflictCauseError)\n\n    def _count_derivations(self, incompatibility: Incompatibility) -> None:\n        if incompatibility in self._derivations:\n            self._derivations[incompatibility] += 1\n        else:\n            self._derivations[incompatibility] = 1\n            cause = incompatibility.cause\n            if isinstance(cause, ConflictCauseError):\n                self._count_derivations(cause.conflict)\n                self._count_derivations(cause.other)\n"
  },
  {
    "path": "src/poetry/mixology/incompatibility.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.mixology.incompatibility_cause import ConflictCauseError\nfrom poetry.mixology.incompatibility_cause import DependencyCauseError\nfrom poetry.mixology.incompatibility_cause import NoVersionsCauseError\nfrom poetry.mixology.incompatibility_cause import PlatformCauseError\nfrom poetry.mixology.incompatibility_cause import PythonCauseError\nfrom poetry.mixology.incompatibility_cause import RootCauseError\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n\n    from poetry.mixology.incompatibility_cause import IncompatibilityCauseError\n    from poetry.mixology.term import Term\n\n\nclass Incompatibility:\n    def __init__(self, terms: list[Term], cause: IncompatibilityCauseError) -> None:\n        # Remove the root package from generated incompatibilities, since it will\n        # always be satisfied. This makes error reporting clearer, and may also\n        # make solving more efficient.\n        if (\n            len(terms) != 1\n            and isinstance(cause, ConflictCauseError)\n            and any(term.is_positive() and term.dependency.is_root for term in terms)\n        ):\n            terms = [\n                term\n                for term in terms\n                if not term.is_positive() or not term.dependency.is_root\n            ]\n\n        if len(terms) != 1 and (\n            # Short-circuit in the common case of a two-term incompatibility with\n            # two different packages (for example, a dependency).\n            len(terms) != 2\n            or terms[0].dependency.complete_name == terms[-1].dependency.complete_name\n        ):\n            # Coalesce multiple terms about the same package if possible.\n            by_name: dict[str, dict[str, Term]] = {}\n            for term in terms:\n                by_ref = by_name.setdefault(term.dependency.complete_name, {})\n                ref = term.dependency.complete_name\n\n                if ref in by_ref:\n                    value = by_ref[ref].intersect(term)\n\n                    # If we have two terms that refer to the same package but have a\n                    # null intersection, they're mutually exclusive, making this\n                    # incompatibility irrelevant, since we already know that mutually\n                    # exclusive version ranges are incompatible. We should never derive\n                    # an irrelevant incompatibility.\n                    err_msg = f\"Package '{ref}' is listed as a dependency of itself.\"\n                    assert value is not None, err_msg\n                    by_ref[ref] = value\n                else:\n                    by_ref[ref] = term\n\n            new_terms = []\n            for by_ref in by_name.values():\n                positive_terms = [\n                    term for term in by_ref.values() if term.is_positive()\n                ]\n                if positive_terms:\n                    new_terms += positive_terms\n                    continue\n\n                new_terms += list(by_ref.values())\n\n            terms = new_terms\n\n        self._terms = terms\n        self._cause = cause\n\n    @property\n    def terms(self) -> list[Term]:\n        return self._terms\n\n    @property\n    def cause(self) -> IncompatibilityCauseError:\n        return self._cause\n\n    @property\n    def external_incompatibilities(\n        self,\n    ) -> Iterator[Incompatibility]:\n        \"\"\"\n        Returns all external incompatibilities in this incompatibility's\n        derivation graph.\n        \"\"\"\n        if isinstance(self._cause, ConflictCauseError):\n            cause: ConflictCauseError = self._cause\n            yield from cause.conflict.external_incompatibilities\n\n            yield from cause.other.external_incompatibilities\n        else:\n            yield self\n\n    def is_failure(self) -> bool:\n        return len(self._terms) == 0 or (\n            len(self._terms) == 1 and self._terms[0].dependency.is_root\n        )\n\n    def __str__(self) -> str:\n        if isinstance(self._cause, DependencyCauseError):\n            assert len(self._terms) == 2\n\n            depender = self._terms[0]\n            dependee = self._terms[1]\n            assert depender.is_positive()\n            assert not dependee.is_positive()\n\n            return (\n                f\"{self._terse(depender, allow_every=True)} depends on\"\n                f\" {self._terse(dependee)}\"\n            )\n        elif isinstance(self._cause, PythonCauseError):\n            assert len(self._terms) == 1\n            assert self._terms[0].is_positive()\n\n            text = f\"{self._terse(self._terms[0], allow_every=True)} requires \"\n            text += f\"Python {self._cause.python_version}\"\n\n            return text\n        elif isinstance(self._cause, PlatformCauseError):\n            assert len(self._terms) == 1\n            assert self._terms[0].is_positive()\n\n            text = f\"{self._terse(self._terms[0], allow_every=True)} requires \"\n            text += f\"platform {self._cause.platform}\"\n\n            return text\n        elif isinstance(self._cause, NoVersionsCauseError):\n            assert len(self._terms) == 1\n            assert self._terms[0].is_positive()\n\n            return (\n                f\"no versions of {self._terms[0].dependency.name} match\"\n                f\" {self._terms[0].constraint}\"\n            )\n        elif isinstance(self._cause, RootCauseError):\n            assert len(self._terms) == 1\n            assert not self._terms[0].is_positive()\n            assert self._terms[0].dependency.is_root\n\n            return (\n                f\"{self._terms[0].dependency.name} is\"\n                f\" {self._terms[0].dependency.constraint}\"\n            )\n        elif self.is_failure():\n            return \"version solving failed\"\n\n        if len(self._terms) == 1:\n            term = self._terms[0]\n            verb = \"forbidden\" if term.is_positive() else \"required\"\n            return f\"{term.dependency.name} is {verb}\"\n\n        if len(self._terms) == 2:\n            term1 = self._terms[0]\n            term2 = self._terms[1]\n\n            if term1.is_positive() == term2.is_positive():\n                if not term1.is_positive():\n                    return f\"either {self._terse(term1)} or {self._terse(term2)}\"\n\n                package1 = (\n                    term1.dependency.name\n                    if term1.constraint.is_any()\n                    else self._terse(term1)\n                )\n                package2 = (\n                    term2.dependency.name\n                    if term2.constraint.is_any()\n                    else self._terse(term2)\n                )\n\n                return f\"{package1} is incompatible with {package2}\"\n\n        positive = []\n        negative = []\n\n        for term in self._terms:\n            if term.is_positive():\n                positive.append(self._terse(term))\n            else:\n                negative.append(self._terse(term))\n\n        if positive and negative:\n            if len(positive) != 1:\n                return f\"if {' and '.join(positive)} then {' or '.join(negative)}\"\n\n            positive_term = next(term for term in self._terms if term.is_positive())\n            return (\n                f\"{self._terse(positive_term, allow_every=True)} requires\"\n                f\" {' or '.join(negative)}\"\n            )\n        elif positive:\n            return f\"one of {' or '.join(positive)} must be false\"\n        else:\n            return f\"one of {' or '.join(negative)} must be true\"\n\n    def and_to_string(\n        self,\n        other: Incompatibility,\n        this_line: int | None,\n        other_line: int | None,\n    ) -> str:\n        requires_both = self._try_requires_both(other, this_line, other_line)\n        if requires_both is not None:\n            return requires_both\n\n        requires_through = self._try_requires_through(other, this_line, other_line)\n        if requires_through is not None:\n            return requires_through\n\n        requires_forbidden = self._try_requires_forbidden(other, this_line, other_line)\n        if requires_forbidden is not None:\n            return requires_forbidden\n\n        buffer = [str(self)]\n        if this_line is not None:\n            buffer.append(f\" {this_line!s}\")\n\n        buffer.append(f\" and {other!s}\")\n\n        if other_line is not None:\n            buffer.append(f\" {other_line!s}\")\n\n        return \"\\n\".join(buffer)\n\n    def _try_requires_both(\n        self,\n        other: Incompatibility,\n        this_line: int | None,\n        other_line: int | None,\n    ) -> str | None:\n        if len(self._terms) == 1 or len(other.terms) == 1:\n            return None\n\n        this_positive = self._single_term_where(lambda term: term.is_positive())\n        if this_positive is None:\n            return None\n\n        other_positive = other._single_term_where(lambda term: term.is_positive())\n        if other_positive is None:\n            return None\n\n        if this_positive.dependency != other_positive.dependency:\n            return None\n\n        this_negatives = \" or \".join(\n            [self._terse(term) for term in self._terms if not term.is_positive()]\n        )\n\n        other_negatives = \" or \".join(\n            [self._terse(term) for term in other.terms if not term.is_positive()]\n        )\n\n        buffer = [self._terse(this_positive, allow_every=True) + \" \"]\n        is_dependency = isinstance(self.cause, DependencyCauseError) and isinstance(\n            other.cause, DependencyCauseError\n        )\n\n        if is_dependency:\n            buffer.append(\"depends on\")\n        else:\n            buffer.append(\"requires\")\n\n        buffer.append(f\" both {this_negatives}\")\n        if this_line is not None:\n            buffer.append(f\" ({this_line})\")\n\n        buffer.append(f\" and {other_negatives}\")\n\n        if other_line is not None:\n            buffer.append(f\" ({other_line})\")\n\n        return \"\".join(buffer)\n\n    def _try_requires_through(\n        self,\n        other: Incompatibility,\n        this_line: int | None,\n        other_line: int | None,\n    ) -> str | None:\n        if len(self._terms) == 1 or len(other.terms) == 1:\n            return None\n\n        this_negative = self._single_term_where(lambda term: not term.is_positive())\n        other_negative = other._single_term_where(lambda term: not term.is_positive())\n\n        if this_negative is None and other_negative is None:\n            return None\n\n        this_positive = self._single_term_where(lambda term: term.is_positive())\n        other_positive = self._single_term_where(lambda term: term.is_positive())\n\n        if (\n            this_negative is not None\n            and other_positive is not None\n            and this_negative.dependency.name == other_positive.dependency.name\n            and this_negative.inverse.satisfies(other_positive)\n        ):\n            prior = self\n            prior_negative = this_negative\n            prior_line = this_line\n            latter = other\n            latter_line = other_line\n        elif (\n            other_negative is not None\n            and this_positive is not None\n            and other_negative.dependency.name == this_positive.dependency.name\n            and other_negative.inverse.satisfies(this_positive)\n        ):\n            prior = other\n            prior_negative = other_negative\n            prior_line = other_line\n            latter = self\n            latter_line = this_line\n        else:\n            return None\n\n        prior_positives = [term for term in prior.terms if term.is_positive()]\n\n        buffer = []\n        if len(prior_positives) > 1:\n            prior_string = \" or \".join([self._terse(term) for term in prior_positives])\n            buffer.append(f\"if {prior_string} then \")\n        else:\n            if isinstance(prior.cause, DependencyCauseError):\n                verb = \"depends on\"\n            else:\n                verb = \"requires\"\n\n            buffer.append(\n                f\"{self._terse(prior_positives[0], allow_every=True)} {verb} \"\n            )\n\n        buffer.append(self._terse(prior_negative))\n        if prior_line is not None:\n            buffer.append(f\" ({prior_line})\")\n\n        buffer.append(\" which \")\n\n        if isinstance(latter.cause, DependencyCauseError):\n            buffer.append(\"depends on \")\n        else:\n            buffer.append(\"requires \")\n\n        buffer.append(\n            \" or \".join(\n                [self._terse(term) for term in latter.terms if not term.is_positive()]\n            )\n        )\n\n        if latter_line is not None:\n            buffer.append(f\" ({latter_line})\")\n\n        return \"\".join(buffer)\n\n    def _try_requires_forbidden(\n        self,\n        other: Incompatibility,\n        this_line: int | None,\n        other_line: int | None,\n    ) -> str | None:\n        if len(self._terms) != 1 and len(other.terms) != 1:\n            return None\n\n        if len(self.terms) == 1:\n            prior = other\n            latter = self\n            prior_line = other_line\n            latter_line = this_line\n        else:\n            prior = self\n            latter = other\n            prior_line = this_line\n            latter_line = other_line\n\n        negative = prior._single_term_where(lambda term: not term.is_positive())\n        if negative is None:\n            return None\n\n        if not negative.inverse.satisfies(latter.terms[0]):\n            return None\n\n        positives = [t for t in prior.terms if t.is_positive()]\n\n        buffer = []\n        if len(positives) > 1:\n            prior_string = \" or \".join([self._terse(term) for term in positives])\n            buffer.append(f\"if {prior_string} then \")\n        else:\n            buffer.append(self._terse(positives[0], allow_every=True))\n            if isinstance(prior.cause, DependencyCauseError):\n                buffer.append(\" depends on \")\n            else:\n                buffer.append(\" requires \")\n\n        buffer.append(self._terse(latter.terms[0]) + \" \")\n        if prior_line is not None:\n            buffer.append(f\"({prior_line}) \")\n\n        if isinstance(latter.cause, PythonCauseError):\n            cause: PythonCauseError = latter.cause\n            buffer.append(f\"which requires Python {cause.python_version}\")\n        elif isinstance(latter.cause, NoVersionsCauseError):\n            buffer.append(\"which doesn't match any versions\")\n        else:\n            buffer.append(\"which is forbidden\")\n\n        if latter_line is not None:\n            buffer.append(f\" ({latter_line})\")\n\n        return \"\".join(buffer)\n\n    def _terse(self, term: Term, allow_every: bool = False) -> str:\n        if allow_every and term.constraint.is_any():\n            return f\"every version of {term.dependency.complete_name}\"\n\n        if term.dependency.is_root:\n            pretty_name: str = term.dependency.pretty_name\n            return pretty_name\n\n        if term.dependency.source_type:\n            return str(term.dependency)\n        pretty_name = term.dependency.complete_pretty_name\n        return f\"{pretty_name} ({term.dependency.pretty_constraint})\"\n\n    def _single_term_where(self, callable: Callable[[Term], bool]) -> Term | None:\n        found = None\n        for term in self._terms:\n            if not callable(term):\n                continue\n\n            if found is not None:\n                return None\n\n            found = term\n\n        return found\n\n    def __repr__(self) -> str:\n        return f\"<Incompatibility {self!s}>\"\n"
  },
  {
    "path": "src/poetry/mixology/incompatibility_cause.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.mixology.incompatibility import Incompatibility\n\n\nclass IncompatibilityCauseError(Exception):\n    \"\"\"\n    The reason and Incompatibility's terms are incompatible.\n    \"\"\"\n\n\nclass RootCauseError(IncompatibilityCauseError):\n    pass\n\n\nclass NoVersionsCauseError(IncompatibilityCauseError):\n    pass\n\n\nclass DependencyCauseError(IncompatibilityCauseError):\n    pass\n\n\nclass ConflictCauseError(IncompatibilityCauseError):\n    \"\"\"\n    The incompatibility was derived from two existing incompatibilities\n    during conflict resolution.\n    \"\"\"\n\n    def __init__(self, conflict: Incompatibility, other: Incompatibility) -> None:\n        self._conflict = conflict\n        self._other = other\n\n    @property\n    def conflict(self) -> Incompatibility:\n        return self._conflict\n\n    @property\n    def other(self) -> Incompatibility:\n        return self._other\n\n    def __str__(self) -> str:\n        return str(self._conflict)\n\n\nclass PythonCauseError(IncompatibilityCauseError):\n    \"\"\"\n    The incompatibility represents a package's python constraint\n    (Python versions) being incompatible\n    with the current python version.\n    \"\"\"\n\n    def __init__(self, python_version: str, root_python_version: str) -> None:\n        self._python_version = python_version\n        self._root_python_version = root_python_version\n\n    @property\n    def python_version(self) -> str:\n        return self._python_version\n\n    @property\n    def root_python_version(self) -> str:\n        return self._root_python_version\n\n\nclass PlatformCauseError(IncompatibilityCauseError):\n    \"\"\"\n    The incompatibility represents a package's platform constraint\n    (OS most likely) being incompatible with the current platform.\n    \"\"\"\n\n    def __init__(self, platform: str) -> None:\n        self._platform = platform\n\n    @property\n    def platform(self) -> str:\n        return self._platform\n"
  },
  {
    "path": "src/poetry/mixology/partial_solution.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.mixology.assignment import Assignment\nfrom poetry.mixology.set_relation import SetRelation\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n    from poetry.mixology.incompatibility import Incompatibility\n    from poetry.mixology.term import Term\n\n\nclass PartialSolution:\n    \"\"\"\n    # A list of Assignments that represent the solver's current best guess about\n    # what's true for the eventual set of package versions that will comprise the\n    # total solution.\n    #\n    # See:\n    # https://github.com/dart-lang/mixology/tree/master/doc/solver.md#partial-solution.\n    \"\"\"\n\n    def __init__(self) -> None:\n        # The assignments that have been made so far, in the order they were\n        # assigned.\n        self._assignments: list[Assignment] = []\n\n        # The decisions made for each package.\n        self._decisions: dict[str, Package] = {}\n\n        # The intersection of all positive Assignments for each package, minus any\n        # negative Assignments that refer to that package.\n        #\n        # This is derived from self._assignments.\n        self._positive: dict[str, Term] = {}\n\n        # The union of all negative Assignments for each package.\n        #\n        # If a package has any positive Assignments, it doesn't appear in this\n        # map.\n        #\n        # This is derived from self._assignments.\n        self._negative: dict[str, Term] = {}\n\n        # The number of distinct solutions that have been attempted so far.\n        self._attempted_solutions = 1\n\n        # Whether the solver is currently backtracking.\n        self._backtracking = False\n\n    @property\n    def decisions(self) -> list[Package]:\n        return list(self._decisions.values())\n\n    @property\n    def decision_level(self) -> int:\n        return len(self._decisions)\n\n    @property\n    def attempted_solutions(self) -> int:\n        return self._attempted_solutions\n\n    @property\n    def unsatisfied(self) -> list[Dependency]:\n        return [\n            term.dependency\n            for term in self._positive.values()\n            if term.dependency.complete_name not in self._decisions\n        ]\n\n    def decide(self, package: Package) -> None:\n        \"\"\"\n        Adds an assignment of package as a decision\n        and increments the decision level.\n        \"\"\"\n        # When we make a new decision after backtracking, count an additional\n        # attempted solution. If we backtrack multiple times in a row, though, we\n        # only want to count one, since we haven't actually started attempting a\n        # new solution.\n        if self._backtracking:\n            self._attempted_solutions += 1\n\n        self._backtracking = False\n        self._decisions[package.complete_name] = package\n\n        self._assign(\n            Assignment.decision(package, self.decision_level, len(self._assignments))\n        )\n\n    def derive(\n        self, dependency: Dependency, is_positive: bool, cause: Incompatibility\n    ) -> None:\n        \"\"\"\n        Adds an assignment of package as a derivation.\n        \"\"\"\n        self._assign(\n            Assignment.derivation(\n                dependency,\n                is_positive,\n                cause,\n                self.decision_level,\n                len(self._assignments),\n            )\n        )\n\n    def _assign(self, assignment: Assignment) -> None:\n        \"\"\"\n        Adds an Assignment to _assignments and _positive or _negative.\n        \"\"\"\n        self._assignments.append(assignment)\n        self._register(assignment)\n\n    def backtrack(self, decision_level: int) -> None:\n        \"\"\"\n        Resets the current decision level to decision_level, and removes all\n        assignments made after that level.\n        \"\"\"\n        self._backtracking = True\n\n        packages = set()\n        while self._assignments[-1].decision_level > decision_level:\n            removed = self._assignments.pop(-1)\n            packages.add(removed.dependency.complete_name)\n            if removed.is_decision():\n                del self._decisions[removed.dependency.complete_name]\n\n        # Re-compute _positive and _negative for the packages that were removed.\n        for package in packages:\n            if package in self._positive:\n                del self._positive[package]\n\n            if package in self._negative:\n                del self._negative[package]\n\n        for assignment in self._assignments:\n            if assignment.dependency.complete_name in packages:\n                self._register(assignment)\n\n    def _register(self, assignment: Assignment) -> None:\n        \"\"\"\n        Registers an Assignment in _positive or _negative.\n        \"\"\"\n        name = assignment.dependency.complete_name\n        old_positive = self._positive.get(name)\n        if old_positive is not None:\n            value = old_positive.intersect(assignment)\n            assert value is not None\n            self._positive[name] = value\n\n            return\n\n        old_negative = self._negative.get(name)\n        term = (\n            assignment if old_negative is None else assignment.intersect(old_negative)\n        )\n        assert term is not None\n\n        if term.is_positive():\n            if name in self._negative:\n                del self._negative[name]\n\n            self._positive[name] = term\n        else:\n            self._negative[name] = term\n\n    def satisfier(self, term: Term) -> Assignment:\n        \"\"\"\n        Returns the first Assignment in this solution such that the sublist of\n        assignments up to and including that entry collectively satisfies term.\n        \"\"\"\n        assigned_term: Term | None = None\n\n        for assignment in self._assignments:\n            if assignment.dependency.complete_name != term.dependency.complete_name:\n                continue\n\n            if (\n                not assignment.dependency.is_root\n                and not assignment.dependency.is_same_package_as(term.dependency)\n            ):\n                if not assignment.is_positive():\n                    continue\n\n                assert not term.is_positive()\n\n                return assignment\n\n            if assigned_term is None:\n                assigned_term = assignment\n            else:\n                assigned_term = assigned_term.intersect(assignment)\n\n            # As soon as we have enough assignments to satisfy term, return them.\n            assert assigned_term is not None\n            if assigned_term.satisfies(term):\n                return assignment\n\n        raise RuntimeError(f\"[BUG] {term} is not satisfied.\")\n\n    def satisfies(self, term: Term) -> bool:\n        return self.relation(term) == SetRelation.SUBSET\n\n    def relation(self, term: Term) -> str:\n        positive = self._positive.get(term.dependency.complete_name)\n        if positive is not None:\n            return positive.relation(term)\n\n        negative = self._negative.get(term.dependency.complete_name)\n        if negative is None:\n            return SetRelation.OVERLAPPING\n\n        return negative.relation(term)\n"
  },
  {
    "path": "src/poetry/mixology/result.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.project_package import ProjectPackage\n\n\nclass SolverResult:\n    def __init__(\n        self,\n        root: ProjectPackage,\n        packages: list[Package],\n        attempted_solutions: int,\n    ) -> None:\n        self._root = root\n        self._packages = packages\n        self._attempted_solutions = attempted_solutions\n\n    @property\n    def packages(self) -> list[Package]:\n        return self._packages\n\n    @property\n    def attempted_solutions(self) -> int:\n        return self._attempted_solutions\n"
  },
  {
    "path": "src/poetry/mixology/set_relation.py",
    "content": "from __future__ import annotations\n\n\nclass SetRelation:\n    \"\"\"\n    An enum of possible relationships between two sets.\n    \"\"\"\n\n    SUBSET = \"subset\"\n\n    DISJOINT = \"disjoint\"\n\n    OVERLAPPING = \"overlapping\"\n"
  },
  {
    "path": "src/poetry/mixology/term.py",
    "content": "from __future__ import annotations\n\nimport functools\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.mixology.set_relation import SetRelation\n\n\nif TYPE_CHECKING:\n    from poetry.core.constraints.version import VersionConstraint\n    from poetry.core.packages.dependency import Dependency\n\n\nclass Term:\n    \"\"\"\n    A statement about a package which is true or false for a given selection of\n    package versions.\n\n    See https://github.com/dart-lang/pub/tree/master/doc/solver.md#term.\n    \"\"\"\n\n    def __init__(self, dependency: Dependency, is_positive: bool) -> None:\n        self._dependency = dependency\n        self._positive = is_positive\n        self.relation = functools.lru_cache(maxsize=None)(self._relation)\n        self.intersect = functools.lru_cache(maxsize=None)(self._intersect)\n\n    @property\n    def inverse(self) -> Term:\n        return Term(self._dependency, not self.is_positive())\n\n    @property\n    def dependency(self) -> Dependency:\n        return self._dependency\n\n    @property\n    def constraint(self) -> VersionConstraint:\n        return self._dependency.constraint\n\n    def is_positive(self) -> bool:\n        return self._positive\n\n    def satisfies(self, other: Term) -> bool:\n        \"\"\"\n        Returns whether this term satisfies another.\n        \"\"\"\n        return (\n            self.dependency.complete_name == other.dependency.complete_name\n            and self.relation(other) == SetRelation.SUBSET\n        )\n\n    def _relation(self, other: Term) -> str:\n        \"\"\"\n        Returns the relationship between the package versions\n        allowed by this term and another.\n        \"\"\"\n        if self.dependency.complete_name != other.dependency.complete_name:\n            raise ValueError(f\"{other} should refer to {self.dependency.complete_name}\")\n\n        other_constraint = other.constraint\n\n        if other.is_positive():\n            if self.is_positive():\n                if not self._compatible_dependency(other.dependency):\n                    return SetRelation.DISJOINT\n\n                # foo ^1.5.0 is a subset of foo ^1.0.0\n                if other_constraint.allows_all(self.constraint):\n                    return SetRelation.SUBSET\n\n                # foo ^2.0.0 is disjoint with foo ^1.0.0\n                if not self.constraint.allows_any(other_constraint):\n                    return SetRelation.DISJOINT\n\n                return SetRelation.OVERLAPPING\n            else:\n                if not self._compatible_dependency(other.dependency):\n                    return SetRelation.OVERLAPPING\n\n                # not foo ^1.0.0 is disjoint with foo ^1.5.0\n                if self.constraint.allows_all(other_constraint):\n                    return SetRelation.DISJOINT\n\n                # not foo ^1.5.0 overlaps foo ^1.0.0\n                # not foo ^2.0.0 is a superset of foo ^1.5.0\n                return SetRelation.OVERLAPPING\n        else:\n            if self.is_positive():\n                if not self._compatible_dependency(other.dependency):\n                    return SetRelation.SUBSET\n\n                # foo ^2.0.0 is a subset of not foo ^1.0.0\n                if not other_constraint.allows_any(self.constraint):\n                    return SetRelation.SUBSET\n\n                # foo ^1.5.0 is disjoint with not foo ^1.0.0\n                if (\n                    other_constraint.allows_all(self.constraint)\n                    # if transitive markers are not equal we have to handle it\n                    # as overlapping so that markers are merged later\n                    and self.dependency.transitive_marker\n                    == other.dependency.transitive_marker\n                ):\n                    return SetRelation.DISJOINT\n\n                # foo ^1.0.0 overlaps not foo ^1.5.0\n                return SetRelation.OVERLAPPING\n            else:\n                if not self._compatible_dependency(other.dependency):\n                    return SetRelation.OVERLAPPING\n\n                # not foo ^1.0.0 is a subset of not foo ^1.5.0\n                if self.constraint.allows_all(other_constraint):\n                    return SetRelation.SUBSET\n\n                # not foo ^2.0.0 overlaps not foo ^1.0.0\n                # not foo ^1.5.0 is a superset of not foo ^1.0.0\n                return SetRelation.OVERLAPPING\n\n    def _intersect(self, other: Term) -> Term | None:\n        \"\"\"\n        Returns a Term that represents the packages\n        allowed by both this term and another\n        \"\"\"\n        if self.dependency.complete_name != other.dependency.complete_name:\n            raise ValueError(f\"{other} should refer to {self.dependency.complete_name}\")\n\n        if self._compatible_dependency(other.dependency):\n            if self.is_positive() != other.is_positive():\n                # foo ^1.0.0 ∩ not foo ^1.5.0 → foo >=1.0.0 <1.5.0\n                positive = self if self.is_positive() else other\n                negative = other if self.is_positive() else self\n\n                return self._non_empty_term(\n                    positive.constraint.difference(negative.constraint), True, other\n                )\n            elif self.is_positive():\n                # foo ^1.0.0 ∩ foo >=1.5.0 <3.0.0 → foo ^1.5.0\n                return self._non_empty_term(\n                    self.constraint.intersect(other.constraint), True, other\n                )\n            else:\n                # not foo ^1.0.0 ∩ not foo >=1.5.0 <3.0.0 → not foo >=1.0.0 <3.0.0\n                return self._non_empty_term(\n                    self.constraint.union(other.constraint), False, other\n                )\n        elif self.is_positive() != other.is_positive():\n            return self if self.is_positive() else other\n        else:\n            return None\n\n    def difference(self, other: Term) -> Term | None:\n        \"\"\"\n        Returns a Term that represents packages\n        allowed by this term and not by the other\n        \"\"\"\n        return self.intersect(other.inverse)\n\n    def _compatible_dependency(self, other: Dependency) -> bool:\n        return (\n            self.dependency.is_root\n            or other.is_root\n            or other.is_same_package_as(self.dependency)\n            or (\n                # we do this here to indicate direct origin dependencies are\n                # compatible with NVR dependencies\n                self.dependency.complete_name == other.complete_name\n                and self.dependency.is_direct_origin() != other.is_direct_origin()\n            )\n        )\n\n    def _non_empty_term(\n        self, constraint: VersionConstraint, is_positive: bool, other: Term\n    ) -> Term | None:\n        if constraint.is_empty():\n            return None\n\n        # when creating a new term prefer direct-reference dependencies\n        dependency = (\n            other.dependency\n            if not self.dependency.is_direct_origin()\n            and other.dependency.is_direct_origin()\n            else self.dependency\n        )\n        new_dep = dependency.with_constraint(constraint)\n        if is_positive and other.is_positive():\n            new_dep.transitive_marker = self.dependency.transitive_marker.union(\n                other.dependency.transitive_marker\n            )\n        return Term(new_dep, is_positive)\n\n    def __str__(self) -> str:\n        prefix = \"not \" if not self.is_positive() else \"\"\n        return f\"{prefix}{self._dependency}\"\n\n    def __repr__(self) -> str:\n        return f\"<Term {self!s}>\"\n"
  },
  {
    "path": "src/poetry/mixology/version_solver.py",
    "content": "from __future__ import annotations\n\nimport collections\nimport functools\nimport time\n\nfrom enum import IntEnum\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.dependency import Dependency\n\nfrom poetry.mixology.failure import SolveFailureError\nfrom poetry.mixology.incompatibility import Incompatibility\nfrom poetry.mixology.incompatibility_cause import ConflictCauseError\nfrom poetry.mixology.incompatibility_cause import NoVersionsCauseError\nfrom poetry.mixology.incompatibility_cause import RootCauseError\nfrom poetry.mixology.partial_solution import PartialSolution\nfrom poetry.mixology.result import SolverResult\nfrom poetry.mixology.set_relation import SetRelation\nfrom poetry.mixology.term import Term\nfrom poetry.packages import PackageCollection\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.packages import DependencyPackage\n    from poetry.puzzle.provider import Provider\n\n\n_conflict = object()\n\n\nclass Preference(IntEnum):\n    \"\"\"\n    Preference is one of the criteria for choosing which dependency to solve\n    first. A higher value means that there are \"more options\" to satisfy\n    a dependency. A lower value takes precedence.\n    \"\"\"\n\n    DIRECT_ORIGIN = 0\n    NO_CHOICE = 1\n    USE_LATEST = 2\n    LOCKED = 3\n    DEFAULT = 4\n\n\nCompKey = tuple[Preference, int, bool, int]\n\nDependencyCacheKey = tuple[str, str | None, str | None, str | None, str | None]\n\n\nclass DependencyCache:\n    \"\"\"\n    A cache of the valid dependencies.\n\n    The key observation here is that during the search - except at backtracking\n    - once we have decided that a dependency is invalid, we never need check it\n    again.\n    \"\"\"\n\n    def __init__(self, provider: Provider) -> None:\n        self._provider = provider\n\n        # self._cache maps a package name to a stack of cached package lists,\n        # ordered by the decision level which added them to the cache. This is\n        # done so that when backtracking we can maintain cache entries from\n        # previous decision levels, while clearing cache entries from only the\n        # rolled back levels.\n        #\n        # In order to maintain the integrity of the cache, `clear_level()`\n        # needs to be called in descending order as decision levels are\n        # backtracked so that the correct items can be popped from the stack.\n        self._cache: dict[DependencyCacheKey, list[list[DependencyPackage]]] = (\n            collections.defaultdict(list)\n        )\n        self._cached_dependencies_by_level: dict[int, list[DependencyCacheKey]] = (\n            collections.defaultdict(list)\n        )\n\n        self._search_for_cached = functools.lru_cache(maxsize=128)(self._search_for)\n\n    def _search_for(\n        self,\n        dependency: Dependency,\n        key: DependencyCacheKey,\n    ) -> list[DependencyPackage]:\n        cache_entries = self._cache[key]\n        if cache_entries:\n            packages = [\n                p\n                for p in cache_entries[-1]\n                if dependency.constraint.allows(p.package.version)\n            ]\n        else:\n            packages = None\n\n        # provider.search_for() normally does not include pre-release packages\n        # (unless requested), but will include them if there are no other\n        # eligible package versions for a version constraint.\n        #\n        # Therefore, if the eligible versions have been filtered down to\n        # nothing, we need to call provider.search_for() again as it may return\n        # additional results this time.\n        if not packages:\n            packages = self._provider.search_for(dependency)\n\n        return packages\n\n    def search_for(\n        self,\n        dependency: Dependency,\n        decision_level: int,\n    ) -> list[DependencyPackage]:\n        key = (\n            dependency.name,\n            dependency.source_type,\n            dependency.source_url,\n            dependency.source_reference,\n            dependency.source_subdirectory,\n        )\n\n        # We could always use dependency.without_features() here,\n        # but for performance reasons we only do it if necessary.\n        packages = self._search_for_cached(\n            dependency.without_features() if dependency.features else dependency, key\n        )\n        if not self._cache[key] or self._cache[key][-1] is not packages:\n            self._cache[key].append(packages)\n            self._cached_dependencies_by_level[decision_level].append(key)\n\n        if dependency.features and packages:\n            # Use the cached dependency so that a possible explicit source is set.\n            return PackageCollection(\n                packages[0].dependency.with_features(dependency.features), packages\n            )\n\n        return packages\n\n    def clear_level(self, level: int) -> None:\n        if level in self._cached_dependencies_by_level:\n            self._search_for_cached.cache_clear()\n            for key in self._cached_dependencies_by_level.pop(level):\n                self._cache[key].pop()\n\n\nclass VersionSolver:\n    \"\"\"\n    The version solver that finds a set of package versions that satisfy the\n    root package's dependencies.\n\n    See https://github.com/dart-lang/pub/tree/master/doc/solver.md for details\n    on how this solver works.\n    \"\"\"\n\n    def __init__(self, root: ProjectPackage, provider: Provider) -> None:\n        self._root = root\n        self._provider = provider\n        self._dependency_cache = DependencyCache(provider)\n        self._incompatibilities: dict[str, list[Incompatibility]] = {}\n        self._contradicted_incompatibilities: set[Incompatibility] = set()\n        self._contradicted_incompatibilities_by_level: dict[\n            int, set[Incompatibility]\n        ] = collections.defaultdict(set)\n        self._solution = PartialSolution()\n        self._get_comp_key_cached = functools.cache(self._get_comp_key)\n\n    @property\n    def solution(self) -> PartialSolution:\n        return self._solution\n\n    def solve(self) -> SolverResult:\n        \"\"\"\n        Finds a set of dependencies that match the root package's constraints,\n        or raises an error if no such set is available.\n        \"\"\"\n        start = time.time()\n        root_dependency = Dependency(self._root.name, self._root.version)\n        root_dependency.is_root = True\n\n        self._add_incompatibility(\n            Incompatibility([Term(root_dependency, False)], RootCauseError())\n        )\n\n        try:\n            next: str | None = self._root.name\n            while next is not None:\n                self._propagate(next)\n                next = self._choose_package_version()\n\n            return self._result()\n        except Exception:\n            raise\n        finally:\n            self._log(\n                f\"Version solving took {time.time() - start:.3f} seconds.\\n\"\n                f\"Tried {self._solution.attempted_solutions} solutions.\"\n            )\n\n    def _propagate(self, package: str) -> None:\n        \"\"\"\n        Performs unit propagation on incompatibilities transitively\n        related to package to derive new assignments for _solution.\n        \"\"\"\n        changed = {package}\n        while changed:\n            package = changed.pop()\n\n            # Iterate in reverse because conflict resolution tends to produce more\n            # general incompatibilities as time goes on. If we look at those first,\n            # we can derive stronger assignments sooner and more eagerly find\n            # conflicts.\n            for incompatibility in reversed(self._incompatibilities[package]):\n                if incompatibility in self._contradicted_incompatibilities:\n                    continue\n\n                result = self._propagate_incompatibility(incompatibility)\n\n                if result is _conflict:\n                    # If the incompatibility is satisfied by the solution, we use\n                    # _resolve_conflict() to determine the root cause of the conflict as\n                    # a new incompatibility.\n                    #\n                    # It also backjumps to a point in the solution\n                    # where that incompatibility will allow us to derive new assignments\n                    # that avoid the conflict.\n                    root_cause = self._resolve_conflict(incompatibility)\n\n                    # Back jumping erases all the assignments we did at the previous\n                    # decision level, so we clear [changed] and refill it with the\n                    # newly-propagated assignment.\n                    changed.clear()\n                    result = self._propagate_incompatibility(root_cause)\n                    assert result is not None\n                    assert result != _conflict\n                    assert isinstance(result, str)\n                    changed.add(result)\n                    break\n\n                if result is not None:\n                    assert isinstance(result, str)\n                    changed.add(result)\n\n    def _propagate_incompatibility(\n        self, incompatibility: Incompatibility\n    ) -> str | object | None:\n        \"\"\"\n        If incompatibility is almost satisfied by _solution, adds the\n        negation of the unsatisfied term to _solution.\n\n        If incompatibility is satisfied by _solution, returns _conflict. If\n        incompatibility is almost satisfied by _solution, returns the\n        unsatisfied term's package name.\n\n        Otherwise, returns None.\n        \"\"\"\n        # The first entry in incompatibility.terms that's not yet satisfied by\n        # _solution, if one exists. If we find more than one, _solution is\n        # inconclusive for incompatibility and we can't deduce anything.\n        unsatisfied = None\n\n        for term in incompatibility.terms:\n            relation = self._solution.relation(term)\n\n            if relation == SetRelation.DISJOINT:\n                # If term is already contradicted by _solution, then\n                # incompatibility is contradicted as well and there's nothing new we\n                # can deduce from it.\n                self._contradicted_incompatibilities.add(incompatibility)\n                self._contradicted_incompatibilities_by_level[\n                    self._solution.decision_level\n                ].add(incompatibility)\n                return None\n            elif relation == SetRelation.OVERLAPPING:\n                # If more than one term is inconclusive, we can't deduce anything about\n                # incompatibility.\n                if unsatisfied is not None:\n                    return None\n\n                # If exactly one term in incompatibility is inconclusive, then it's\n                # almost satisfied and [term] is the unsatisfied term. We can add the\n                # inverse of the term to _solution.\n                unsatisfied = term\n\n        # If *all* terms in incompatibility are satisfied by _solution, then\n        # incompatibility is satisfied and we have a conflict.\n        if unsatisfied is None:\n            return _conflict\n\n        self._contradicted_incompatibilities.add(incompatibility)\n        self._contradicted_incompatibilities_by_level[\n            self._solution.decision_level\n        ].add(incompatibility)\n\n        adverb = \"not \" if unsatisfied.is_positive() else \"\"\n        self._log(f\"derived: {adverb}{unsatisfied.dependency}\")\n\n        self._solution.derive(\n            unsatisfied.dependency, not unsatisfied.is_positive(), incompatibility\n        )\n\n        complete_name: str = unsatisfied.dependency.complete_name\n        return complete_name\n\n    def _resolve_conflict(self, incompatibility: Incompatibility) -> Incompatibility:\n        \"\"\"\n        Given an incompatibility that's satisfied by _solution,\n        The `conflict resolution`_ constructs a new incompatibility that encapsulates\n        the root cause of the conflict and backtracks _solution until the new\n        incompatibility will allow _propagate() to deduce new assignments.\n\n        Adds the new incompatibility to _incompatibilities and returns it.\n\n        .. _conflict resolution:\n        https://github.com/dart-lang/pub/tree/master/doc/solver.md#conflict-resolution\n        \"\"\"\n        self._log(f\"conflict: {incompatibility}\")\n\n        new_incompatibility = False\n        while not incompatibility.is_failure():\n            # The term in incompatibility.terms that was most recently satisfied by\n            # _solution.\n            most_recent_term = None\n\n            # The earliest assignment in _solution such that incompatibility is\n            # satisfied by _solution up to and including this assignment.\n            most_recent_satisfier = None\n\n            # The difference between most_recent_satisfier and most_recent_term;\n            # that is, the versions that are allowed by most_recent_satisfier and not\n            # by most_recent_term. This is None if most_recent_satisfier totally\n            # satisfies most_recent_term.\n            difference = None\n\n            # The decision level of the earliest assignment in _solution *before*\n            # most_recent_satisfier such that incompatibility is satisfied by\n            # _solution up to and including this assignment plus\n            # most_recent_satisfier.\n            #\n            # Decision level 1 is the level where the root package was selected. It's\n            # safe to go back to decision level 0, but stopping at 1 tends to produce\n            # better error messages, because references to the root package end up\n            # closer to the final conclusion that no solution exists.\n            previous_satisfier_level = 1\n\n            for term in incompatibility.terms:\n                satisfier = self._solution.satisfier(term)\n\n                if most_recent_satisfier is None:\n                    most_recent_term = term\n                    most_recent_satisfier = satisfier\n                elif most_recent_satisfier.index < satisfier.index:\n                    previous_satisfier_level = max(\n                        previous_satisfier_level, most_recent_satisfier.decision_level\n                    )\n                    most_recent_term = term\n                    most_recent_satisfier = satisfier\n                    difference = None\n                else:\n                    previous_satisfier_level = max(\n                        previous_satisfier_level, satisfier.decision_level\n                    )\n\n                if most_recent_term == term:\n                    # If most_recent_satisfier doesn't satisfy most_recent_term on its\n                    # own, then the next-most-recent satisfier may be the one that\n                    # satisfies the remainder.\n                    difference = most_recent_satisfier.difference(most_recent_term)\n                    if difference is not None:\n                        previous_satisfier_level = max(\n                            previous_satisfier_level,\n                            self._solution.satisfier(difference.inverse).decision_level,\n                        )\n\n            # If most_recent_identifier is the only satisfier left at its decision\n            # level, or if it has no cause (indicating that it's a decision rather\n            # than a derivation), then incompatibility is the root cause. We then\n            # backjump to previous_satisfier_level, where incompatibility is\n            # guaranteed to allow _propagate to produce more assignments.\n\n            # using assert to suppress mypy [union-attr]\n            assert most_recent_satisfier is not None\n            if (\n                previous_satisfier_level < most_recent_satisfier.decision_level\n                or most_recent_satisfier.cause is None\n            ):\n                for level in range(\n                    self._solution.decision_level, previous_satisfier_level, -1\n                ):\n                    if level in self._contradicted_incompatibilities_by_level:\n                        self._contradicted_incompatibilities.difference_update(\n                            self._contradicted_incompatibilities_by_level.pop(level),\n                        )\n                    self._dependency_cache.clear_level(level)\n\n                self._solution.backtrack(previous_satisfier_level)\n                if new_incompatibility:\n                    self._add_incompatibility(incompatibility)\n\n                return incompatibility\n\n            # Create a new incompatibility by combining incompatibility with the\n            # incompatibility that caused most_recent_satisfier to be assigned. Doing\n            # this iteratively constructs an incompatibility that's guaranteed to be\n            # true (that is, we know for sure no solution will satisfy the\n            # incompatibility) while also approximating the intuitive notion of the\n            # \"root cause\" of the conflict.\n            new_terms = [\n                term for term in incompatibility.terms if term != most_recent_term\n            ]\n\n            for term in most_recent_satisfier.cause.terms:\n                if term.dependency != most_recent_satisfier.dependency:\n                    new_terms.append(term)\n\n            # The most_recent_satisfier may not satisfy most_recent_term on its own\n            # if there are a collection of constraints on most_recent_term that\n            # only satisfy it together. For example, if most_recent_term is\n            # `foo ^1.0.0` and _solution contains `[foo >=1.0.0,\n            # foo <2.0.0]`, then most_recent_satisfier will be `foo <2.0.0` even\n            # though it doesn't totally satisfy `foo ^1.0.0`.\n            #\n            # In this case, we add `not (most_recent_satisfier \\ most_recent_term)` to\n            # the incompatibility as well, See the `algorithm documentation`_ for\n            # details.\n            #\n            # .. _algorithm documentation:\n            # https://github.com/dart-lang/pub/tree/master/doc/solver.md#conflict-resolution\n            if difference is not None:\n                inverse = difference.inverse\n                if inverse.dependency != most_recent_satisfier.dependency:\n                    new_terms.append(inverse)\n\n            incompatibility = Incompatibility(\n                new_terms,\n                ConflictCauseError(incompatibility, most_recent_satisfier.cause),\n            )\n            new_incompatibility = True\n\n            partially = \"\" if difference is None else \" partially\"\n            self._log(\n                f\"! {most_recent_term} is{partially} satisfied by\"\n                f\" {most_recent_satisfier}\"\n            )\n            self._log(f'! which is caused by \"{most_recent_satisfier.cause}\"')\n            self._log(f\"! thus: {incompatibility}\")\n\n        raise SolveFailureError(incompatibility)\n\n    def _get_comp_key(self, dependency: Dependency) -> CompKey:\n        \"\"\"\n        Returns a tuple of\n        - preference\n        - num_deps_upper_bound\n        - has_deps\n        - num_packages\n        that serves as priority for choosing the next package to resolve.\n        (A lower value takes precedence.)\n\n        In order to provide results that are as deterministic as possible\n        and consistent between `poetry lock` and `poetry update`, the return value\n        of two different dependencies should not be equal if possible.\n\n        ## preference\n\n        See Preference class.\n\n        ## num_deps_upper_bound\n\n        A dependency with an upper bound is more likely to cause conflicts. Therefore,\n        a package with more dependencies with upper bounds should be chosen first.\n\n        ## has_deps\n\n        A package with dependencies should be chosen first\n        because a package without dependencies is less likely to cause conflicts.\n\n        ## num_packages\n\n        The original algorithm proposes to prefer packages with as few remaining\n        versions as possible, so that if a conflict is necessary it's forced quickly.\n        https://github.com/dart-lang/pub/blob/master/doc/solver.md#decision-making\n        However, this leads to the famous boto3 vs. urllib3 issue, so we prefer\n        packages with more remaining versions (see\n        https://github.com/python-poetry/poetry/pull/8255#issuecomment-1657198242\n        for more details).\n        \"\"\"\n        preference = Preference.DEFAULT\n\n        # Direct origin dependencies must be handled first: we don't want to resolve\n        # a regular dependency for some package only to find later that we had a\n        # direct-origin dependency.\n        if dependency.is_direct_origin():\n            preference = Preference.DIRECT_ORIGIN\n\n        packages: list[DependencyPackage] = []\n        use_latest = dependency.name in self._provider.use_latest\n        if not use_latest:\n            locked = self._provider.get_locked(dependency)\n            if locked:\n                if preference == Preference.DEFAULT:\n                    preference = Preference.LOCKED\n                packages = [locked]\n\n        if not packages:\n            packages = self._dependency_cache.search_for(\n                dependency, self._solution.decision_level\n            )\n        num_packages = len(packages)\n        if packages:\n            package = packages[0].package\n            if package.is_root():\n                relevant_dependencies = package.all_requires\n            else:\n                if preference != Preference.LOCKED and not package.is_direct_origin():\n                    # We have to get the package from the pool,\n                    # otherwise `requires` will be empty.\n                    #\n                    # We might need `package.source_reference` as fallback\n                    # for transitive dependencies without a source\n                    # if there is a top-level dependency\n                    # for the same package with an explicit source.\n                    for repo in (dependency.source_name, package.source_reference):\n                        try:\n                            package = self._provider.get_package_from_pool(\n                                package.pretty_name,\n                                package.version,\n                                repository_name=repo,\n                            )\n                        except Exception:\n                            pass\n                        else:\n                            break\n\n                relevant_dependencies = [\n                    r\n                    for r in package.requires\n                    if not r.in_extras or r.in_extras[0] in dependency.extras\n                ]\n            has_deps = bool(relevant_dependencies)\n            num_deps_upper_bound = sum(\n                1 for d in relevant_dependencies if d.constraint.has_upper_bound()\n            )\n        else:\n            has_deps = False\n            num_deps_upper_bound = 0\n\n        if preference == Preference.DEFAULT:\n            if num_packages < 2:\n                preference = Preference.NO_CHOICE\n            elif use_latest:\n                preference = Preference.USE_LATEST\n        return preference, -num_deps_upper_bound, not has_deps, -num_packages\n\n    def _choose_next(self, unsatisfied: list[Dependency]) -> Dependency:\n        \"\"\"\n        Chooses the next package to resolve.\n        \"\"\"\n        return min(unsatisfied, key=self._get_comp_key_cached)\n\n    def _choose_package_version(self) -> str | None:\n        \"\"\"\n        Tries to select a version of a required package.\n\n        Returns the name of the package whose incompatibilities should be\n        propagated by _propagate(), or None indicating that version solving is\n        complete and a solution has been found.\n        \"\"\"\n        unsatisfied = self._solution.unsatisfied\n        if not unsatisfied:\n            return None\n\n        dependency = self._choose_next(unsatisfied)\n\n        locked = self._provider.get_locked(dependency)\n        if locked is None:\n            packages = self._dependency_cache.search_for(\n                dependency, self._solution.decision_level\n            )\n            package = next(iter(packages), None)\n\n            if package is None:\n                # If there are no versions that satisfy the constraint,\n                # add an incompatibility that indicates that.\n                self._add_incompatibility(\n                    Incompatibility([Term(dependency, True)], NoVersionsCauseError())\n                )\n\n                complete_name = dependency.complete_name\n                return complete_name\n\n            package.dependency.transitive_marker = dependency.transitive_marker\n        else:\n            package = locked\n\n        package = self._provider.complete_package(package)\n\n        conflict = False\n        for incompatibility in self._provider.incompatibilities_for(package):\n            self._add_incompatibility(incompatibility)\n\n            # If an incompatibility is already satisfied, then selecting version\n            # would cause a conflict.\n            #\n            # We'll continue adding its dependencies, then go back to\n            # unit propagation which will guide us to choose a better version.\n            conflict = conflict or all(\n                term.dependency.complete_name == dependency.complete_name\n                or self._solution.satisfies(term)\n                for term in incompatibility.terms\n            )\n\n        if not conflict:\n            self._solution.decide(package.package)\n            self._log(\n                f\"selecting {package.package.complete_name}\"\n                f\" ({package.package.full_pretty_version})\"\n            )\n\n        complete_name = dependency.complete_name\n        return complete_name\n\n    def _result(self) -> SolverResult:\n        \"\"\"\n        Creates a #SolverResult from the decisions in _solution\n        \"\"\"\n        decisions = self._solution.decisions\n\n        return SolverResult(\n            self._root,\n            [p for p in decisions if not p.is_root()],\n            self._solution.attempted_solutions,\n        )\n\n    def _add_incompatibility(self, incompatibility: Incompatibility) -> None:\n        self._log(f\"fact: {incompatibility}\")\n\n        for term in incompatibility.terms:\n            if term.dependency.complete_name not in self._incompatibilities:\n                self._incompatibilities[term.dependency.complete_name] = []\n\n            if (\n                incompatibility\n                in self._incompatibilities[term.dependency.complete_name]\n            ):\n                continue\n\n            self._incompatibilities[term.dependency.complete_name].append(\n                incompatibility\n            )\n\n    def _log(self, text: str) -> None:\n        self._provider.debug(text, self._solution.attempted_solutions)\n"
  },
  {
    "path": "src/poetry/packages/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.packages.dependency_package import DependencyPackage\nfrom poetry.packages.locker import Locker\nfrom poetry.packages.package_collection import PackageCollection\n\n\n__all__ = [\"DependencyPackage\", \"Locker\", \"PackageCollection\"]\n"
  },
  {
    "path": "src/poetry/packages/dependency_package.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n\nclass DependencyPackage:\n    def __init__(self, dependency: Dependency, package: Package) -> None:\n        self._dependency = dependency\n        self._package = package\n\n    @property\n    def dependency(self) -> Dependency:\n        return self._dependency\n\n    @property\n    def package(self) -> Package:\n        return self._package\n\n    def clone(self) -> DependencyPackage:\n        return self.__class__(self._dependency, self._package.clone())\n\n    def with_features(self, features: Iterable[str]) -> DependencyPackage:\n        return self.__class__(self._dependency, self._package.with_features(features))\n\n    def without_features(self) -> DependencyPackage:\n        return self.with_features([])\n\n    def __str__(self) -> str:\n        return str(self._package)\n\n    def __repr__(self) -> str:\n        return repr(self._package)\n\n    def __hash__(self) -> int:\n        return hash(self._package)\n\n    def __eq__(self, other: object) -> bool:\n        if isinstance(other, DependencyPackage):\n            other = other.package\n\n        equal: bool = self._package == other\n        return equal\n"
  },
  {
    "path": "src/poetry/packages/direct_origin.py",
    "content": "from __future__ import annotations\n\nimport functools\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.config.config import Config\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.inspection.info import PackageInfoError\nfrom poetry.utils.authenticator import get_default_authenticator\nfrom poetry.utils.helpers import download_file\nfrom poetry.utils.helpers import get_file_hash\nfrom poetry.vcs.git import Git\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n    from poetry.utils.cache import ArtifactCache\n\n\n@functools.cache\ndef _get_package_from_git(\n    url: str,\n    branch: str | None = None,\n    tag: str | None = None,\n    rev: str | None = None,\n    subdirectory: str | None = None,\n    source_root: Path | None = None,\n) -> Package:\n    source = Git.clone(\n        url=url,\n        source_root=source_root,\n        branch=branch,\n        tag=tag,\n        revision=rev,\n        clean=False,\n    )\n    revision = Git.get_revision(source)\n\n    path = Path(source.path)\n    if subdirectory:\n        path = path.joinpath(subdirectory)\n\n    package = DirectOrigin.get_package_from_directory(path)\n    package._source_type = \"git\"\n    package._source_url = url\n    package._source_reference = rev or tag or branch or \"HEAD\"\n    package._source_resolved_reference = revision\n    package._source_subdirectory = subdirectory\n\n    return package\n\n\nclass DirectOrigin:\n    def __init__(self, artifact_cache: ArtifactCache) -> None:\n        self._artifact_cache = artifact_cache\n        config = Config.create()\n        self._max_retries = config.get(\"requests.max-retries\", 0)\n        self._authenticator = get_default_authenticator()\n\n    @classmethod\n    def get_package_from_file(cls, file_path: Path) -> Package:\n        try:\n            package = PackageInfo.from_path(path=file_path).to_package(\n                root_dir=file_path\n            )\n        except PackageInfoError:\n            raise RuntimeError(\n                f\"Unable to determine package info from path: {file_path}\"\n            )\n\n        package.files = [\n            {\n                \"file\": file_path.name,\n                \"hash\": \"sha256:\" + get_file_hash(file_path),\n                \"size\": file_path.stat().st_size,\n            }\n        ]\n\n        return package\n\n    @classmethod\n    def get_package_from_directory(cls, directory: Path) -> Package:\n        return PackageInfo.from_directory(path=directory).to_package(root_dir=directory)\n\n    def _download_file(self, url: str, dest: Path) -> None:\n        download_file(\n            url, dest, session=self._authenticator, max_retries=self._max_retries\n        )\n\n    def get_package_from_url(self, url: str) -> Package:\n        link = Link(url)\n        artifact = self._artifact_cache.get_cached_archive_for_link(\n            link, strict=True, download_func=self._download_file\n        )\n\n        package = self.get_package_from_file(artifact)\n\n        package._source_type = \"url\"\n        package._source_url = url\n\n        return package\n\n    @staticmethod\n    def get_package_from_vcs(\n        vcs: str,\n        url: str,\n        branch: str | None = None,\n        tag: str | None = None,\n        rev: str | None = None,\n        subdirectory: str | None = None,\n        source_root: Path | None = None,\n    ) -> Package:\n        if vcs != \"git\":\n            raise ValueError(f\"Unsupported VCS dependency {vcs}\")\n\n        return _get_package_from_git(\n            url=url,\n            branch=branch,\n            tag=tag,\n            rev=rev,\n            subdirectory=subdirectory,\n            source_root=source_root,\n        )\n"
  },
  {
    "path": "src/poetry/packages/locker.py",
    "content": "from __future__ import annotations\n\nimport json\nimport logging\nimport os\nimport re\nimport warnings\n\nfrom hashlib import sha256\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom typing import cast\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.version.exceptions import InvalidVersionError\nfrom poetry.core.version.markers import parse_marker\nfrom poetry.core.version.requirements import InvalidRequirementError\nfrom tomlkit import array\nfrom tomlkit import comment\nfrom tomlkit import document\nfrom tomlkit import inline_table\nfrom tomlkit import table\n\nfrom poetry.__version__ import __version__\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\nfrom poetry.packages.transitive_package_info import group_sort_key\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils._compat import tomllib\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.directory_dependency import DirectoryDependency\n    from poetry.core.packages.file_dependency import FileDependency\n    from poetry.core.packages.url_dependency import URLDependency\n    from poetry.core.packages.vcs_dependency import VCSDependency\n    from tomlkit.toml_document import TOMLDocument\n\n    from poetry.repositories.lockfile_repository import LockfileRepository\n\nlogger = logging.getLogger(__name__)\n_GENERATED_IDENTIFIER = \"@\" + \"generated\"\nGENERATED_COMMENT = (\n    f\"This file is automatically {_GENERATED_IDENTIFIER} by Poetry\"\n    f\" {__version__} and should not be changed by hand.\"\n)\n\n\nclass Locker:\n    _VERSION = \"2.1\"\n    _READ_VERSION_RANGE = \">=1,<3\"\n\n    _legacy_keys: ClassVar[list[str]] = [\n        \"dependencies\",\n        \"source\",\n        \"extras\",\n        \"dev-dependencies\",\n    ]\n    _relevant_keys: ClassVar[list[str]] = [*_legacy_keys, \"group\"]\n    _relevant_project_keys: ClassVar[list[str]] = [\n        \"requires-python\",\n        \"dependencies\",\n        \"optional-dependencies\",\n    ]\n\n    def __init__(self, lock: Path, pyproject_data: dict[str, Any]) -> None:\n        self._lock = lock\n        self._pyproject_data = pyproject_data\n        self._lock_data: dict[str, Any] | None = None\n        self._content_hash = self._get_content_hash()\n\n    @property\n    def lock(self) -> Path:\n        return self._lock\n\n    @property\n    def lock_data(self) -> dict[str, Any]:\n        if self._lock_data is None:\n            self._lock_data = self._get_lock_data()\n\n        return self._lock_data\n\n    def is_locked(self) -> bool:\n        \"\"\"\n        Checks whether the locker has been locked (lockfile found).\n        \"\"\"\n        return self._lock.exists()\n\n    def is_fresh(self) -> bool:\n        \"\"\"\n        Checks whether the lock file is still up to date with the current hash.\n        \"\"\"\n        with self.lock.open(\"rb\") as f:\n            lock = tomllib.load(f)\n        metadata = lock.get(\"metadata\", {})\n\n        if \"content-hash\" in metadata:\n            fresh: bool = self._content_hash == metadata[\"content-hash\"]\n            if not fresh:\n                with self.lock.open(\"r\", encoding=\"utf-8\") as f:\n                    generated_comment = f.readline()\n                if m := re.search(\"Poetry ([^ ]+)\", generated_comment):\n                    try:\n                        version = Version.parse(m.group(1))\n                    except InvalidVersionError:\n                        pass\n                    else:\n                        if version < Version.parse(\"2.3.0\"):\n                            # Before Poetry 2.3.0, the content hash did not include\n                            # dependency groups, so we need to recompute it without\n                            # them for comparison.\n                            old_content_hash = self._get_content_hash(\n                                with_dependency_groups=False\n                            )\n                            fresh = old_content_hash == metadata[\"content-hash\"]\n            return fresh\n\n        return False\n\n    def is_locked_groups_and_markers(self) -> bool:\n        if not self.is_locked():\n            return False\n\n        version = Version.parse(self.lock_data[\"metadata\"][\"lock-version\"])\n        return version >= Version.parse(\"2.1\")\n\n    def set_pyproject_data(self, pyproject_data: dict[str, Any]) -> None:\n        self._pyproject_data = pyproject_data\n        self._content_hash = self._get_content_hash()\n\n    def set_local_config(self, local_config: dict[str, Any]) -> None:\n        warnings.warn(\n            \"Locker.set_local_config() is deprecated and will be removed in a future\"\n            \" release. Use Locker.set_pyproject_data() instead.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        self._pyproject_data.setdefault(\"tool\", {})[\"poetry\"] = local_config\n        self._content_hash = self._get_content_hash()\n\n    def locked_repository(self) -> LockfileRepository:\n        \"\"\"\n        Searches and returns a repository of locked packages.\n        \"\"\"\n        from poetry.repositories.lockfile_repository import LockfileRepository\n\n        repository = LockfileRepository()\n\n        if not self.is_locked():\n            return repository\n\n        locked_packages = cast(\"list[dict[str, Any]]\", self.lock_data[\"package\"])\n\n        if not locked_packages:\n            return repository\n\n        for info in locked_packages:\n            repository.add_package(self._get_locked_package(info))\n\n        return repository\n\n    def locked_packages(self) -> dict[Package, TransitivePackageInfo]:\n        if not self.is_locked_groups_and_markers():\n            raise RuntimeError(\n                \"This method should not be called if the lock file\"\n                \" is not at least version 2.1.\"\n            )\n\n        locked_packages: dict[Package, TransitivePackageInfo] = {}\n\n        locked_package_info = cast(\"list[dict[str, Any]]\", self.lock_data[\"package\"])\n\n        for info in locked_package_info:\n            package = self._get_locked_package(info, with_dependencies=False)\n            groups = set(info[\"groups\"])\n            locked_marker = info.get(\"markers\", \"*\")\n            if isinstance(locked_marker, str):\n                markers = {\n                    canonicalize_name(group): parse_marker(locked_marker)\n                    for group in groups\n                }\n            else:\n                markers = {\n                    canonicalize_name(group): parse_marker(\n                        locked_marker.get(group, \"*\")\n                    )\n                    for group in groups\n                }\n            locked_packages[package] = TransitivePackageInfo(\n                0, {canonicalize_name(g) for g in groups}, markers\n            )\n\n        return locked_packages\n\n    def set_lock_data(\n        self, root: Package, packages: dict[Package, TransitivePackageInfo]\n    ) -> bool:\n        \"\"\"Store lock data and eventually persist to the lock file\"\"\"\n        lock = self._compute_lock_data(root, packages)\n\n        if self._should_write(lock):\n            self._write_lock_data(lock)\n            return True\n\n        return False\n\n    def _compute_lock_data(\n        self, root: Package, packages: dict[Package, TransitivePackageInfo]\n    ) -> TOMLDocument:\n        package_specs = self._lock_packages(packages)\n        # Retrieving hashes\n        for package in package_specs:\n            files = array()\n\n            for f in package[\"files\"]:\n                file_metadata = inline_table()\n                for k, v in sorted(f.items()):\n                    file_metadata[k] = v\n\n                files.append(file_metadata)\n\n            package[\"files\"] = files.multiline(True)\n\n        lock = document()\n        lock.add(comment(GENERATED_COMMENT))\n        lock[\"package\"] = package_specs\n\n        if root.extras:\n            lock[\"extras\"] = {\n                extra: sorted(dep.pretty_name for dep in deps)\n                for extra, deps in sorted(root.extras.items())\n            }\n\n        lock[\"metadata\"] = {\n            \"lock-version\": self._VERSION,\n            \"python-versions\": root.python_versions,\n            \"content-hash\": self._content_hash,\n        }\n\n        return lock\n\n    def _should_write(self, lock: TOMLDocument) -> bool:\n        # if lock file exists: compare with existing lock data\n        do_write = True\n        if self.is_locked():\n            try:\n                lock_data = self.lock_data\n            except RuntimeError:\n                # incompatible, invalid or no lock file\n                pass\n            else:\n                do_write = lock != lock_data\n        return do_write\n\n    def _write_lock_data(self, data: TOMLDocument) -> None:\n        if self.lock.exists():\n            # The following code is roughly equivalent to\n            # • lockfile = TOMLFile(self.lock)\n            # • lockfile.read()\n            # • lockfile.write(data)\n            # However, lockfile.read() takes more than half a second even\n            # for a modestly sized project like Poetry itself and the only reason\n            # for reading the lockfile is to determine the line endings. Thus,\n            # we do that part for ourselves here, which only takes about 10 ms.\n\n            # get original line endings\n            with open(self.lock, encoding=\"utf-8\", newline=\"\") as f:\n                line = f.readline()\n            linesep = \"\\r\\n\" if line.endswith(\"\\r\\n\") else \"\\n\"\n\n            # enforce original line endings\n            content = data.as_string()\n            if linesep == \"\\n\":\n                content = content.replace(\"\\r\\n\", \"\\n\")\n            elif linesep == \"\\r\\n\":\n                content = re.sub(r\"(?<!\\r)\\n\", \"\\r\\n\", content)\n            with open(self.lock, \"w\", encoding=\"utf-8\", newline=\"\") as f:\n                f.write(content)\n\n        else:\n            lockfile = TOMLFile(self.lock)\n            lockfile.write(data)\n\n        self._lock_data = None\n\n    def _get_content_hash(self, *, with_dependency_groups: bool = True) -> str:\n        \"\"\"\n        Returns the sha256 hash of the sorted content of the pyproject file.\n        \"\"\"\n        project_content = self._pyproject_data.get(\"project\", {})\n        group_content = (\n            self._pyproject_data.get(\"dependency-groups\", {})\n            if with_dependency_groups\n            else {}\n        )\n        tool_poetry_content = self._pyproject_data.get(\"tool\", {}).get(\"poetry\", {})\n\n        relevant_project_content = {}\n        for key in self._relevant_project_keys:\n            data = project_content.get(key)\n            if data is not None:\n                relevant_project_content[key] = data\n\n        relevant_poetry_content = {}\n        for key in self._relevant_keys:\n            data = tool_poetry_content.get(key)\n\n            if data is None and (\n                # Special handling for legacy keys is just for backwards compatibility,\n                # and thereby not required if there is relevant content in [project]\n                # or [dependency-groups].\n                key not in self._legacy_keys\n                or relevant_project_content\n                or group_content\n            ):\n                continue\n\n            relevant_poetry_content[key] = data\n\n        relevant_content = {}\n        if relevant_project_content:\n            relevant_content[\"project\"] = relevant_project_content\n        if group_content:\n            # For backwards compatibility, we must not add dependency-groups\n            # if it is empty.\n            relevant_content[\"dependency-groups\"] = group_content\n\n        if relevant_content:\n            relevant_content[\"tool\"] = {\"poetry\": relevant_poetry_content}\n        else:\n            # For backwards compatibility, we have to put the relevant content\n            # of the [tool.poetry] section at top level!\n            relevant_content = relevant_poetry_content\n\n        return sha256(json.dumps(relevant_content, sort_keys=True).encode()).hexdigest()\n\n    def _get_lock_data(self) -> dict[str, Any]:\n        if not self.lock.exists():\n            raise RuntimeError(\"No lockfile found. Unable to read locked packages\")\n\n        with self.lock.open(\"rb\") as f:\n            try:\n                lock_data = tomllib.load(f)\n            except tomllib.TOMLDecodeError as e:\n                raise RuntimeError(f\"Unable to read the lock file ({e}).\")\n\n        # if the lockfile doesn't contain a metadata section at all,\n        # it probably needs to be rebuilt completely\n        if \"metadata\" not in lock_data:\n            raise RuntimeError(\n                \"The lock file does not have a metadata entry.\\n\"\n                \"Regenerate the lock file with the `poetry lock` command.\"\n            )\n\n        metadata = lock_data[\"metadata\"]\n        if \"lock-version\" not in metadata:\n            raise RuntimeError(\n                \"The lock file is not compatible with the current version of Poetry.\\n\"\n                \"Regenerate the lock file with the `poetry lock` command.\"\n            )\n        lock_version = Version.parse(metadata[\"lock-version\"])\n        current_version = Version.parse(self._VERSION)\n        accepted_versions = parse_constraint(self._READ_VERSION_RANGE)\n        lock_version_allowed = accepted_versions.allows(lock_version)\n        if lock_version_allowed and current_version < lock_version:\n            logger.warning(\n                \"The lock file might not be compatible with the current version of\"\n                \" Poetry.\\nUpgrade Poetry to ensure the lock file is read properly or,\"\n                \" alternatively, regenerate the lock file with the `poetry lock`\"\n                \" command.\"\n            )\n        elif not lock_version_allowed:\n            raise RuntimeError(\n                \"The lock file is not compatible with the current version of Poetry.\\n\"\n                \"Upgrade Poetry to be able to read the lock file or, alternatively, \"\n                \"regenerate the lock file with the `poetry lock` command.\"\n            )\n\n        return lock_data\n\n    def _get_locked_package(\n        self, info: dict[str, Any], with_dependencies: bool = True\n    ) -> Package:\n        source = info.get(\"source\", {})\n        source_type = source.get(\"type\")\n        url = source.get(\"url\")\n        if source_type in [\"directory\", \"file\"]:\n            url = self.lock.parent.joinpath(url).resolve().as_posix()\n\n        name = info[\"name\"]\n        package = Package(\n            name,\n            info[\"version\"],\n            source_type=source_type,\n            source_url=url,\n            source_reference=source.get(\"reference\"),\n            source_resolved_reference=source.get(\"resolved_reference\"),\n            source_subdirectory=source.get(\"subdirectory\"),\n        )\n        package.description = info.get(\"description\", \"\")\n        package.optional = info[\"optional\"]\n        metadata = cast(\"dict[str, Any]\", self.lock_data[\"metadata\"])\n\n        # Storing of package files and hashes has been through a few generations in\n        # the lockfile, we can read them all:\n        #\n        # - latest and preferred is that this is read per package, from\n        #   package.files\n        # - oldest is that hashes were stored in metadata.hashes without filenames\n        # - in between those two, hashes were stored alongside filenames in\n        #   metadata.files\n        package_files = info.get(\"files\")\n        if package_files is not None:\n            package.files = package_files\n        elif \"hashes\" in metadata:\n            hashes = cast(\"dict[str, Any]\", metadata[\"hashes\"])\n            # Strictly speaking, this is not correct,\n            # but we do not know the file names here,\n            # so we just set both file and hash.\n            package.files = [{\"file\": h, \"hash\": h} for h in hashes[name]]\n        elif source_type in {\"git\", \"directory\", \"url\"}:\n            package.files = []\n        else:\n            files = metadata[\"files\"][name]\n            if source_type == \"file\":\n                filename = Path(url).name\n                package.files = [item for item in files if item[\"file\"] == filename]\n            else:\n                # Strictly speaking, this is not correct, but we have no chance\n                # to always determine which are the correct files because the\n                # lockfile doesn't keep track which files belong to which package.\n                package.files = files\n\n        package.python_versions = info[\"python-versions\"]\n\n        if \"develop\" in info:\n            package.develop = info[\"develop\"]\n\n        if with_dependencies:\n            from poetry.factory import Factory\n\n            package_extras: dict[NormalizedName, list[Dependency]] = {}\n            extras = info.get(\"extras\", {})\n            if extras:\n                for name, deps in extras.items():\n                    name = canonicalize_name(name)\n                    package_extras[name] = []\n\n                    for dep in deps:\n                        try:\n                            dependency = Dependency.create_from_pep_508(dep)\n                        except InvalidRequirementError:\n                            # handle lock files with invalid PEP 508\n                            m = re.match(r\"^(.+?)(?:\\[(.+?)])?(?:\\s+\\((.+)\\))?$\", dep)\n                            if not m:\n                                raise\n                            dep_name = m.group(1)\n                            extras = m.group(2) or \"\"\n                            constraint = m.group(3) or \"*\"\n                            dependency = Dependency(\n                                dep_name, constraint, extras=extras.split(\",\")\n                            )\n                        package_extras[name].append(dependency)\n\n            package.extras = package_extras\n\n            for dep_name, constraint in info.get(\"dependencies\", {}).items():\n                root_dir = self.lock.parent\n                if package.source_type == \"directory\":\n                    # root dir should be the source of the package relative to the lock\n                    # path\n                    assert package.source_url is not None\n                    root_dir = Path(package.source_url)\n\n                if isinstance(constraint, list):\n                    for c in constraint:\n                        package.add_dependency(\n                            Factory.create_dependency(dep_name, c, root_dir=root_dir)\n                        )\n\n                    continue\n\n                package.add_dependency(\n                    Factory.create_dependency(dep_name, constraint, root_dir=root_dir)\n                )\n\n        return package\n\n    def _lock_packages(\n        self, packages: dict[Package, TransitivePackageInfo]\n    ) -> list[dict[str, Any]]:\n        locked = []\n\n        for package in sorted(\n            packages,\n            key=lambda x: (\n                x.name,\n                x.version,\n                x.source_type or \"\",\n                x.source_url or \"\",\n                x.source_subdirectory or \"\",\n                x.source_reference or \"\",\n                x.source_resolved_reference or \"\",\n            ),\n        ):\n            spec = self._dump_package(package, packages[package])\n\n            locked.append(spec)\n\n        return locked\n\n    def _dump_package(\n        self, package: Package, transitive_info: TransitivePackageInfo\n    ) -> dict[str, Any]:\n        dependencies: dict[str, list[Any]] = {}\n        for dependency in sorted(\n            package.requires,\n            key=lambda d: (\n                d.name,\n                str(d.marker) if not d.marker.is_any() else \"\",\n            ),\n        ):\n            dependencies.setdefault(dependency.pretty_name, [])\n\n            constraint = inline_table()\n\n            if dependency.is_directory():\n                dependency = cast(\"DirectoryDependency\", dependency)\n                constraint[\"path\"] = dependency.path.as_posix()\n\n                if dependency.develop:\n                    constraint[\"develop\"] = True\n\n            elif dependency.is_file():\n                dependency = cast(\"FileDependency\", dependency)\n                constraint[\"path\"] = dependency.path.as_posix()\n\n            elif dependency.is_url():\n                dependency = cast(\"URLDependency\", dependency)\n                constraint[\"url\"] = dependency.url\n\n            elif dependency.is_vcs():\n                dependency = cast(\"VCSDependency\", dependency)\n                constraint[dependency.vcs] = dependency.source\n\n                if dependency.branch:\n                    constraint[\"branch\"] = dependency.branch\n                elif dependency.tag:\n                    constraint[\"tag\"] = dependency.tag\n                elif dependency.rev:\n                    constraint[\"rev\"] = dependency.rev\n\n                if dependency.directory:\n                    constraint[\"subdirectory\"] = dependency.directory\n\n            else:\n                constraint[\"version\"] = str(dependency.pretty_constraint)\n\n            if dependency.extras:\n                constraint[\"extras\"] = sorted(dependency.extras)\n\n            if dependency.is_optional():\n                constraint[\"optional\"] = True\n\n            if not dependency.marker.is_any():\n                constraint[\"markers\"] = str(dependency.marker)\n\n            dependencies[dependency.pretty_name].append(constraint)\n\n        # All the constraints should have the same type,\n        # but we want to simplify them if it's possible\n        for dependency_name, constraints in dependencies.items():\n            if all(\n                len(constraint) == 1 and \"version\" in constraint\n                for constraint in constraints\n            ):\n                dependencies[dependency_name] = [\n                    constraint[\"version\"] for constraint in constraints\n                ]\n\n        data: dict[str, Any] = {\n            \"name\": package.pretty_name,\n            \"version\": package.pretty_version,\n            \"description\": package.description or \"\",\n            \"optional\": package.optional,\n            \"python-versions\": package.python_versions,\n            \"groups\": sorted(transitive_info.groups, key=group_sort_key),\n        }\n        if transitive_info.markers:\n            if len(markers := set(transitive_info.markers.values())) == 1:\n                if not (marker := next(iter(markers))).is_any():\n                    data[\"markers\"] = str(marker)\n            else:\n                data[\"markers\"] = inline_table()\n                for group, marker in sorted(\n                    transitive_info.markers.items(),\n                    key=lambda x: group_sort_key(x[0]),\n                ):\n                    if not marker.is_any():\n                        data[\"markers\"][group] = str(marker)\n        data[\"files\"] = sorted(\n            [\n                {k: v for k, v in f.items() if k in {\"file\", \"hash\"}}\n                for f in package.files\n            ],\n            key=lambda x: x[\"file\"],\n        )\n\n        if dependencies:\n            data[\"dependencies\"] = table()\n            for dep_name, constraints in dependencies.items():\n                if len(constraints) == 1:\n                    data[\"dependencies\"][dep_name] = constraints[0]\n                else:\n                    data[\"dependencies\"][dep_name] = array().multiline(True)\n                    for constraint in constraints:\n                        data[\"dependencies\"][dep_name].append(constraint)\n\n        if package.extras:\n            extras = {}\n            for name, deps in sorted(package.extras.items()):\n                extras[name] = sorted(dep.to_pep_508(with_extras=False) for dep in deps)\n\n            data[\"extras\"] = extras\n\n        if package.source_url:\n            url = package.source_url\n            if package.source_type in [\"file\", \"directory\"]:\n                # The lock file should only store paths relative to the root project\n                url = Path(\n                    os.path.relpath(\n                        Path(url).resolve(),\n                        Path(self.lock.parent).resolve(),\n                    )\n                ).as_posix()\n\n            data[\"source\"] = {}\n\n            if package.source_type:\n                data[\"source\"][\"type\"] = package.source_type\n\n            data[\"source\"][\"url\"] = url\n\n            if package.source_reference:\n                data[\"source\"][\"reference\"] = package.source_reference\n\n            if package.source_resolved_reference:\n                data[\"source\"][\"resolved_reference\"] = package.source_resolved_reference\n\n            if package.source_subdirectory:\n                data[\"source\"][\"subdirectory\"] = package.source_subdirectory\n\n            if package.source_type in [\"directory\", \"git\"]:\n                data[\"develop\"] = package.develop\n\n        return data\n"
  },
  {
    "path": "src/poetry/packages/package_collection.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.packages.dependency_package import DependencyPackage\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n\nclass PackageCollection(list[DependencyPackage]):\n    def __init__(\n        self,\n        dependency: Dependency,\n        packages: Iterable[Package | DependencyPackage] = (),\n    ) -> None:\n        self._dependency = dependency\n\n        super().__init__()\n\n        for package in packages:\n            self.append(package)\n\n    def append(self, package: Package | DependencyPackage) -> None:\n        if isinstance(package, DependencyPackage):\n            package = package.package\n\n        package = DependencyPackage(self._dependency, package)\n\n        return super().append(package)\n"
  },
  {
    "path": "src/poetry/packages/transitive_package_info.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.version.markers import BaseMarker\nfrom poetry.core.version.markers import EmptyMarker\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from packaging.utils import NormalizedName\n\n\ndef group_sort_key(group: NormalizedName) -> tuple[bool, NormalizedName]:\n    return group != MAIN_GROUP, group\n\n\n@dataclass\nclass TransitivePackageInfo:\n    depth: int  # max depth in the dependency tree\n    groups: set[NormalizedName]\n    markers: dict[NormalizedName, BaseMarker]  # group -> marker\n\n    def get_marker(self, groups: Iterable[NormalizedName]) -> BaseMarker:\n        marker: BaseMarker = EmptyMarker()\n        for group in sorted(groups, key=group_sort_key):\n            if group_marker := self.markers.get(group):\n                marker = marker.union(group_marker)\n        return marker\n"
  },
  {
    "path": "src/poetry/plugins/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.plugins.application_plugin import ApplicationPlugin\nfrom poetry.plugins.plugin import Plugin\n\n\n__all__ = [\"ApplicationPlugin\", \"Plugin\"]\n"
  },
  {
    "path": "src/poetry/plugins/application_plugin.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.plugins.base_plugin import BasePlugin\n\n\nif TYPE_CHECKING:\n    from poetry.console.application import Application\n    from poetry.console.commands.command import Command\n\n\nclass ApplicationPlugin(BasePlugin):\n    \"\"\"\n    Base class for application plugins.\n    \"\"\"\n\n    group = \"poetry.application.plugin\"\n\n    @property\n    def commands(self) -> list[type[Command]]:\n        return []\n\n    def activate(self, application: Application) -> None:\n        for command in self.commands:\n            assert command.name is not None\n            application.command_loader.register_factory(command.name, command)\n"
  },
  {
    "path": "src/poetry/plugins/base_plugin.py",
    "content": "from __future__ import annotations\n\nfrom abc import ABC\nfrom abc import abstractmethod\n\n\nclass BasePlugin(ABC):\n    \"\"\"\n    Base class for all plugin types\n\n    The `activate()` method must be implemented and receives the Poetry instance.\n    \"\"\"\n\n    PLUGIN_API_VERSION = \"1.0.0\"\n\n    @property\n    @abstractmethod\n    def group(self) -> str:\n        \"\"\"\n        Name of entrypoint group the plugin belongs to.\n        \"\"\"\n"
  },
  {
    "path": "src/poetry/plugins/plugin.py",
    "content": "from __future__ import annotations\n\nfrom abc import abstractmethod\nfrom typing import TYPE_CHECKING\n\nfrom poetry.plugins.base_plugin import BasePlugin\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n\n\nclass Plugin(BasePlugin):\n    \"\"\"\n    Generic plugin not related to the console application.\n    \"\"\"\n\n    group = \"poetry.plugin\"\n\n    @abstractmethod\n    def activate(self, poetry: Poetry, io: IO) -> None: ...\n"
  },
  {
    "path": "src/poetry/plugins/plugin_manager.py",
    "content": "from __future__ import annotations\n\nimport hashlib\nimport json\nimport logging\nimport shutil\nimport sys\n\nfrom functools import cached_property\nfrom importlib import metadata\nfrom pathlib import Path\nfrom site import addsitedir\nfrom typing import TYPE_CHECKING\n\nimport tomlkit\n\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.__version__ import __version__\nfrom poetry.installation import Installer\nfrom poetry.packages import Locker\nfrom poetry.plugins.application_plugin import ApplicationPlugin\nfrom poetry.plugins.plugin import Plugin\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.toml import TOMLFile\nfrom poetry.utils._compat import tomllib\nfrom poetry.utils.env import Env\nfrom poetry.utils.env import EnvManager\n\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n    from typing import Any\n\n    from cleo.io.io import IO\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n    from poetry.poetry import Poetry\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass PluginManager:\n    \"\"\"\n    This class registers and activates plugins.\n    \"\"\"\n\n    def __init__(self, group: str) -> None:\n        self._group = group\n        self._plugins: list[Plugin] = []\n\n    @staticmethod\n    def add_project_plugin_path(directory: Path) -> None:\n        from poetry.factory import Factory\n\n        try:\n            pyproject_toml = Factory.locate(directory)\n        except RuntimeError:\n            # no pyproject.toml -> no project plugins\n            return\n\n        plugin_path = pyproject_toml.parent / ProjectPluginCache.PATH\n        if plugin_path.exists():\n            # insert at the beginning to allow overriding dependencies\n            EnvManager.get_system_env(naive=True).sys_path.insert(0, str(plugin_path))\n            # process .pth files (among other things)\n            addsitedir(str(plugin_path))\n\n    @classmethod\n    def ensure_project_plugins(cls, poetry: Poetry, io: IO) -> None:\n        ProjectPluginCache(poetry, io).ensure_plugins()\n\n    def load_plugins(self) -> None:\n        plugin_entrypoints = self.get_plugin_entry_points()\n\n        for ep in plugin_entrypoints:\n            self._load_plugin_entry_point(ep)\n\n    def get_plugin_entry_points(self) -> list[metadata.EntryPoint]:\n        return list(metadata.entry_points(group=self._group))\n\n    def activate(self, *args: Any, **kwargs: Any) -> None:\n        for plugin in self._plugins:\n            plugin.activate(*args, **kwargs)\n\n    def _add_plugin(self, plugin: Plugin) -> None:\n        if not isinstance(plugin, (Plugin, ApplicationPlugin)):\n            raise ValueError(\n                \"The Poetry plugin must be an instance of Plugin or ApplicationPlugin\"\n            )\n\n        self._plugins.append(plugin)\n\n    def _load_plugin_entry_point(self, ep: metadata.EntryPoint) -> None:\n        logger.debug(\"Loading the %s plugin\", ep.name)\n\n        plugin = ep.load()\n\n        if not issubclass(plugin, (Plugin, ApplicationPlugin)):\n            raise ValueError(\n                \"The Poetry plugin must be an instance of Plugin or ApplicationPlugin\"\n            )\n\n        self._add_plugin(plugin())\n\n\nclass ProjectPluginCache:\n    PATH = Path(\".poetry\") / \"plugins\"\n\n    def __init__(self, poetry: Poetry, io: IO) -> None:\n        self._poetry = poetry\n        self._io = io\n        self._path = poetry.pyproject_path.parent / self.PATH\n        self._config_file = self._path / \"config.toml\"\n        self._gitignore_file = self._path.parent / \".gitignore\"\n\n    @property\n    def _plugin_section(self) -> dict[str, Any]:\n        plugins = self._poetry.local_config.get(\"requires-plugins\", {})\n        assert isinstance(plugins, dict)\n        return plugins\n\n    @cached_property\n    def _config(self) -> dict[str, Any]:\n        return {\n            \"python\": sys.version,\n            \"poetry\": __version__,\n            \"plugins-hash\": hashlib.sha256(\n                json.dumps(self._plugin_section, sort_keys=True).encode()\n            ).hexdigest(),\n        }\n\n    def ensure_plugins(self) -> None:\n        from poetry.factory import Factory\n\n        # parse project plugins\n        plugins = []\n        for name, constraints in self._plugin_section.items():\n            _constraints = (\n                constraints if isinstance(constraints, list) else [constraints]\n            )\n            for _constraint in _constraints:\n                plugins.append(Factory.create_dependency(name, _constraint))\n\n        if not plugins:\n            if self._path.exists():\n                self._io.write_line(\n                    \"<info>No project plugins defined.\"\n                    \" Removing the project's plugin cache</info>\"\n                )\n                self._io.write_line(\"\")\n                shutil.rmtree(self._path)\n            return\n\n        if self._is_fresh():\n            if self._io.is_debug():\n                self._io.write_line(\"The project's plugin cache is up to date.\")\n                self._io.write_line(\"\")\n            return\n        elif self._path.exists():\n            self._io.write_line(\n                \"Removing the project's plugin cache because it is outdated\"\n            )\n            # Just remove the cache for two reasons:\n            # 1. Since the path of the cache has already been added to sys.path\n            #    at this point, we had to distinguish between packages installed\n            #    directly into Poetry's env and packages installed in the project cache.\n            # 2. Updating packages in the cache does not work out of the box,\n            #    probably, because we use pip to uninstall and pip does not know\n            #    about the cache so that we end up with just overwriting installed\n            #    packages and multiple dist-info folders per package.\n            # In sum, we keep it simple by always starting from an empty cache\n            # if something has changed.\n            shutil.rmtree(self._path)\n\n        # determine plugins relevant for Poetry's environment\n        poetry_env = EnvManager.get_system_env(naive=True)\n        relevant_plugins = {\n            plugin.name: plugin\n            for plugin in plugins\n            if plugin.marker.validate(poetry_env.marker_env)\n        }\n        if not relevant_plugins:\n            if self._io.is_debug():\n                self._io.write_line(\n                    \"No relevant project plugins for Poetry's environment defined.\"\n                )\n                self._io.write_line(\"\")\n            self._write_config()\n            return\n\n        self._io.write_line(\n            \"<info>Ensuring that the Poetry plugins required\"\n            \" by the project are available...</info>\"\n        )\n\n        # check if required plugins are already available\n        missing_plugin_count = len(relevant_plugins)\n        satisfied_plugins = set()\n        insufficient_plugins = []\n        installed_packages = []\n        installed_repo = InstalledRepository.load(poetry_env)\n        for package in installed_repo.packages:\n            if required_plugin := relevant_plugins.get(package.name):\n                if package.satisfies(required_plugin):\n                    satisfied_plugins.add(package.name)\n                    installed_packages.append(package)\n                else:\n                    insufficient_plugins.append((package, required_plugin))\n                    # Do not add the package to installed_packages so that\n                    # the solver does not consider it.\n                missing_plugin_count -= 1\n                if missing_plugin_count == 0:\n                    break\n            else:\n                installed_packages.append(package)\n\n        if missing_plugin_count == 0 and not insufficient_plugins:\n            # all required plugins are installed and satisfy the requirements\n            self._write_config()\n            self._io.write_line(\n                \"All required plugins have already been installed\"\n                \" in Poetry's environment.\"\n            )\n            self._io.write_line(\"\")\n            return\n\n        if insufficient_plugins and self._io.is_debug():\n            plugins_str = \"\\n\".join(\n                f\"  - {req}\\n    installed: {p}\" for p, req in insufficient_plugins\n            )\n            self._io.write_line(\n                \"The following Poetry plugins are required by the project\"\n                f\" but are not satisfied by the installed versions:\\n{plugins_str}\"\n            )\n\n        # install missing plugins\n        missing_plugins = [\n            plugin\n            for name, plugin in relevant_plugins.items()\n            if name not in satisfied_plugins\n        ]\n        plugins_str = \"\\n\".join(f\"  - {p}\" for p in missing_plugins)\n        self._io.write_line(\n            \"The following Poetry plugins are required by the project\"\n            f\" but are not installed in Poetry's environment:\\n{plugins_str}\\n\"\n            f\"Installing Poetry plugins only for the current project...\"\n        )\n        self._install(missing_plugins, poetry_env, installed_packages)\n        self._io.write_line(\"\")\n        self._write_config()\n\n    def _is_fresh(self) -> bool:\n        if not self._config_file.exists():\n            return False\n\n        with self._config_file.open(\"rb\") as f:\n            stored_config = tomllib.load(f)\n\n        return stored_config == self._config\n\n    def _install(\n        self,\n        plugins: Sequence[Dependency],\n        poetry_env: Env,\n        locked_packages: Sequence[Package],\n    ) -> None:\n        project = ProjectPackage(name=\"poetry-project-instance\", version=\"0\")\n        project.python_versions = \".\".join(str(v) for v in poetry_env.version_info[:3])\n        # consider all packages in Poetry's environment pinned\n        for package in locked_packages:\n            project.add_dependency(package.to_dependency())\n        # add missing plugin dependencies\n        for dependency in plugins:\n            project.add_dependency(dependency)\n\n        # force new package to be installed in the project cache instead of Poetry's env\n        poetry_env.set_paths(purelib=self._path, platlib=self._path)\n\n        self._ensure_cache_directory()\n\n        installer = Installer(\n            self._io,\n            poetry_env,\n            project,\n            Locker(self._path / \"poetry.lock\", {}),\n            self._poetry.pool,\n            self._poetry.config,\n            # Build installed repository from locked packages so that plugins\n            # that may be overwritten are not included.\n            InstalledRepository(locked_packages),\n        )\n        installer.update(True)\n\n        if installer.run() != 0:\n            raise RuntimeError(\"Failed to install required Poetry plugins\")\n\n    def _write_config(self) -> None:\n        self._ensure_cache_directory()\n\n        document = tomlkit.document()\n\n        for key, value in self._config.items():\n            document[key] = value\n\n        TOMLFile(self._config_file).write(data=document)\n\n    def _ensure_cache_directory(self) -> None:\n        if self._path.exists():\n            return\n\n        self._path.mkdir(parents=True, exist_ok=True)\n        # only write .gitignore if path did not exist before\n        self._gitignore_file.write_text(\"*\", encoding=\"utf-8\")\n"
  },
  {
    "path": "src/poetry/poetry.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import cast\n\nfrom poetry.core.poetry import Poetry as BasePoetry\n\nfrom poetry.__version__ import __version__\nfrom poetry.config.source import Source\nfrom poetry.pyproject.toml import PyProjectTOML\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n    from pathlib import Path\n\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.config.config import Config\n    from poetry.packages.locker import Locker\n    from poetry.plugins.plugin_manager import PluginManager\n    from poetry.repositories.repository_pool import RepositoryPool\n    from poetry.toml import TOMLFile\n\n\nclass Poetry(BasePoetry):\n    VERSION = __version__\n\n    def __init__(\n        self,\n        file: Path,\n        local_config: dict[str, Any],\n        package: ProjectPackage,\n        locker: Locker,\n        config: Config,\n        disable_cache: bool = False,\n        *,\n        build_constraints: Mapping[NormalizedName, list[Dependency]] | None = None,\n    ) -> None:\n        from poetry.repositories.repository_pool import RepositoryPool\n\n        super().__init__(file, local_config, package, pyproject_type=PyProjectTOML)\n\n        self._locker = locker\n        self._config = config\n        self._pool = RepositoryPool(config=config)\n        self._plugin_manager: PluginManager | None = None\n        self._disable_cache = disable_cache\n        self._build_constraints = build_constraints or {}\n\n    @property\n    def pyproject(self) -> PyProjectTOML:\n        pyproject = super().pyproject\n        return cast(\"PyProjectTOML\", pyproject)\n\n    @property\n    def file(self) -> TOMLFile:\n        return self.pyproject.file\n\n    @property\n    def locker(self) -> Locker:\n        return self._locker\n\n    @property\n    def pool(self) -> RepositoryPool:\n        return self._pool\n\n    @property\n    def config(self) -> Config:\n        return self._config\n\n    @property\n    def disable_cache(self) -> bool:\n        return self._disable_cache\n\n    @property\n    def build_constraints(self) -> Mapping[NormalizedName, list[Dependency]]:\n        return self._build_constraints\n\n    def set_locker(self, locker: Locker) -> Poetry:\n        self._locker = locker\n\n        return self\n\n    def set_pool(self, pool: RepositoryPool) -> Poetry:\n        self._pool = pool\n\n        return self\n\n    def set_config(self, config: Config) -> Poetry:\n        self._config = config\n\n        return self\n\n    def get_sources(self) -> list[Source]:\n        return [\n            Source(**source)\n            for source in self.pyproject.data.get(\"tool\", {})\n            .get(\"poetry\", {})\n            .get(\"source\", [])\n        ]\n"
  },
  {
    "path": "src/poetry/publishing/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.publishing.publisher import Publisher\n\n\n__all__ = [\"Publisher\"]\n"
  },
  {
    "path": "src/poetry/publishing/hash_manager.py",
    "content": "from __future__ import annotations\n\nimport hashlib\nimport io\n\nfrom contextlib import suppress\nfrom typing import TYPE_CHECKING\nfrom typing import NamedTuple\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\nclass Hexdigest(NamedTuple):\n    md5: str | None\n    sha256: str | None\n    blake2_256: str | None\n\n\nclass HashManager:\n    def __init__(self) -> None:\n        self._sha2_hasher = hashlib.sha256()\n\n        self._md5_hasher = None\n        with suppress(ValueError):\n            # FIPS mode disables MD5\n            self._md5_hasher = hashlib.md5()\n\n        self._blake_hasher = None\n        with suppress(ValueError, TypeError):\n            # FIPS mode disables blake2\n            self._blake_hasher = hashlib.blake2b(digest_size=256 // 8)\n\n    def _md5_update(self, content: bytes) -> None:\n        if self._md5_hasher is not None:\n            self._md5_hasher.update(content)\n\n    def _md5_hexdigest(self) -> str | None:\n        if self._md5_hasher is not None:\n            return self._md5_hasher.hexdigest()\n        return None\n\n    def _blake_update(self, content: bytes) -> None:\n        if self._blake_hasher is not None:\n            self._blake_hasher.update(content)\n\n    def _blake_hexdigest(self) -> str | None:\n        if self._blake_hasher is not None:\n            return self._blake_hasher.hexdigest()\n        return None\n\n    def hash(self, file: Path) -> None:\n        with file.open(\"rb\") as fp:\n            for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b\"\"):\n                self._md5_update(content)\n                self._sha2_hasher.update(content)\n                self._blake_update(content)\n\n    def hexdigest(self) -> Hexdigest:\n        return Hexdigest(\n            self._md5_hexdigest(),\n            self._sha2_hasher.hexdigest(),\n            self._blake_hexdigest(),\n        )\n"
  },
  {
    "path": "src/poetry/publishing/publisher.py",
    "content": "from __future__ import annotations\n\nimport logging\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.publishing.uploader import Uploader\nfrom poetry.utils.authenticator import Authenticator\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n\nlogger = logging.getLogger(__name__)\n\n\nclass Publisher:\n    \"\"\"\n    Registers and publishes packages to remote repositories.\n    \"\"\"\n\n    def __init__(self, poetry: Poetry, io: IO, dist_dir: Path | None = None) -> None:\n        self._poetry = poetry\n        self._package = poetry.package\n        self._io = io\n        self._uploader = Uploader(poetry, io, dist_dir)\n        self._authenticator = Authenticator(poetry.config, self._io)\n\n    @property\n    def files(self) -> list[Path]:\n        return self._uploader.files\n\n    def publish(\n        self,\n        repository_name: str | None,\n        username: str | None,\n        password: str | None,\n        cert: Path | None = None,\n        client_cert: Path | None = None,\n        dry_run: bool = False,\n        skip_existing: bool = False,\n    ) -> None:\n        if not repository_name:\n            url = \"https://upload.pypi.org/legacy/\"\n            repository_name = \"pypi\"\n        else:\n            # Retrieving config information\n            url = self._poetry.config.get(f\"repositories.{repository_name}.url\")\n            if url is None:\n                raise RuntimeError(f\"Repository {repository_name} is not defined\")\n\n        if not (username and password):\n            # Check if we have a token first\n            token = self._authenticator.get_pypi_token(repository_name)\n            if token:\n                logger.debug(\"Found an API token for %s.\", repository_name)\n                username = \"__token__\"\n                password = token\n            else:\n                auth = self._authenticator.get_http_auth(repository_name)\n                if auth:\n                    logger.debug(\n                        \"Found authentication information for %s.\", repository_name\n                    )\n                    username = auth.username\n                    password = auth.password\n\n        certificates = self._authenticator.get_certs_for_repository(repository_name)\n        resolved_cert = cert or certificates.cert or certificates.verify\n        resolved_client_cert = client_cert or certificates.client_cert\n\n        self._uploader.auth(username, password)\n\n        if repository_name == \"pypi\":\n            repository_name = \"PyPI\"\n        self._io.write_line(\n            f\"Publishing <c1>{self._package.pretty_name}</c1>\"\n            f\" (<c2>{self._uploader.version}</c2>) to\"\n            f\" <info>{repository_name}</info>\"\n        )\n\n        self._uploader.upload(\n            url,\n            cert=resolved_cert,\n            client_cert=resolved_client_cert,\n            dry_run=dry_run,\n            skip_existing=skip_existing,\n        )\n"
  },
  {
    "path": "src/poetry/publishing/uploader.py",
    "content": "from __future__ import annotations\n\nimport itertools\nimport tarfile\nimport zipfile\n\nfrom collections import defaultdict\nfrom functools import cached_property\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Literal\n\nimport requests\n\nfrom packaging.metadata import RawMetadata\nfrom packaging.metadata import parse_email\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.masonry.utils.helpers import distribution_name\nfrom requests_toolbelt import user_agent\nfrom requests_toolbelt.multipart import MultipartEncoder\nfrom requests_toolbelt.multipart import MultipartEncoderMonitor\n\nfrom poetry.__version__ import __version__\nfrom poetry.publishing.hash_manager import HashManager\nfrom poetry.utils.constants import REQUESTS_TIMEOUT\nfrom poetry.utils.patterns import wheel_file_re\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n\n\nclass UploadError(Exception):\n    pass\n\n\nclass Uploader:\n    def __init__(self, poetry: Poetry, io: IO, dist_dir: Path | None = None) -> None:\n        self._poetry = poetry\n        self._dist_name = distribution_name(poetry.package.name)\n        self._io = io\n        self._dist_dir = dist_dir or self.default_dist_dir\n        self._username: str | None = None\n        self._password: str | None = None\n\n    @property\n    def user_agent(self) -> str:\n        agent: str = user_agent(\"poetry\", __version__)\n        return agent\n\n    @property\n    def default_dist_dir(self) -> Path:\n        return self._poetry.file.path.parent / \"dist\"\n\n    @property\n    def dist_dir(self) -> Path:\n        if not self._dist_dir.is_absolute():\n            return self._poetry.file.path.parent / self._dist_dir\n\n        return self._dist_dir\n\n    @property\n    def files(self) -> list[Path]:\n        return self._files_and_version[0]\n\n    @property\n    def version(self) -> str:\n        return self._files_and_version[1]\n\n    @cached_property\n    def _files_and_version(self) -> tuple[list[Path], str]:\n        dist = self.dist_dir\n\n        wheels = dist.glob(f\"{self._dist_name}-*-*.whl\")\n        tars = dist.glob(f\"{self._dist_name}-*.tar.gz\")\n        artifacts_by_version = defaultdict(list)\n        for artifact in itertools.chain(wheels, tars):\n            version = (\n                artifact.stem.removesuffix(\".tar\")\n                .removeprefix(f\"{self._dist_name}-\")\n                .split(\"-\", maxsplit=1)[0]\n            )\n            artifacts_by_version[version].append(artifact)\n        match len(artifacts_by_version):\n            case 0:\n                return [], \"\"\n            case 1:\n                latest_version = next(iter(artifacts_by_version))\n                artifacts = artifacts_by_version[latest_version]\n            case _:\n                latest_version = max(\n                    artifacts_by_version, key=lambda v: Version.parse(v)\n                )\n                artifacts = artifacts_by_version[latest_version]\n\n        return sorted(artifacts, key=lambda a: (a.suffix == \".whl\", a)), latest_version\n\n    def auth(self, username: str | None, password: str | None) -> None:\n        self._username = username\n        self._password = password\n\n    def make_session(self) -> requests.Session:\n        session = requests.Session()\n        auth = self.get_auth()\n        if auth is not None:\n            session.auth = auth\n\n        session.headers[\"User-Agent\"] = self.user_agent\n        return session\n\n    def get_auth(self) -> tuple[str, str] | None:\n        if self._username is None or self._password is None:\n            return None\n\n        return (self._username, self._password)\n\n    def upload(\n        self,\n        url: str,\n        cert: Path | bool = True,\n        client_cert: Path | None = None,\n        dry_run: bool = False,\n        skip_existing: bool = False,\n    ) -> None:\n        session = self.make_session()\n\n        session.verify = str(cert) if isinstance(cert, Path) else cert\n\n        if client_cert:\n            session.cert = str(client_cert)\n\n        with session:\n            self._upload(session, url, dry_run, skip_existing)\n\n    @classmethod\n    def post_data(cls, file: Path) -> dict[str, Any]:\n        file_type = cls._get_type(file)\n\n        hash_manager = HashManager()\n        hash_manager.hash(file)\n        file_hashes = hash_manager.hexdigest()\n\n        md5_digest = file_hashes.md5\n        sha2_digest = file_hashes.sha256\n        blake2_256_digest = file_hashes.blake2_256\n\n        py_version: str | None = None\n        if file_type == \"bdist_wheel\":\n            wheel_info = wheel_file_re.match(file.name)\n            if wheel_info is not None:\n                py_version = wheel_info.group(\"pyver\")\n        else:\n            py_version = \"source\"\n\n        data: dict[str, Any] = {\n            # Upload API (https://docs.pypi.org/api/upload/)\n            # \":action\", \"protocol_version\" and \"content are added later\n            \"md5_digest\": md5_digest,\n            \"sha256_digest\": sha2_digest,\n            \"blake2_256_digest\": blake2_256_digest,\n            \"filetype\": file_type,\n            \"pyversion\": py_version,\n        }\n\n        for key, value in cls._get_metadata(file).items():\n            # strip trailing 's' to match API field names\n            # see https://docs.pypi.org/api/upload/\n            if key in {\"platforms\", \"supported_platforms\", \"license_files\"}:\n                key = key[:-1]\n\n            # revert some special cases from packaging.metadata.parse_email()\n\n            # \"keywords\" is not \"multiple use\" but a comma-separated string\n            if key == \"keywords\":\n                assert isinstance(value, list)\n                value = \", \".join(value)\n\n            # \"project_urls\" is not a dict\n            if key == \"project_urls\":\n                assert isinstance(value, dict)\n                value = [f\"{k}, {v}\" for k, v in value.items()]\n\n            data[key] = value\n\n        return data\n\n    def _upload(\n        self,\n        session: requests.Session,\n        url: str,\n        dry_run: bool = False,\n        skip_existing: bool = False,\n    ) -> None:\n        for file in self.files:\n            self._upload_file(session, url, file, dry_run, skip_existing)\n\n    def _upload_file(\n        self,\n        session: requests.Session,\n        url: str,\n        file: Path,\n        dry_run: bool = False,\n        skip_existing: bool = False,\n    ) -> None:\n        from cleo.ui.progress_bar import ProgressBar\n\n        if not file.is_file():\n            raise UploadError(f\"Archive ({file}) does not exist\")\n\n        data = self.post_data(file)\n        data.update({\":action\": \"file_upload\", \"protocol_version\": \"1\"})\n\n        data_to_send: list[tuple[str, Any]] = self._prepare_data(data)\n\n        with file.open(\"rb\") as fp:\n            data_to_send.append(\n                (\"content\", (file.name, fp, \"application/octet-stream\"))\n            )\n            encoder = MultipartEncoder(data_to_send)\n            bar = ProgressBar(self._io, max=encoder.len)\n            bar.set_format(f\" - Uploading <c1>{file.name}</c1> <b>%percent%%</b>\")\n            monitor = MultipartEncoderMonitor(\n                encoder, lambda monitor: bar.set_progress(monitor.bytes_read)\n            )\n\n            bar.start()\n\n            resp = None\n\n            try:\n                if not dry_run:\n                    resp = session.post(\n                        url,\n                        data=monitor,\n                        allow_redirects=False,\n                        headers={\"Content-Type\": monitor.content_type},\n                        timeout=REQUESTS_TIMEOUT,\n                    )\n                if resp is None or 200 <= resp.status_code < 300:\n                    bar.set_format(\n                        f\" - Uploading <c1>{file.name}</c1> <fg=green>%percent%%</>\"\n                    )\n                    bar.finish()\n                elif 300 <= resp.status_code < 400:\n                    if self._io.output.is_decorated():\n                        self._io.overwrite(\n                            f\" - Uploading <c1>{file.name}</c1> <error>FAILED</>\"\n                        )\n                    raise UploadError(\n                        \"Redirects are not supported. \"\n                        \"Is the URL missing a trailing slash?\"\n                    )\n                elif resp.status_code == 400 and \"was ever registered\" in resp.text:\n                    self._register(session, url)\n                    resp.raise_for_status()\n                elif skip_existing and self._is_file_exists_error(resp):\n                    bar.set_format(\n                        f\" - Uploading <c1>{file.name}</c1> <warning>File exists.\"\n                        \" Skipping</>\"\n                    )\n                    bar.display()\n                else:\n                    resp.raise_for_status()\n\n            except requests.RequestException as e:\n                if self._io.output.is_decorated():\n                    self._io.overwrite(\n                        f\" - Uploading <c1>{file.name}</c1> <error>FAILED</>\"\n                    )\n\n                if e.response is not None:\n                    message = (\n                        f\"HTTP Error {e.response.status_code}: \"\n                        f\"{e.response.reason} | {e.response.content!r}\"\n                    )\n                    raise UploadError(message) from e\n\n                raise UploadError(\"Error connecting to repository\") from e\n\n            finally:\n                self._io.write_line(\"\")\n\n    def _register(self, session: requests.Session, url: str) -> requests.Response:\n        \"\"\"\n        Register a package to a repository.\n        \"\"\"\n        data = self.post_data(self.files[0])\n        data.update({\":action\": \"submit\", \"protocol_version\": \"1\"})\n\n        data_to_send = self._prepare_data(data)\n        encoder = MultipartEncoder(data_to_send)\n        resp = session.post(\n            url,\n            data=encoder,\n            allow_redirects=False,\n            headers={\"Content-Type\": encoder.content_type},\n            timeout=REQUESTS_TIMEOUT,\n        )\n\n        resp.raise_for_status()\n\n        return resp\n\n    def _prepare_data(self, data: dict[str, Any]) -> list[tuple[str, str]]:\n        data_to_send = []\n        for key, value in data.items():\n            if not isinstance(value, (list, tuple)):\n                data_to_send.append((key, value))\n            else:\n                for item in value:\n                    data_to_send.append((key, item))\n\n        return data_to_send\n\n    @staticmethod\n    def _get_type(file: Path) -> Literal[\"bdist_wheel\", \"sdist\"]:\n        exts = file.suffixes\n        if exts and exts[-1] == \".whl\":\n            return \"bdist_wheel\"\n        elif len(exts) >= 2 and \"\".join(exts[-2:]) == \".tar.gz\":\n            return \"sdist\"\n\n        raise ValueError(\"Unknown distribution format \" + \"\".join(exts))\n\n    @staticmethod\n    def _get_metadata(file: Path) -> RawMetadata:\n        if file.suffix == \".whl\":\n            with zipfile.ZipFile(file) as z:\n                for name in z.namelist():\n                    parts = Path(name).parts\n                    if (\n                        len(parts) == 2\n                        and parts[1] == \"METADATA\"\n                        and parts[0].endswith(\".dist-info\")\n                    ):\n                        with z.open(name) as mf:\n                            return parse_email(mf.read().decode(\"utf-8\"))[0]\n            raise FileNotFoundError(\"METADATA not found in wheel\")\n\n        elif file.suffixes[-2:] == [\".tar\", \".gz\"]:\n            with tarfile.open(file, \"r:gz\") as tar:\n                for member in tar.getmembers():\n                    parts = Path(member.name).parts\n                    if (\n                        len(parts) == 2\n                        and parts[1] == \"PKG-INFO\"\n                        and (pf := tar.extractfile(member))\n                    ):\n                        return parse_email(pf.read().decode(\"utf-8\"))[0]\n            raise FileNotFoundError(\"PKG-INFO not found in sdist\")\n\n        raise ValueError(f\"Unsupported file type: {file}\")\n\n    def _is_file_exists_error(self, response: requests.Response) -> bool:\n        # based on https://github.com/pypa/twine/blob/a6dd69c79f7b5abfb79022092a5d3776a499e31b/twine/commands/upload.py#L32\n        status = response.status_code\n        reason = response.reason.lower()\n        text = response.text.lower()\n        reason_and_text = reason + text\n\n        return (\n            # pypiserver (https://pypi.org/project/pypiserver)\n            status == 409\n            # PyPI / TestPyPI / GCP Artifact Registry\n            or (status == 400 and \"already exist\" in reason_and_text)\n            # Nexus Repository OSS (https://www.sonatype.com/nexus-repository-oss)\n            or (status == 400 and \"updating asset\" in reason_and_text)\n            or (status == 400 and \"cannot be updated\" in reason_and_text)\n            # Artifactory (https://jfrog.com/artifactory/)\n            or (status == 403 and \"overwrite artifact\" in reason_and_text)\n            # Gitlab Enterprise Edition (https://about.gitlab.com)\n            or (status == 400 and \"already been taken\" in reason_and_text)\n        )\n"
  },
  {
    "path": "src/poetry/puzzle/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.puzzle.solver import Solver\n\n\n__all__ = [\"Solver\"]\n"
  },
  {
    "path": "src/poetry/puzzle/exceptions.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n    from poetry.mixology.failure import SolveFailureError\n\n\nclass SolverProblemError(Exception):\n    def __init__(self, error: SolveFailureError) -> None:\n        self._error = error\n\n        super().__init__(str(error))\n\n    @property\n    def error(self) -> SolveFailureError:\n        return self._error\n\n\nclass OverrideNeededError(Exception):\n    def __init__(self, *overrides: dict[Package, dict[str, Dependency]]) -> None:\n        self._overrides = overrides\n\n    @property\n    def overrides(self) -> tuple[dict[Package, dict[str, Dependency]], ...]:\n        return self._overrides\n"
  },
  {
    "path": "src/poetry/puzzle/provider.py",
    "content": "from __future__ import annotations\n\nimport functools\nimport itertools\nimport logging\nimport re\nimport time\n\nfrom collections import defaultdict\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import ClassVar\nfrom typing import cast\n\nfrom cleo.ui.progress_indicator import ProgressIndicator\nfrom poetry.core.constraints.version import EmptyConstraint\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import VersionRange\nfrom poetry.core.packages.utils.utils import get_python_constraint_from_marker\nfrom poetry.core.version.markers import AnyMarker\nfrom poetry.core.version.markers import parse_marker\nfrom poetry.core.version.markers import union as marker_union\n\nfrom poetry.mixology.incompatibility import Incompatibility\nfrom poetry.mixology.incompatibility_cause import DependencyCauseError\nfrom poetry.mixology.incompatibility_cause import PythonCauseError\nfrom poetry.mixology.term import Term\nfrom poetry.packages import DependencyPackage\nfrom poetry.packages.direct_origin import DirectOrigin\nfrom poetry.packages.package_collection import PackageCollection\nfrom poetry.puzzle.exceptions import OverrideNeededError\nfrom poetry.repositories.repository_pool import Priority\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Collection\n    from collections.abc import Iterable\n    from collections.abc import Iterator\n    from collections.abc import Sequence\n    from pathlib import Path\n\n    from cleo.io.io import IO\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import VersionConstraint\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.directory_dependency import DirectoryDependency\n    from poetry.core.packages.file_dependency import FileDependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.package import PackageFile\n    from poetry.core.packages.url_dependency import URLDependency\n    from poetry.core.packages.vcs_dependency import VCSDependency\n    from poetry.core.version.markers import BaseMarker\n\n    from poetry.repositories import RepositoryPool\n    from poetry.utils.env import Env\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass IncompatibleConstraintsError(Exception):\n    \"\"\"\n    Exception when there are duplicate dependencies with incompatible constraints.\n    \"\"\"\n\n    def __init__(\n        self, package: Package, *dependencies: Dependency, with_sources: bool = False\n    ) -> None:\n        constraints = []\n        for dep in dependencies:\n            constraint = dep.to_pep_508()\n            if dep.is_direct_origin():\n                # add version info because issue might be a version conflict\n                # with a version constraint\n                constraint += f\" ({dep.constraint})\"\n            if with_sources and dep.source_name:\n                constraint += f\" ; source={dep.source_name}\"\n            constraints.append(constraint)\n        super().__init__(\n            f\"Incompatible constraints in requirements of {package}:\\n\"\n            + \"\\n\".join(constraints)\n        )\n\n\nclass Indicator(ProgressIndicator):\n    CONTEXT: str | None = None\n\n    @staticmethod\n    @contextmanager\n    def context() -> Iterator[Callable[[str | None], None]]:\n        def _set_context(context: str | None) -> None:\n            Indicator.CONTEXT = context\n\n        yield _set_context\n\n        _set_context(None)\n\n    def _formatter_context(self) -> str:\n        if Indicator.CONTEXT is None:\n            return \" \"\n        else:\n            return f\" <c1>{Indicator.CONTEXT}</> \"\n\n    def _formatter_elapsed(self) -> str:\n        assert self._start_time is not None\n        elapsed = time.time() - self._start_time\n\n        return f\"{elapsed:.1f}s\"\n\n\nclass Provider:\n    UNSAFE_PACKAGES: ClassVar[set[str]] = set()\n\n    def __init__(\n        self,\n        package: Package,\n        pool: RepositoryPool,\n        io: IO,\n        *,\n        locked: list[Package] | None = None,\n        active_root_extras: Collection[NormalizedName] | None = None,\n    ) -> None:\n        self._package = package\n        self._pool = pool\n        self._direct_origin = DirectOrigin(self._pool.artifact_cache)\n        self._io = io\n        self._env: Env | None = None\n        self._package_python_constraint = package.python_constraint\n        self._is_debugging: bool = self._io.is_debug() or self._io.is_very_verbose()\n        self._overrides: dict[Package, dict[str, Dependency]] = {}\n        self._deferred_cache: dict[Dependency, Package] = {}\n        self._load_deferred = True\n        self._source_root: Path | None = None\n        self._direct_origin_packages: dict[str, Package] = {}\n        self._locked: dict[NormalizedName, list[DependencyPackage]] = defaultdict(list)\n        self._use_latest: Collection[NormalizedName] = []\n        self._active_root_extras = (\n            frozenset(active_root_extras) if active_root_extras is not None else None\n        )\n\n        self._explicit_sources: dict[str, str] = {}\n        for package in locked or []:\n            self._locked[package.name].append(\n                DependencyPackage(package.to_dependency(), package)\n            )\n        for dependency_packages in self._locked.values():\n            dependency_packages.sort(\n                key=lambda p: p.package.version,\n                reverse=True,\n            )\n\n        self.get_package_from_pool = functools.cache(self._pool.package)\n        self._refreshed: set[tuple[str, Version, str | None]] = set()\n\n    @property\n    def pool(self) -> RepositoryPool:\n        return self._pool\n\n    @property\n    def use_latest(self) -> Collection[NormalizedName]:\n        return self._use_latest\n\n    @functools.cached_property\n    def _overrides_marker_intersection(self) -> BaseMarker:\n        overrides_marker_intersection: BaseMarker = AnyMarker()\n        for dep_overrides in self._overrides.values():\n            for dep in dep_overrides.values():\n                overrides_marker_intersection = overrides_marker_intersection.intersect(\n                    dep.marker\n                )\n        return overrides_marker_intersection\n\n    @functools.cached_property\n    def _python_constraint(self) -> VersionConstraint:\n        return self._package_python_constraint.intersect(\n            get_python_constraint_from_marker(self._overrides_marker_intersection)\n        )\n\n    def is_debugging(self) -> bool:\n        return self._is_debugging\n\n    def set_overrides(self, overrides: dict[Package, dict[str, Dependency]]) -> None:\n        self._overrides = overrides\n        self.__dict__.pop(\"_python_constraint\", None)\n        self.__dict__.pop(\"_overrides_marker_intersection\", None)\n\n    def load_deferred(self, load_deferred: bool) -> None:\n        self._load_deferred = load_deferred\n\n    @contextmanager\n    def use_source_root(self, source_root: Path) -> Iterator[Provider]:\n        original_source_root = self._source_root\n        self._source_root = source_root\n\n        try:\n            yield self\n        finally:\n            self._source_root = original_source_root\n\n    @contextmanager\n    def use_environment(self, env: Env) -> Iterator[Provider]:\n        original_python_constraint = self._package_python_constraint\n\n        self._env = env\n        # We use the stable version here to improve support of environments of Python pre-release\n        # versions, e.g. Python 3.14rc2. Without using the stable version here, a dependency with\n        # a marker like `python_version >= \"3.14\"` would not be installed.\n        self._package_python_constraint = Version.parse(\n            env.marker_env[\"python_full_version\"]\n        ).stable\n\n        try:\n            yield self\n        finally:\n            self._env = None\n            self._package_python_constraint = original_python_constraint\n\n    @contextmanager\n    def use_latest_for(self, names: Collection[NormalizedName]) -> Iterator[Provider]:\n        self._use_latest = names\n\n        try:\n            yield self\n        finally:\n            self._use_latest = []\n\n    @staticmethod\n    def validate_package_for_dependency(\n        dependency: Dependency, package: Package\n    ) -> None:\n        if dependency.name != package.name:\n            # For now, the dependency's name must match the actual package's name\n            raise RuntimeError(\n                f\"The dependency name for {dependency.name} does not match the actual\"\n                f\" package's name: {package.name}\"\n            )\n\n    def search_for_direct_origin_dependency(self, dependency: Dependency) -> Package:\n        package = self._deferred_cache.get(dependency)\n        if package is not None:\n            pass\n\n        elif dependency.is_vcs():\n            dependency = cast(\"VCSDependency\", dependency)\n            package = self._search_for_vcs(dependency)\n\n        elif dependency.is_file():\n            dependency = cast(\"FileDependency\", dependency)\n            package = self._search_for_file(dependency)\n\n        elif dependency.is_directory():\n            dependency = cast(\"DirectoryDependency\", dependency)\n            package = self._search_for_directory(dependency)\n\n        elif dependency.is_url():\n            dependency = cast(\"URLDependency\", dependency)\n            package = self._search_for_url(dependency)\n\n        else:\n            raise RuntimeError(\n                f\"{dependency}: unknown direct dependency type {dependency.source_type}\"\n            )\n\n        if dependency.is_vcs():\n            dependency._source_reference = package.source_reference\n            dependency._source_resolved_reference = package.source_resolved_reference\n            dependency._source_subdirectory = package.source_subdirectory\n\n        dependency._constraint = package.version\n        dependency._pretty_constraint = package.version.text\n\n        self._deferred_cache[dependency] = package\n\n        return package\n\n    def search_for(self, dependency: Dependency) -> list[DependencyPackage]:\n        \"\"\"\n        Search for the specifications that match the given dependency.\n\n        The specifications in the returned list will be considered in reverse\n        order, so the latest version ought to be last.\n        \"\"\"\n        if dependency.is_root:\n            return PackageCollection(dependency, [self._package])\n\n        if dependency.is_direct_origin():\n            package = self.search_for_direct_origin_dependency(dependency)\n            self._direct_origin_packages[dependency.name] = package\n            return PackageCollection(dependency, [package])\n\n        # If we've previously found a direct-origin package that meets this dependency,\n        # use it.\n        #\n        # We rely on the VersionSolver resolving direct-origin dependencies first.\n        direct_origin_package = self._direct_origin_packages.get(dependency.name)\n        if direct_origin_package and direct_origin_package.satisfies(dependency):\n            packages = [direct_origin_package]\n            return PackageCollection(dependency, packages)\n\n        packages = self._pool.find_packages(dependency)\n\n        packages.sort(\n            key=lambda p: (\n                not p.yanked,\n                not p.is_prerelease() and not dependency.allows_prereleases(),\n                p.version,\n            ),\n            reverse=True,\n        )\n\n        return PackageCollection(dependency, packages)\n\n    def _search_for_vcs(self, dependency: VCSDependency) -> Package:\n        \"\"\"\n        Search for the specifications that match the given VCS dependency.\n\n        Basically, we clone the repository in a temporary directory\n        and get the information we need by checking out the specified reference.\n        \"\"\"\n        package = self._direct_origin.get_package_from_vcs(\n            dependency.vcs,\n            dependency.source,\n            branch=dependency.branch,\n            tag=dependency.tag,\n            rev=dependency.rev,\n            subdirectory=dependency.source_subdirectory,\n            source_root=self._source_root\n            or (self._env.path.joinpath(\"src\") if self._env else None),\n        )\n\n        self.validate_package_for_dependency(dependency=dependency, package=package)\n\n        package.develop = dependency.develop\n\n        return package\n\n    def _search_for_file(self, dependency: FileDependency) -> Package:\n        dependency.validate(raise_error=True)\n        package = self._direct_origin.get_package_from_file(dependency.full_path)\n\n        self.validate_package_for_dependency(dependency=dependency, package=package)\n\n        if dependency.base is not None:\n            package.root_dir = dependency.base\n\n        return package\n\n    def _search_for_directory(self, dependency: DirectoryDependency) -> Package:\n        dependency.validate(raise_error=True)\n        package = self._direct_origin.get_package_from_directory(dependency.full_path)\n\n        self.validate_package_for_dependency(dependency=dependency, package=package)\n\n        package.develop = dependency.develop\n\n        if dependency.base is not None:\n            package.root_dir = dependency.base\n\n        return package\n\n    def _search_for_url(self, dependency: URLDependency) -> Package:\n        package = self._direct_origin.get_package_from_url(dependency.url)\n\n        self.validate_package_for_dependency(dependency=dependency, package=package)\n\n        for extra in sorted(dependency.extras):\n            if extra in package.extras:\n                for dep in package.extras[extra]:\n                    dep.activate()\n\n                for extra_dep in package.extras[extra]:\n                    package.add_dependency(extra_dep)\n\n        return package\n\n    def _get_dependencies_with_overrides(\n        self, dependencies: list[Dependency], package: Package\n    ) -> list[Dependency]:\n        overrides = self._overrides.get(package, {})\n        _dependencies = []\n        overridden = []\n        for dep in dependencies:\n            if dep.name in overrides:\n                if dep.name in overridden:\n                    continue\n\n                # empty constraint is used in overrides to mark that the package has\n                # already been handled and is not required for the attached markers\n                if not overrides[dep.name].constraint.is_empty():\n                    _dependencies.append(overrides[dep.name])\n                overridden.append(dep.name)\n\n                continue\n\n            _dependencies.append(dep)\n        return _dependencies\n\n    def incompatibilities_for(\n        self, dependency_package: DependencyPackage\n    ) -> list[Incompatibility]:\n        \"\"\"\n        Returns incompatibilities that encapsulate a given package's dependencies,\n        or that it can't be safely selected.\n\n        If multiple subsequent versions of this package have the same\n        dependencies, this will return incompatibilities that reflect that. It\n        won't return incompatibilities that have already been returned by a\n        previous call to _incompatibilities_for().\n        \"\"\"\n        package = dependency_package.package\n        if package.is_root():\n            dependencies = package.all_requires\n        else:\n            dependencies = package.requires\n\n            if not package.python_constraint.allows_all(self._python_constraint):\n                transitive_python_constraint = get_python_constraint_from_marker(\n                    dependency_package.dependency.transitive_marker\n                )\n                intersection = package.python_constraint.intersect(\n                    transitive_python_constraint\n                )\n                difference = transitive_python_constraint.difference(intersection)\n\n                # The difference is only relevant if it intersects\n                # the root package python constraint\n                difference = difference.intersect(self._python_constraint)\n                if (\n                    transitive_python_constraint.is_any()\n                    or self._python_constraint.intersect(\n                        dependency_package.dependency.python_constraint\n                    ).is_empty()\n                    or intersection.is_empty()\n                    or not difference.is_empty()\n                ):\n                    return [\n                        Incompatibility(\n                            [Term(package.to_dependency(), True)],\n                            PythonCauseError(\n                                package.python_versions, str(self._python_constraint)\n                            ),\n                        )\n                    ]\n\n        return [\n            Incompatibility(\n                [Term(package.to_dependency(), True), Term(dep, False)],\n                DependencyCauseError(),\n            )\n            for dep in self._get_dependencies_with_overrides(dependencies, package)\n        ]\n\n    @staticmethod\n    def _files_list_for_cmp(files: Sequence[PackageFile]) -> list[str]:\n        \"\"\"\n        :return: A list of strings representing the files and their hashes, for\n            the purpose of comparing the file list to another one.\n            We only use file+hash, because that's what uniquely identifies the file,\n            the other properties (like URL) are not relevant.\n        \"\"\"\n        return sorted(f[\"file\"] + f[\"hash\"] for f in files)\n\n    def complete_package(\n        self, dependency_package: DependencyPackage\n    ) -> DependencyPackage:\n        package = dependency_package.package\n        dependency = dependency_package.dependency\n\n        if package.is_root():\n            dependency_package = dependency_package.clone()\n            package = dependency_package.package\n            dependency = dependency_package.dependency\n            requires = package.all_requires\n        elif package.is_direct_origin():\n            requires = package.requires\n        else:\n            if (\n                package.pretty_name,\n                package.version,\n                dependency.source_name,\n            ) in self._refreshed:\n                # circumvent lru_cache to avoid unnecessary refresh\n                pool_package = self.pool.package(\n                    package.pretty_name,\n                    package.version,\n                    repository_name=dependency.source_name,\n                )\n            else:\n                pool_package = self.get_package_from_pool(\n                    package.pretty_name,\n                    package.version,\n                    repository_name=dependency.source_name,\n                )\n            if package.files and self._files_list_for_cmp(\n                package.files\n            ) != self._files_list_for_cmp(pool_package.files):\n                # This happens if additional artifacts are uploaded later. Either our own cache\n                # is outdated or the lockfile has been created with an outdated cache.\n                # Refresh to cover the first case. (It does not hurt much in the second case.)\n                pool_package = self.pool.refresh(pool_package)\n                self._refreshed.add(\n                    (package.pretty_name, package.version, dependency.source_name)\n                )\n            dependency_package = DependencyPackage(dependency, pool_package)\n\n            package = dependency_package.package\n            dependency = dependency_package.dependency\n            requires = package.requires\n\n        found_extras = set()\n        optional_dependencies = set()\n        _dependencies = []\n\n        if dependency.extras:\n            # Find all the optional dependencies that are wanted - taking care to allow\n            # for self-referential extras.\n            stack = sorted(dependency.extras)\n            while stack:\n                extra = stack.pop()\n                if extra in found_extras:\n                    continue\n                found_extras.add(extra)\n\n                extra_dependencies = package.extras.get(extra, [])\n                for extra_dependency in extra_dependencies:\n                    if extra_dependency.name == dependency.name:\n                        stack += sorted(extra_dependency.extras)\n                    else:\n                        optional_dependencies.add(extra_dependency.name)\n\n            # If some extras/features were required, we need to add a special dependency\n            # representing the base package to the current package.\n\n            dependency_package = dependency_package.with_features(dependency.extras)\n            package = dependency_package.package\n            dependency = dependency_package.dependency\n            new_dependency = package.without_features().to_dependency()\n            new_dependency.marker = dependency.marker\n\n            # When adding dependency foo[extra] -> foo, preserve foo's source, if it's\n            # specified. This prevents us from trying to get foo from PyPI\n            # when user explicitly set repo for foo[extra].\n            if not new_dependency.source_name and dependency.source_name:\n                new_dependency.source_name = dependency.source_name\n\n            _dependencies.append(new_dependency)\n\n        for dep in requires:\n            if not self._python_constraint.allows_any(dep.python_constraint):\n                continue\n\n            if dep.name in self.UNSAFE_PACKAGES:\n                continue\n\n            if self._env:\n                marker_values = (\n                    self._marker_values(self._active_root_extras)\n                    if package.is_root()\n                    else self._env.marker_env\n                )\n                if not dep.marker.validate(marker_values):\n                    continue\n\n            if not package.is_root() and (\n                (dep.is_optional() and dep.name not in optional_dependencies)\n                or (dep.in_extras and not set(dep.in_extras).intersection(found_extras))\n            ):\n                continue\n\n            # For normal dependency resolution, we have to make sure that root extras\n            # are represented in the markers. This is required to identify mutually\n            # exclusive markers in cases like 'extra == \"foo\"' and 'extra != \"foo\"'.\n            # However, for installation with re-resolving (installer.re-resolve=true,\n            # which results in self._env being not None), this spoils the result\n            # because we have to keep extras so that they are uninstalled\n            # when calculating the operations of the transaction.\n            if self._env is None and package.is_root() and dep.in_extras:\n                # The clone is required for installation with re-resolving\n                # without an existing lock file because the root package is used\n                # once for solving and a second time for re-resolving for installation.\n                dep = dep.clone()\n                dep.marker = dep.marker.intersect(\n                    parse_marker(\n                        \" or \".join(f'extra == \"{extra}\"' for extra in dep.in_extras)\n                    )\n                )\n\n            _dependencies.append(dep)\n\n        if self._load_deferred:\n            # Retrieving constraints for deferred dependencies\n            for dep in _dependencies:\n                if dep.is_direct_origin():\n                    locked = self.get_locked(dep)\n                    # If lock file contains exactly the same URL and reference\n                    # (commit hash) of dependency as is requested,\n                    # do not analyze it again: nothing could have changed.\n                    if locked is not None and locked.package.is_same_package_as(dep):\n                        continue\n                    self.search_for_direct_origin_dependency(dep)\n\n        dependencies = self._get_dependencies_with_overrides(_dependencies, package)\n\n        # Searching for duplicate dependencies\n        #\n        # If the duplicate dependencies have the same constraint,\n        # the requirements will be merged.\n        #\n        # For instance:\n        #   • enum34; python_version==\"2.7\"\n        #   • enum34; python_version==\"3.3\"\n        #\n        # will become:\n        #   • enum34; python_version==\"2.7\" or python_version==\"3.3\"\n        #\n        # If the duplicate dependencies have different constraints\n        # we have to split the dependency graph.\n        #\n        # An example of this is:\n        #   • pypiwin32 (220); sys_platform == \"win32\" and python_version >= \"3.6\"\n        #   • pypiwin32 (219); sys_platform == \"win32\" and python_version < \"3.6\"\n        duplicates: dict[str, list[Dependency]] = defaultdict(list)\n        for dep in dependencies:\n            duplicates[dep.name].append(dep)\n\n        dependencies = []\n        for dep_name, deps in duplicates.items():\n            if len(deps) == 1:\n                dependencies.append(deps[0])\n                continue\n\n            self.debug(f\"<debug>Duplicate dependencies for {dep_name}</debug>\")\n\n            # For dependency resolution, markers of duplicate dependencies must be\n            # mutually exclusive. However, we have to take care about duplicates\n            # with differing extras.\n            duplicates_by_extras: dict[str, list[Dependency]] = defaultdict(list)\n            for dep in deps:\n                duplicates_by_extras[dep.complete_name].append(dep)\n\n            if len(duplicates_by_extras) == 1:\n                active_extras = (\n                    self._active_root_extras if package.is_root() else found_extras\n                )\n                deps = self._resolve_overlapping_markers(package, deps, active_extras)\n            else:\n                # There are duplicates with different extras.\n                for complete_dep_name, deps_by_extra in duplicates_by_extras.items():\n                    if len(deps_by_extra) > 1:\n                        duplicates_by_extras[complete_dep_name] = (\n                            self._resolve_overlapping_markers(\n                                package, deps_by_extra, None\n                            )\n                        )\n                if all(len(d) == 1 for d in duplicates_by_extras.values()) and all(\n                    d1[0].marker.intersect(d2[0].marker).is_empty()\n                    for d1, d2 in itertools.combinations(\n                        duplicates_by_extras.values(), 2\n                    )\n                ):\n                    # Since all markers are mutually exclusive,\n                    # we can trigger overrides.\n                    deps = list(itertools.chain(*duplicates_by_extras.values()))\n                else:\n                    # Too complicated to handle with overrides,\n                    # fallback to basic handling without overrides.\n                    for d in duplicates_by_extras.values():\n                        dependencies.extend(d)\n                    continue\n\n            if len(deps) == 1:\n                self.debug(f\"<debug>Merging requirements for {dep_name}</debug>\")\n                dependencies.append(deps[0])\n                continue\n\n            # Sort out irrelevant requirements\n            deps = [\n                dep\n                for dep in deps\n                if not self._overrides_marker_intersection.intersect(\n                    dep.marker\n                ).is_empty()\n            ]\n            if len(deps) < 2:\n                if not deps or (len(deps) == 1 and deps[0].constraint.is_empty()):\n                    msg = f\"<debug>No relevant requirements for {dep_name}</debug>\"\n                else:\n                    msg = f\"<debug>Only one relevant requirement for {dep_name}</debug>\"\n                    dependencies.append(deps[0])\n                self.debug(msg)\n                continue\n\n            # At this point, we raise an exception that will\n            # tell the solver to make new resolutions with specific overrides.\n            #\n            # For instance, if the foo (1.2.3) package has the following dependencies:\n            #   • bar (>=2.0) ; python_version >= \"3.6\"\n            #   • bar (<2.0) ; python_version < \"3.6\"\n            #\n            # then the solver will need to make two new resolutions\n            # with the following overrides:\n            #   • {<Package foo (1.2.3): {\"bar\": <Dependency bar (>=2.0)>}\n            #   • {<Package foo (1.2.3): {\"bar\": <Dependency bar (<2.0)>}\n\n            def fmt_warning(d: Dependency) -> str:\n                dependency_marker = d.marker if not d.marker.is_any() else \"*\"\n                return (\n                    f\"<c1>{d.name}</c1> <fg=default>(<c2>{d.pretty_constraint}</c2>)</>\"\n                    f\" with markers <b>{dependency_marker}</b>\"\n                )\n\n            warnings = \", \".join(fmt_warning(d) for d in deps[:-1])\n            warnings += f\" and {fmt_warning(deps[-1])}\"\n            self.debug(\n                f\"<warning>Different requirements found for {warnings}.</warning>\"\n            )\n\n            overrides = []\n            for dep in deps:\n                current_overrides = self._overrides.copy()\n                package_overrides = current_overrides.get(package, {}).copy()\n                package_overrides.update({dep.name: dep})\n                current_overrides.update({package: package_overrides})\n                overrides.append(current_overrides)\n\n            raise OverrideNeededError(*overrides)\n\n        # Modifying dependencies as needed\n        clean_dependencies = []\n        for dep in dependencies:\n            if not dependency.transitive_marker.without_extras().is_any():\n                transitive_marker_intersection = (\n                    dependency.transitive_marker.without_extras().intersect(\n                        dep.marker.without_extras()\n                    )\n                )\n                if transitive_marker_intersection.is_empty():\n                    # The dependency is not needed, since the markers specified\n                    # for the current package selection are not compatible with\n                    # the markers for the current dependency, so we skip it\n                    continue\n\n                dep.transitive_marker = transitive_marker_intersection\n\n            if not dependency.python_constraint.is_any():\n                python_constraint_intersection = dep.python_constraint.intersect(\n                    dependency.python_constraint\n                )\n                if python_constraint_intersection.is_empty():\n                    # This dependency is not needed under current python constraint.\n                    continue\n\n            clean_dependencies.append(dep)\n\n        package = package.with_dependency_groups([], only=True)\n        dependency_package = DependencyPackage(dependency, package)\n\n        for dep in clean_dependencies:\n            package.add_dependency(dep)\n\n        if self._locked and package.is_root():\n            # At this point all duplicates have been eliminated via overrides\n            # so that explicit sources are unambiguous.\n            # Clear _explicit_sources because it might be filled\n            # from a previous override.\n            self._explicit_sources.clear()\n            for dep in clean_dependencies:\n                if dep.source_name:\n                    self._explicit_sources[dep.name] = dep.source_name\n\n        return dependency_package\n\n    def get_locked(self, dependency: Dependency) -> DependencyPackage | None:\n        if dependency.name in self._use_latest:\n            return None\n\n        locked = self._locked.get(dependency.name, [])\n        for dependency_package in locked:\n            package = dependency_package.package\n            if package.satisfies(dependency):\n                if explicit_source := self._explicit_sources.get(dependency.name):\n                    dependency.source_name = explicit_source\n                elif (\n                    not dependency.source_name\n                    and package.source_type == \"legacy\"\n                    and package.source_reference\n                    and self._pool.get_priority(package.source_reference)\n                    == Priority.EXPLICIT\n                ):\n                    continue\n                return DependencyPackage(dependency, package)\n        return None\n\n    def debug(self, message: str, depth: int = 0) -> None:\n        if not (self._io.is_very_verbose() or self._io.is_debug()):\n            return\n\n        if message.startswith(\"fact:\"):\n            if \"depends on\" in message:\n                m = re.match(r\"fact: (.+?) depends on (.+?) \\((.+?)\\)\", message)\n                if m is None:\n                    raise ValueError(f\"Unable to parse fact: {message}\")\n                m2 = re.match(r\"(.+?) \\((.+?)\\)\", m.group(1))\n                if m2:\n                    name = m2.group(1)\n                    version = f\" (<c2>{m2.group(2)}</c2>)\"\n                else:\n                    name = m.group(1)\n                    version = \"\"\n\n                message = (\n                    f\"<fg=blue>fact</>: <c1>{name}</c1>{version} \"\n                    f\"depends on <c1>{m.group(2)}</c1> (<c2>{m.group(3)}</c2>)\"\n                )\n            elif \" is \" in message:\n                message = re.sub(\n                    \"fact: (.+) is (.+)\",\n                    \"<fg=blue>fact</>: <c1>\\\\1</c1> is <c2>\\\\2</c2>\",\n                    message,\n                )\n            else:\n                message = re.sub(\n                    r\"(?<=: )(.+?) \\((.+?)\\)\", \"<c1>\\\\1</c1> (<c2>\\\\2</c2>)\", message\n                )\n                message = f\"<fg=blue>fact</>: {message.split('fact: ')[1]}\"\n        elif message.startswith(\"selecting \"):\n            message = re.sub(\n                r\"selecting (.+?) \\((.+?)\\)\",\n                \"<fg=blue>selecting</> <c1>\\\\1</c1> (<c2>\\\\2</c2>)\",\n                message,\n            )\n        elif message.startswith(\"derived:\"):\n            m = re.match(r\"derived: (.+?) \\((.+?)\\)$\", message)\n            if m:\n                message = (\n                    f\"<fg=blue>derived</>: <c1>{m.group(1)}</c1>\"\n                    f\" (<c2>{m.group(2)}</c2>)\"\n                )\n            else:\n                message = (\n                    f\"<fg=blue>derived</>: <c1>{message.split('derived: ')[1]}</c1>\"\n                )\n        elif message.startswith(\"conflict:\"):\n            m = re.match(r\"conflict: (.+?) depends on (.+?) \\((.+?)\\)\", message)\n            if m:\n                m2 = re.match(r\"(.+?) \\((.+?)\\)\", m.group(1))\n                if m2:\n                    name = m2.group(1)\n                    version = f\" (<c2>{m2.group(2)}</c2>)\"\n                else:\n                    name = m.group(1)\n                    version = \"\"\n\n                message = (\n                    f\"<fg=red;options=bold>conflict</>: <c1>{name}</c1>{version} \"\n                    f\"depends on <c1>{m.group(2)}</c1> (<c2>{m.group(3)}</c2>)\"\n                )\n            else:\n                message = (\n                    \"<fg=red;options=bold>conflict</>:\"\n                    f\" {message.split('conflict: ')[1]}\"\n                )\n\n        message = message.replace(\"! \", \"<error>!</error> \")\n\n        if self.is_debugging():\n            debug_info = str(message)\n            debug_info = (\n                \"\\n\".join(\n                    [\n                        f\"<debug>{str(depth).rjust(4)}:</debug> {s}\"\n                        for s in debug_info.split(\"\\n\")\n                    ]\n                )\n                + \"\\n\"\n            )\n\n            self._io.write(debug_info)\n\n    def _group_by_source(\n        self, dependencies: Iterable[Dependency]\n    ) -> list[list[Dependency]]:\n        \"\"\"\n        Takes a list of dependencies and returns a list of groups of dependencies,\n        each group containing all dependencies from the same source.\n        \"\"\"\n        groups: list[list[Dependency]] = []\n        for dep in dependencies:\n            for group in groups:\n                if (\n                    dep.is_same_source_as(group[0])\n                    and dep.source_name == group[0].source_name\n                ):\n                    group.append(dep)\n                    break\n            else:\n                groups.append([dep])\n        return groups\n\n    def _merge_dependencies_by_constraint(\n        self, dependencies: Iterable[Dependency]\n    ) -> list[Dependency]:\n        \"\"\"\n        Merge dependencies with the same constraint\n        by building a union of their markers.\n\n        For instance, if we have:\n           - foo (>=2.0) ; python_version >= \"3.6\" and python_version < \"3.7\"\n           - foo (>=2.0) ; python_version >= \"3.7\"\n        we can avoid two overrides by merging them to:\n           - foo (>=2.0) ; python_version >= \"3.6\"\n        \"\"\"\n        dep_groups = self._group_by_source(dependencies)\n        merged_dependencies = []\n        for group in dep_groups:\n            by_constraint: dict[VersionConstraint, list[Dependency]] = defaultdict(list)\n            for dep in group:\n                by_constraint[dep.constraint].append(dep)\n            for deps in by_constraint.values():\n                dep = deps[0]\n                if len(deps) > 1:\n                    new_markers = (dep.marker for dep in deps)\n                    dep.marker = marker_union(*new_markers)\n                merged_dependencies.append(dep)\n\n        return merged_dependencies\n\n    def _is_relevant_marker(\n        self, marker: BaseMarker, active_extras: Collection[NormalizedName] | None\n    ) -> bool:\n        \"\"\"\n        A marker is relevant if\n        - it is not empty\n        - allowed by the project's python constraint\n        - allowed by active extras of the dependency (not relevant for root package)\n        - allowed by the environment (only during installation)\n        \"\"\"\n        return (\n            not marker.is_empty()\n            and self._python_constraint.allows_any(\n                get_python_constraint_from_marker(marker)\n            )\n            and (active_extras is None or marker.validate({\"extra\": active_extras}))\n            and (not self._env or marker.validate(self._env.marker_env))\n        )\n\n    def _resolve_overlapping_markers(\n        self,\n        package: Package,\n        dependencies: list[Dependency],\n        active_extras: Collection[NormalizedName] | None,\n    ) -> list[Dependency]:\n        \"\"\"\n        Convert duplicate dependencies with potentially overlapping markers\n        into duplicate dependencies with mutually exclusive markers.\n\n        Therefore, the intersections of all combinations of markers and inverted markers\n        have to be calculated. If such an intersection is relevant (not empty, etc.),\n        the intersection of all constraints, whose markers were not inverted is built\n        and a new dependency with the calculated version constraint and marker is added.\n        (The marker of such a dependency does not overlap with the marker\n        of any other new dependency.)\n        \"\"\"\n        # In order to reduce the number of intersections,\n        # we merge duplicate dependencies by constraint.\n        dependencies = self._merge_dependencies_by_constraint(dependencies)\n\n        new_dependencies = []\n        for uses in itertools.product([True, False], repeat=len(dependencies)):\n            # intersection of markers\n            # For performance optimization, we don't just intersect all markers at once,\n            # but intersect them one after the other to get empty markers early.\n            # Further, we intersect the inverted markers at last because\n            # they are more likely to overlap than the non-inverted ones.\n            markers = (\n                dep.marker if use else dep.marker.invert()\n                for use, dep in sorted(\n                    zip(uses, dependencies), key=lambda ud: ud[0], reverse=True\n                )\n            )\n            used_marker_intersection: BaseMarker = AnyMarker()\n            for m in markers:\n                used_marker_intersection = used_marker_intersection.intersect(m)\n            if not self._is_relevant_marker(used_marker_intersection, active_extras):\n                continue\n\n            # intersection of constraints\n            constraint: VersionConstraint = VersionRange()\n            specific_source_dependency = None\n            used_dependencies = list(itertools.compress(dependencies, uses))\n            for dep in used_dependencies:\n                if dep.is_direct_origin() or dep.source_name:\n                    # if direct origin or specific source:\n                    # conflict if specific source already set and not the same\n                    if specific_source_dependency and (\n                        not dep.is_same_source_as(specific_source_dependency)\n                        or dep.source_name != specific_source_dependency.source_name\n                    ):\n                        raise IncompatibleConstraintsError(\n                            package, dep, specific_source_dependency, with_sources=True\n                        )\n                    specific_source_dependency = dep\n                constraint = constraint.intersect(dep.constraint)\n            if constraint.is_empty():\n                # conflict in overlapping area\n                raise IncompatibleConstraintsError(package, *used_dependencies)\n\n            if not any(uses):\n                # This is an edge case where the dependency is not required\n                # for the resulting marker. However, we have to consider it anyway\n                #  in order to not miss other dependencies later, for instance:\n                #   • foo (1.0) ; python == 3.7\n                #   • foo (2.0) ; python == 3.8\n                #   • bar (2.0) ; python == 3.8\n                #   • bar (3.0) ; python == 3.9\n                # the last dependency would be missed without this,\n                # because the intersection with both foo dependencies is empty.\n\n                # Set constraint to empty to mark dependency as \"not required\".\n                constraint = EmptyConstraint()\n                used_dependencies = dependencies\n\n            # build new dependency with intersected constraint and marker\n            # (and correct source)\n            new_dep = (\n                specific_source_dependency\n                if specific_source_dependency\n                else used_dependencies[0]\n            ).with_constraint(constraint)\n            new_dep.marker = used_marker_intersection\n            new_dependencies.append(new_dep)\n\n        # In order to reduce the number of overrides we merge duplicate\n        # dependencies by constraint again. After overlapping markers were\n        # resolved, there might be new dependencies with the same constraint.\n        return self._merge_dependencies_by_constraint(new_dependencies)\n\n    def _marker_values(\n        self, extras: Collection[NormalizedName] | None = None\n    ) -> dict[str, Any]:\n        \"\"\"\n        Marker values, from `self._env` if present plus the supplied extras\n\n        :param extras: the values to add to the 'extra' marker value\n        \"\"\"\n        result = dict(self._env.marker_env) if self._env is not None else {}\n        if extras is not None:\n            assert \"extra\" not in result, (\n                \"'extra' marker key is already present in environment\"\n            )\n            result[\"extra\"] = set(extras)\n        return result\n"
  },
  {
    "path": "src/poetry/puzzle/solver.py",
    "content": "from __future__ import annotations\n\nimport functools\nimport time\n\nfrom collections import defaultdict\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.version.markers import AnyMarker\nfrom poetry.core.version.markers import EmptyMarker\nfrom poetry.core.version.markers import MultiMarker\nfrom poetry.core.version.markers import SingleMarker\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.mixology import resolve_version\nfrom poetry.mixology.failure import SolveFailureError\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\nfrom poetry.puzzle.exceptions import OverrideNeededError\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.puzzle.provider import Indicator\nfrom poetry.puzzle.provider import Provider\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n    from collections.abc import Iterator\n    from collections.abc import Sequence\n\n    from cleo.io.io import IO\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import VersionConstraint\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.project_package import ProjectPackage\n    from poetry.core.version.markers import BaseMarker\n    from typing_extensions import Self\n\n    from poetry.puzzle.transaction import Transaction\n    from poetry.repositories import RepositoryPool\n    from poetry.utils.env import Env\n\n    # markers[child_package][parent_package][groups] -> BaseMarker\n    MarkerOriginDict = defaultdict[\n        Package,\n        defaultdict[Package, defaultdict[frozenset[NormalizedName], BaseMarker]],\n    ]\n\n\nclass Solver:\n    def __init__(\n        self,\n        package: ProjectPackage,\n        pool: RepositoryPool,\n        installed: list[Package],\n        locked: list[Package],\n        io: IO,\n        active_root_extras: Collection[NormalizedName] | None = None,\n    ) -> None:\n        self._package = package\n        self._pool = pool\n        self._installed_packages = installed\n        self._locked_packages = locked\n        self._io = io\n\n        self._provider = Provider(\n            self._package,\n            self._pool,\n            self._io,\n            locked=locked,\n            active_root_extras=active_root_extras,\n        )\n        self._overrides: list[dict[Package, dict[str, Dependency]]] = []\n\n    @property\n    def provider(self) -> Provider:\n        return self._provider\n\n    @contextmanager\n    def use_environment(self, env: Env) -> Iterator[None]:\n        with self.provider.use_environment(env):\n            yield\n\n    def solve(\n        self, use_latest: Collection[NormalizedName] | None = None\n    ) -> Transaction:\n        from poetry.puzzle.transaction import Transaction\n\n        with self._progress(), self._provider.use_latest_for(use_latest or []):\n            start = time.time()\n            packages = self._solve()\n            # simplify markers by removing redundant information\n            for transitive_info in packages.values():\n                for group, marker in transitive_info.markers.items():\n                    transitive_info.markers[group] = simplify_marker(\n                        marker, self._package.python_constraint\n                    )\n            end = time.time()\n\n            if len(self._overrides) > 1:\n                self._provider.debug(\n                    # ignore the warning as provider does not do interpolation\n                    f\"Complete version solving took {end - start:.3f}\"\n                    f\" seconds with {len(self._overrides)} overrides\"\n                )\n                self._provider.debug(\n                    # ignore the warning as provider does not do interpolation\n                    \"Resolved with overrides:\"\n                    f\" {', '.join(f'({b})' for b in self._overrides)}\"\n                )\n\n        for p in packages:\n            if p.yanked:\n                message = (\n                    f\"The locked version {p.pretty_version} for {p.pretty_name} is a\"\n                    \" yanked version.\"\n                )\n                if p.yanked_reason:\n                    message += f\" Reason for being yanked: {p.yanked_reason}\"\n                self._io.write_error_line(f\"<warning>Warning: {message}</warning>\")\n\n        return Transaction(\n            self._locked_packages,\n            packages,\n            installed_packages=self._installed_packages,\n            root_package=self._package,\n        )\n\n    @contextmanager\n    def _progress(self) -> Iterator[None]:\n        if not self._io.output.is_decorated() or self._provider.is_debugging():\n            self._io.write_line(\"Resolving dependencies...\")\n            yield\n        else:\n            indicator = Indicator(\n                self._io, \"{message}{context}<debug>({elapsed:2s})</debug>\"\n            )\n\n            with indicator.auto(\n                \"<info>Resolving dependencies...</info>\",\n                \"<info>Resolving dependencies...</info>\",\n            ):\n                yield\n\n    def _solve_in_compatibility_mode(\n        self,\n        overrides: tuple[dict[Package, dict[str, Dependency]], ...],\n    ) -> dict[Package, TransitivePackageInfo]:\n        override_packages: list[\n            tuple[\n                dict[Package, dict[str, Dependency]],\n                dict[Package, TransitivePackageInfo],\n            ]\n        ] = []\n        for override in overrides:\n            self._provider.debug(\n                # ignore the warning as provider does not do interpolation\n                \"<comment>Retrying dependency resolution \"\n                f\"with the following overrides ({override}).</comment>\"\n            )\n            self._provider.set_overrides(override)\n            new_packages = self._solve()\n            override_packages.append((override, new_packages))\n\n        return merge_override_packages(\n            override_packages, self._package.python_constraint\n        )\n\n    def _solve(self) -> dict[Package, TransitivePackageInfo]:\n        if self._provider._overrides:\n            self._overrides.append(self._provider._overrides)\n\n        try:\n            result = resolve_version(self._package, self._provider)\n\n            packages = result.packages\n        except OverrideNeededError as e:\n            return self._solve_in_compatibility_mode(e.overrides)\n        except SolveFailureError as e:\n            raise SolverProblemError(e)\n\n        return self._aggregate_solved_packages(packages)\n\n    def _aggregate_solved_packages(\n        self, packages: list[Package]\n    ) -> dict[Package, TransitivePackageInfo]:\n        combined_nodes, markers = depth_first_search(\n            PackageNode(self._package, packages)\n        )\n        results = dict(aggregate_package_nodes(nodes) for nodes in combined_nodes)\n        calculate_markers(results, markers)\n\n        # Merging feature packages with base packages\n        solved_packages = {}\n        for package in packages:\n            if package.features:\n                for _package in packages:\n                    if (\n                        not _package.features\n                        and _package.name == package.name\n                        and _package.version == package.version\n                    ):\n                        for dep in package.requires:\n                            # Prevent adding base package as a dependency to itself\n                            if _package.name == dep.name:\n                                continue\n\n                            # Avoid duplication.\n                            if any(\n                                _dep == dep and _dep.marker == dep.marker\n                                for _dep in _package.requires\n                            ):\n                                continue\n\n                            _package.add_dependency(dep)\n            else:\n                solved_packages[package] = results[package]\n\n        return solved_packages\n\n\nDFSNodeID = tuple[str, frozenset[str], bool]\n\n\nclass DFSNode:\n    def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None:\n        self.id = id\n        self.name = name\n        self.base_name = base_name\n\n    def reachable(self) -> Sequence[Self]:\n        return []\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        pass\n\n    def __str__(self) -> str:\n        return str(self.id)\n\n\ndef depth_first_search(\n    source: PackageNode,\n) -> tuple[list[list[PackageNode]], MarkerOriginDict]:\n    back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list)\n    markers: MarkerOriginDict = defaultdict(\n        lambda: defaultdict(lambda: defaultdict(EmptyMarker))\n    )\n    visited: set[DFSNodeID] = set()\n    topo_sorted_nodes: list[PackageNode] = []\n\n    dfs_visit(source, back_edges, visited, topo_sorted_nodes, markers)\n\n    # Combine the nodes by name\n    combined_nodes: dict[str, list[PackageNode]] = defaultdict(list)\n    for node in topo_sorted_nodes:\n        node.visit(back_edges[node.id])\n        combined_nodes[node.name].append(node)\n\n    combined_topo_sorted_nodes: list[list[PackageNode]] = [\n        combined_nodes.pop(node.name)\n        for node in topo_sorted_nodes\n        if node.name in combined_nodes\n    ]\n\n    return combined_topo_sorted_nodes, markers\n\n\ndef dfs_visit(\n    node: PackageNode,\n    back_edges: dict[DFSNodeID, list[PackageNode]],\n    visited: set[DFSNodeID],\n    sorted_nodes: list[PackageNode],\n    markers: MarkerOriginDict,\n) -> None:\n    if node.id in visited:\n        return\n    visited.add(node.id)\n\n    for out_neighbor in node.reachable():\n        back_edges[out_neighbor.id].append(node)\n        groups = out_neighbor.groups\n        prev_marker = markers[out_neighbor.package][node.package][groups]\n        new_marker = (\n            out_neighbor.marker\n            if node.package.is_root()\n            else out_neighbor.marker.without_extras()\n        )\n        markers[out_neighbor.package][node.package][groups] = prev_marker.union(\n            new_marker\n        )\n        dfs_visit(out_neighbor, back_edges, visited, sorted_nodes, markers)\n    sorted_nodes.insert(0, node)\n\n\nclass PackageNode(DFSNode):\n    def __init__(\n        self,\n        package: Package,\n        packages: list[Package],\n        previous: PackageNode | None = None,\n        dep: Dependency | None = None,\n        marker: BaseMarker | None = None,\n    ) -> None:\n        self.package = package\n        self.packages = packages\n\n        self.dep = dep\n        self.marker = marker or AnyMarker()\n        self.depth = -1\n\n        if not previous:\n            self.groups: frozenset[NormalizedName] = frozenset()\n            self.optional = True\n        elif dep:\n            self.groups = dep.groups\n            self.optional = dep.is_optional()\n        else:\n            raise ValueError(\"Both previous and dep must be passed\")\n\n        package_repr = repr(package)\n        super().__init__(\n            (package_repr, self.groups, self.optional),\n            package_repr,\n            package.name,\n        )\n\n    def reachable(self) -> Sequence[PackageNode]:\n        children: list[PackageNode] = []\n\n        for dependency in self.package.all_requires:\n            for pkg in self.packages:\n                if pkg.complete_name == dependency.complete_name and pkg.satisfies(\n                    dependency\n                ):\n                    marker = dependency.marker\n                    if self.package.is_root() and dependency.in_extras:\n                        marker = marker.intersect(\n                            parse_marker(\n                                \" or \".join(\n                                    f'extra == \"{extra}\"'\n                                    for extra in dependency.in_extras\n                                )\n                            )\n                        )\n                    children.append(\n                        PackageNode(\n                            pkg,\n                            self.packages,\n                            self,\n                            self.dep or dependency,\n                            marker,\n                        )\n                    )\n\n        return children\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        # The root package, which has no parents, is defined as having depth -1\n        # So that the root package's top-level dependencies have depth 0.\n        self.depth = 1 + max(\n            [\n                parent.depth if parent.base_name != self.base_name else parent.depth - 1\n                for parent in parents\n            ]\n            + [-2]\n        )\n\n\ndef aggregate_package_nodes(\n    nodes: list[PackageNode],\n) -> tuple[Package, TransitivePackageInfo]:\n    package = nodes[0].package\n    depth = max(node.depth for node in nodes)\n    groups: set[NormalizedName] = set()\n    for node in nodes:\n        groups.update(node.groups)\n\n    optional = all(node.optional for node in nodes)\n    for node in nodes:\n        node.depth = depth\n        node.optional = optional\n\n    package.optional = optional\n\n    # TransitivePackageInfo.markers is updated later,\n    # because the nodes of all packages have to be aggregated first.\n    return package, TransitivePackageInfo(depth, groups, {})\n\n\ndef calculate_markers(\n    packages: dict[Package, TransitivePackageInfo], markers: MarkerOriginDict\n) -> None:\n    # group packages by depth\n    packages_by_depth: dict[int, list[Package]] = defaultdict(list)\n    max_depth = -1\n    for package, info in packages.items():\n        max_depth = max(max_depth, info.depth)\n        packages_by_depth[info.depth].append(package)\n\n    # calculate markers from lowest to highest depth\n    # (start with depth 0 because the root package has depth -1)\n    has_incomplete_markers = True\n    while has_incomplete_markers:\n        has_incomplete_markers = False\n        for depth in range(max_depth + 1):\n            for package in packages_by_depth[depth]:\n                transitive_info = packages[package]\n                transitive_marker: dict[NormalizedName, BaseMarker] = {\n                    group: EmptyMarker() for group in transitive_info.groups\n                }\n                for parent, group_markers in markers[package].items():\n                    parent_info = packages[parent]\n                    if parent_info.groups:\n                        # If parent has groups, we need to intersect its per-group\n                        # markers with each edge marker and union into child's groups.\n                        if parent_info.groups != set(parent_info.markers):\n                            # there is a cycle -> we need one more iteration\n                            has_incomplete_markers = True\n                            continue\n                        for group in parent_info.groups:\n                            for edge_marker in group_markers.values():\n                                transitive_marker[group] = transitive_marker[\n                                    group\n                                ].union(\n                                    parent_info.markers[group].intersect(edge_marker)\n                                )\n                    else:\n                        # Parent is the root (no groups). Edge markers specify which\n                        # dependency groups the edge belongs to. We should only add\n                        # the edge marker to the corresponding child groups.\n                        for groups, edge_marker in group_markers.items():\n                            assert groups, (\n                                f\"Package {package.name} at depth {depth} has no groups.\"\n                                f\" All dependencies except for the root package at depth -1 must have groups\"\n                            )\n                            for group in transitive_info.groups:\n                                if group in groups:\n                                    transitive_marker[group] = transitive_marker[\n                                        group\n                                    ].union(edge_marker)\n                transitive_info.markers = transitive_marker\n\n\ndef merge_override_packages(\n    override_packages: list[\n        tuple[\n            dict[Package, dict[str, Dependency]], dict[Package, TransitivePackageInfo]\n        ]\n    ],\n    python_constraint: VersionConstraint,\n) -> dict[Package, TransitivePackageInfo]:\n    result: dict[Package, TransitivePackageInfo] = {}\n    all_packages: dict[\n        Package, list[tuple[Package, TransitivePackageInfo, BaseMarker]]\n    ] = {}\n    for override, o_packages in override_packages:\n        override_marker: BaseMarker = AnyMarker()\n        for deps in override.values():\n            for dep in deps.values():\n                override_marker = override_marker.intersect(dep.marker.without_extras())\n        override_marker = simplify_marker(override_marker, python_constraint)\n        for package, info in o_packages.items():\n            for group, marker in info.markers.items():\n                # `override_marker` is often a SingleMarker or a MultiMarker,\n                # `marker` often is a MultiMarker that contains `override_marker`.\n                # We can \"remove\" `override_marker` from `marker`\n                # because we will do an intersection later anyway.\n                # By removing it now, it is more likely that we hit\n                # the performance shortcut instead of the fallback algorithm.\n                info.markers[group] = remove_other_from_marker(marker, override_marker)\n            all_packages.setdefault(package, []).append(\n                (package, info, override_marker)\n            )\n    for package_duplicates in all_packages.values():\n        base = package_duplicates[0]\n        remaining = package_duplicates[1:]\n        package = base[0]\n        package_info = base[1]\n        first_override_marker = base[2]\n        result[package] = package_info\n        package_info.depth = max(info.depth for _, info, _ in package_duplicates)\n        package_info.groups = {\n            g for _, info, _ in package_duplicates for g in info.groups\n        }\n        if all(info.markers == package_info.markers for _, info, _ in remaining):\n            # performance shortcut:\n            # if markers are the same for all overrides,\n            # we can use less expensive marker operations\n            override_marker = EmptyMarker()\n            for _, _, marker in package_duplicates:\n                override_marker = override_marker.union(marker)\n            package_info.markers = {\n                group: override_marker.intersect(marker)\n                for group, marker in package_info.markers.items()\n            }\n        else:\n            # fallback / general algorithm with performance issues\n            for group, marker in package_info.markers.items():\n                package_info.markers[group] = first_override_marker.intersect(marker)\n            for _, info, override_marker in remaining:\n                for group, marker in info.markers.items():\n                    package_info.markers[group] = package_info.markers.get(\n                        group, EmptyMarker()\n                    ).union(override_marker.intersect(marker))\n        for duplicate_package, _, _ in remaining:\n            for dep in duplicate_package.requires:\n                if dep not in package.requires:\n                    package.add_dependency(dep)\n    return result\n\n\ndef remove_other_from_marker(marker: BaseMarker, other: BaseMarker) -> BaseMarker:\n    if isinstance(other, SingleMarker):\n        other_markers: set[BaseMarker] = {other}\n    elif isinstance(other, MultiMarker):\n        other_markers = set(other.markers)\n    else:\n        return marker\n    if isinstance(marker, MultiMarker) and other_markers.issubset(marker.markers):\n        return MultiMarker.of(*(m for m in marker.markers if m not in other_markers))\n    return marker\n\n\n@functools.cache\ndef simplify_marker(\n    marker: BaseMarker, python_constraint: VersionConstraint\n) -> BaseMarker:\n    \"\"\"\n    Remove constraints from markers that are covered by the projects Python constraint.\n\n    Use cache because we call this function often for the same markers.\n    \"\"\"\n    return marker.reduce_by_python_constraint(python_constraint)\n"
  },
  {
    "path": "src/poetry/puzzle/transaction.py",
    "content": "from __future__ import annotations\n\nfrom collections import defaultdict\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.utils.extras import get_extra_package_names\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import Package\n\n    from poetry.installation.operations.operation import Operation\n    from poetry.packages.transitive_package_info import TransitivePackageInfo\n\n\nclass Transaction:\n    def __init__(\n        self,\n        current_packages: list[Package],\n        result_packages: list[Package] | dict[Package, TransitivePackageInfo],\n        installed_packages: list[Package] | None = None,\n        root_package: Package | None = None,\n        marker_env: Mapping[str, Any] | None = None,\n        groups: set[NormalizedName] | None = None,\n    ) -> None:\n        self._current_packages = current_packages\n        self._result_packages = result_packages\n\n        if installed_packages is None:\n            installed_packages = []\n\n        self._installed_packages = {pkg.name: pkg for pkg in installed_packages}\n        self._root_package = root_package\n        self._marker_env = marker_env\n        self._groups = groups\n\n    def get_solved_packages(self) -> dict[Package, TransitivePackageInfo]:\n        assert isinstance(self._result_packages, dict)\n        return self._result_packages\n\n    def calculate_operations(\n        self,\n        *,\n        with_uninstalls: bool = True,\n        synchronize: bool = False,\n        skip_directory: bool = False,\n        extras: set[NormalizedName] | None = None,\n        system_site_packages: set[NormalizedName] | None = None,\n    ) -> list[Operation]:\n        from poetry.installation.operations import Install\n        from poetry.installation.operations import Uninstall\n        from poetry.installation.operations import Update\n\n        if not system_site_packages:\n            system_site_packages = set()\n\n        operations: list[Operation] = []\n\n        extra_packages: set[NormalizedName] = set()\n        if self._marker_env:\n            marker_env_with_extras = dict(self._marker_env)\n            if extras is not None:\n                marker_env_with_extras[\"extra\"] = extras\n        elif extras is not None:\n            assert self._root_package is not None\n            extra_packages = get_extra_package_names(\n                self._result_packages,\n                {k: [d.name for d in v] for k, v in self._root_package.extras.items()},\n                extras,\n            )\n\n        if isinstance(self._result_packages, dict):\n            priorities = {\n                pkg: info.depth for pkg, info in self._result_packages.items()\n            }\n        else:\n            priorities = defaultdict(int)\n        relevant_result_packages: set[NormalizedName] = set()\n        for result_package in self._result_packages:\n            is_unsolicited_extra = False\n            if self._marker_env:\n                assert self._groups is not None\n                assert isinstance(self._result_packages, dict)\n                info = self._result_packages[result_package]\n\n                if info.groups & self._groups and info.get_marker(\n                    self._groups\n                ).validate(marker_env_with_extras):\n                    relevant_result_packages.add(result_package.name)\n                elif result_package.optional:\n                    is_unsolicited_extra = True\n                else:\n                    continue\n            else:\n                is_unsolicited_extra = extras is not None and (\n                    result_package.optional\n                    and result_package.name not in extra_packages\n                )\n                if not is_unsolicited_extra:\n                    relevant_result_packages.add(result_package.name)\n\n            if installed_package := self._installed_packages.get(result_package.name):\n                # Extras that were not requested are not relevant.\n                if is_unsolicited_extra:\n                    pass\n\n                # We have to perform an update if the version or another\n                # attribute of the package has changed (source type, url, ref, ...).\n                elif result_package.version != installed_package.version or (\n                    (\n                        # This has to be done because installed packages cannot\n                        # have type \"legacy\". If a package with type \"legacy\"\n                        # is installed, the installed package has no source_type.\n                        # Thus, if installed_package has no source_type and\n                        # the result_package has source_type \"legacy\" (negation of\n                        # the following condition), update must not be performed.\n                        # This quirk has the side effect that when switching\n                        # from PyPI to legacy (or vice versa),\n                        # no update is performed.\n                        installed_package.source_type\n                        or result_package.source_type != \"legacy\"\n                    )\n                    and not result_package.is_same_package_as(installed_package)\n                ):\n                    operations.append(\n                        Update(\n                            installed_package,\n                            result_package,\n                            priority=priorities[result_package],\n                        )\n                    )\n                else:\n                    operations.append(Install(result_package).skip(\"Already installed\"))\n\n            elif not (skip_directory and result_package.source_type == \"directory\"):\n                op = Install(result_package, priority=priorities[result_package])\n                if is_unsolicited_extra:\n                    op.skip(\"Not required\")\n                operations.append(op)\n\n        if with_uninstalls:\n            uninstalls: set[NormalizedName] = set()\n\n            result_packages = {package.name for package in self._result_packages}\n            for current_package in self._current_packages:\n                if current_package.name not in (result_packages | uninstalls) and (\n                    installed_package := self._installed_packages.get(\n                        current_package.name\n                    )\n                ):\n                    uninstalls.add(installed_package.name)\n                    if installed_package.name not in system_site_packages:\n                        operations.append(Uninstall(installed_package))\n\n            if synchronize:\n                # We preserve pip when not managed by poetry, this is done to avoid\n                # externally managed virtual environments causing unnecessary removals.\n                preserved_package_names = {\"pip\"} - relevant_result_packages\n\n                for installed_package in self._installed_packages.values():\n                    if installed_package.name in uninstalls:\n                        continue\n\n                    if (\n                        self._root_package\n                        and installed_package.name == self._root_package.name\n                    ):\n                        continue\n\n                    if installed_package.name in preserved_package_names:\n                        continue\n\n                    if installed_package.name not in relevant_result_packages:\n                        uninstalls.add(installed_package.name)\n                        if installed_package.name not in system_site_packages:\n                            operations.append(Uninstall(installed_package))\n\n        return sorted(\n            operations,\n            key=lambda o: (\n                -o.priority,\n                o.package.name,\n                o.package.version,\n            ),\n        )\n"
  },
  {
    "path": "src/poetry/py.typed",
    "content": ""
  },
  {
    "path": "src/poetry/pyproject/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/pyproject/toml.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.pyproject.toml import PyProjectTOML as BasePyProjectTOML\nfrom tomlkit.api import table\nfrom tomlkit.items import Table\nfrom tomlkit.toml_document import TOMLDocument\n\nfrom poetry.toml import TOMLFile\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\nclass PyProjectTOML(BasePyProjectTOML):\n    \"\"\"\n    Enhanced version of poetry-core's PyProjectTOML\n    which is capable of writing pyproject.toml\n\n    The poetry-core class uses tomli to read the file,\n    here we use tomlkit to preserve comments and formatting when writing.\n    \"\"\"\n\n    def __init__(self, path: Path) -> None:\n        super().__init__(path)\n        self._toml_file = TOMLFile(path=path)\n        self._toml_document: TOMLDocument | None = None\n\n    @property\n    def file(self) -> TOMLFile:\n        return self._toml_file\n\n    @property\n    def data(self) -> TOMLDocument:\n        if self._toml_document is None:\n            if not self.file.exists():\n                self._toml_document = TOMLDocument()\n            else:\n                self._toml_document = self.file.read()\n\n        return self._toml_document\n\n    def save(self) -> None:\n        data = self.data\n\n        if self._build_system is not None:\n            if \"build-system\" not in data:\n                data[\"build-system\"] = table()\n\n            build_system = data[\"build-system\"]\n            assert isinstance(build_system, Table)\n\n            build_system[\"requires\"] = self._build_system.requires\n            build_system[\"build-backend\"] = self._build_system.build_backend\n\n        self.file.write(data=data)\n\n    def reload(self) -> None:\n        self._toml_document = None\n        self._build_system = None\n"
  },
  {
    "path": "src/poetry/repositories/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.repositories.repository import Repository\nfrom poetry.repositories.repository_pool import RepositoryPool\n\n\n__all__ = [\"Repository\", \"RepositoryPool\"]\n"
  },
  {
    "path": "src/poetry/repositories/abstract_repository.py",
    "content": "from __future__ import annotations\n\nfrom abc import ABC\nfrom abc import abstractmethod\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.constraints.version import Version\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n\nclass AbstractRepository(ABC):\n    def __init__(self, name: str) -> None:\n        self._name = name\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @abstractmethod\n    def find_packages(self, dependency: Dependency) -> list[Package]: ...\n\n    @abstractmethod\n    def search(self, query: str | list[str]) -> list[Package]: ...\n\n    @abstractmethod\n    def package(self, name: str, version: Version) -> Package: ...\n"
  },
  {
    "path": "src/poetry/repositories/cached_repository.py",
    "content": "from __future__ import annotations\n\nfrom abc import ABC\nfrom abc import abstractmethod\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import parse_constraint\n\nfrom poetry.config.config import Config\nfrom poetry.repositories.repository import Repository\nfrom poetry.utils.cache import FileCache\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import Version\n    from poetry.core.packages.package import Package\n\n    from poetry.inspection.info import PackageInfo\n\n\nclass CachedRepository(Repository, ABC):\n    CACHE_VERSION = parse_constraint(\"2.1.0\")\n\n    def __init__(\n        self, name: str, *, disable_cache: bool = False, config: Config | None = None\n    ) -> None:\n        super().__init__(name)\n        self._disable_cache = disable_cache\n        self._cache_dir = (config or Config.create()).repository_cache_directory / name\n        self._release_cache: FileCache[dict[str, Any]] = FileCache(path=self._cache_dir)\n\n    @abstractmethod\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]: ...\n\n    def get_release_info(self, name: NormalizedName, version: Version) -> PackageInfo:\n        \"\"\"\n        Return the release information given a package name and a version.\n\n        The information is returned from the cache if it exists\n        or retrieved from the remote server.\n        \"\"\"\n        from poetry.inspection.info import PackageInfo\n\n        if self._disable_cache:\n            return PackageInfo.load(self._get_release_info(name, version))\n\n        cached = self._release_cache.remember(\n            f\"{name}:{version}\", lambda: self._get_release_info(name, version)\n        )\n\n        cache_version = cached.get(\"_cache_version\", \"0.0.0\")\n        if parse_constraint(cache_version) != self.CACHE_VERSION:\n            # The cache must be updated\n            self._log(\n                f\"The cache for {name} {version} is outdated. Refreshing.\",\n                level=\"debug\",\n            )\n            cached = self._get_release_info(name, version)\n\n            self._release_cache.put(f\"{name}:{version}\", cached)\n\n        return PackageInfo.load(cached)\n\n    def package(self, name: str, version: Version) -> Package:\n        return self.get_release_info(canonicalize_name(name), version).to_package(\n            name=name\n        )\n\n    def forget(self, name: str, version: Version) -> None:\n        self._release_cache.forget(f\"{canonicalize_name(name)}:{version}\")\n"
  },
  {
    "path": "src/poetry/repositories/exceptions.py",
    "content": "from __future__ import annotations\n\n\nclass RepositoryError(Exception):\n    pass\n\n\nclass PackageNotFoundError(Exception):\n    pass\n\n\nclass InvalidSourceError(Exception):\n    pass\n"
  },
  {
    "path": "src/poetry/repositories/http_repository.py",
    "content": "from __future__ import annotations\n\nimport functools\nimport hashlib\n\nfrom contextlib import contextmanager\nfrom contextlib import suppress\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport requests\nimport requests.adapters\n\nfrom packaging.metadata import parse_email\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.config.config import Config\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.inspection.lazy_wheel import LazyWheelUnsupportedError\nfrom poetry.inspection.lazy_wheel import metadata_from_wheel_url\nfrom poetry.repositories.cached_repository import CachedRepository\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.exceptions import RepositoryError\nfrom poetry.repositories.link_sources.html import HTMLPage\nfrom poetry.repositories.link_sources.json import SimpleJsonPage\nfrom poetry.utils.authenticator import Authenticator\nfrom poetry.utils.constants import REQUESTS_TIMEOUT\nfrom poetry.utils.helpers import HTTPRangeRequestSupportedError\nfrom poetry.utils.helpers import download_file\nfrom poetry.utils.helpers import get_highest_priority_hash_type\nfrom poetry.utils.patterns import wheel_file_re\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import PackageFile\n    from poetry.core.packages.utils.link import Link\n\n    from poetry.repositories.link_sources.base import LinkSource\n    from poetry.utils.authenticator import RepositoryCertificateConfig\n\n\nclass HTTPRepository(CachedRepository):\n    def __init__(\n        self,\n        name: str,\n        url: str,\n        *,\n        config: Config | None = None,\n        disable_cache: bool = False,\n        pool_size: int = requests.adapters.DEFAULT_POOLSIZE,\n    ) -> None:\n        super().__init__(name, disable_cache=disable_cache, config=config)\n        self._url = url\n        if config is None:\n            config = Config.create()\n        self._authenticator = Authenticator(\n            config=config,\n            cache_id=name,\n            disable_cache=disable_cache,\n            pool_size=pool_size,\n        )\n        self._authenticator.add_repository(name, url)\n        self.get_page = functools.lru_cache(maxsize=None)(self._get_page)\n\n        self._lazy_wheel = config.get(\"solver.lazy-wheel\", True)\n        self._max_retries = config.get(\"requests.max-retries\", 0)\n        # We are tracking if a domain supports range requests or not to avoid\n        # unnecessary requests.\n        # ATTENTION: A domain might support range requests only for some files, so the\n        # meaning is as follows:\n        # - Domain not in dict: We don't know anything.\n        # - True: The domain supports range requests for at least some files.\n        # - False: The domain does not support range requests for the files we tried.\n        self._supports_range_requests: dict[str, bool] = {}\n\n    @property\n    def session(self) -> Authenticator:\n        return self._authenticator\n\n    @property\n    def url(self) -> str:\n        return self._url\n\n    @property\n    def certificates(self) -> RepositoryCertificateConfig:\n        return self._authenticator.get_certs_for_url(self.url)\n\n    @property\n    def authenticated_url(self) -> str:\n        return self._authenticator.authenticated_url(url=self.url)\n\n    def _download(\n        self, url: str, dest: Path, *, raise_accepts_ranges: bool = False\n    ) -> None:\n        return download_file(\n            url,\n            dest,\n            session=self.session,\n            raise_accepts_ranges=raise_accepts_ranges,\n            max_retries=self._max_retries,\n        )\n\n    @contextmanager\n    def _cached_or_downloaded_file(\n        self, link: Link, *, raise_accepts_ranges: bool = False\n    ) -> Iterator[Path]:\n        self._log(f\"Downloading: {link.url}\", level=\"debug\")\n        with TemporaryDirectory(ignore_cleanup_errors=True) as temp_dir:\n            filepath = Path(temp_dir) / link.filename\n            self._download(\n                link.url, filepath, raise_accepts_ranges=raise_accepts_ranges\n            )\n            yield filepath\n\n    def _get_info_from_wheel(self, link: Link) -> PackageInfo:\n        from poetry.inspection.info import PackageInfo\n\n        netloc = link.netloc\n\n        # If \"lazy-wheel\" is enabled and the domain supports range requests\n        # or we don't know yet, we try range requests.\n        raise_accepts_ranges = self._lazy_wheel\n        if self._lazy_wheel and self._supports_range_requests.get(netloc, True):\n            try:\n                package_info = PackageInfo.from_metadata(\n                    metadata_from_wheel_url(link.filename, link.url, self.session)\n                )\n            except LazyWheelUnsupportedError as e:\n                # Do not set to False if we already know that the domain supports\n                # range requests for some URLs!\n                self._log(\n                    f\"Disabling lazy wheel support for {netloc}: {e}\",\n                    level=\"debug\",\n                )\n                raise_accepts_ranges = False\n                self._supports_range_requests.setdefault(netloc, False)\n            else:\n                self._supports_range_requests[netloc] = True\n                return package_info\n\n        try:\n            with self._cached_or_downloaded_file(\n                link, raise_accepts_ranges=raise_accepts_ranges\n            ) as filepath:\n                return PackageInfo.from_wheel(filepath)\n        except HTTPRangeRequestSupportedError:\n            # The domain did not support range requests for the first URL(s) we tried,\n            # but supports it for some URLs (especially the current URL),\n            # so we abort the download, update _supports_range_requests to try\n            # range requests for all files and use it for the current URL.\n            self._log(\n                f\"Abort downloading {link.url} because server supports range requests\",\n                level=\"debug\",\n            )\n            self._supports_range_requests[netloc] = True\n            return self._get_info_from_wheel(link)\n\n    def _get_info_from_sdist(self, link: Link) -> PackageInfo:\n        from poetry.inspection.info import PackageInfo\n\n        with self._cached_or_downloaded_file(link) as filepath:\n            return PackageInfo.from_sdist(filepath)\n\n    def _get_info_from_metadata(self, link: Link) -> PackageInfo | None:\n        if link.has_metadata:\n            try:\n                assert link.metadata_url is not None\n                response = self.session.get(link.metadata_url)\n                if link.metadata_hashes and (\n                    hash_name := get_highest_priority_hash_type(\n                        link.metadata_hashes, f\"{link.filename}.metadata\"\n                    )\n                ):\n                    metadata_hash = getattr(hashlib, hash_name)(\n                        response.content\n                    ).hexdigest()\n                    if metadata_hash != link.metadata_hashes[hash_name]:\n                        self._log(\n                            f\"Metadata file hash ({metadata_hash}) does not match\"\n                            f\" expected hash ({link.metadata_hashes[hash_name]}).\"\n                            f\" Metadata file for {link.filename} will be ignored.\",\n                            level=\"warning\",\n                        )\n                        return None\n\n                metadata, _ = parse_email(response.content)\n                return PackageInfo.from_metadata(metadata)\n\n            except requests.HTTPError:\n                self._log(\n                    f\"Failed to retrieve metadata at {link.metadata_url}\",\n                    level=\"warning\",\n                )\n\n        return None\n\n    def _get_info_from_links(\n        self, links: list[Link], *, ignore_yanked: bool\n    ) -> PackageInfo:\n        # Sort links by distribution type\n        wheels: list[Link] = []\n        sdists: list[Link] = []\n        for link in links:\n            if link.yanked and ignore_yanked:\n                # drop yanked files unless the entire release is yanked\n                continue\n            if link.is_wheel:\n                wheels.append(link)\n            elif link.filename.endswith(\n                (\".tar.gz\", \".zip\", \".bz2\", \".xz\", \".Z\", \".tar\")\n            ):\n                sdists.append(link)\n\n        # Prefer to read data from wheels: this is faster and more reliable\n        if wheels:\n            # We ought just to be able to look at any of the available wheels to read\n            # metadata, they all should give the same answer.\n            #\n            # In practice this hasn't always been true.\n            #\n            # Most of the code in here is to deal with cases such as isort 4.3.4 which\n            # published separate python3 and python2 wheels with quite different\n            # dependencies.  We try to detect such cases and combine the data from the\n            # two wheels into what ought to have been published in the first place...\n            universal_wheel = None\n            universal_python2_wheel = None\n            universal_python3_wheel = None\n            platform_specific_wheels = []\n            for wheel in wheels:\n                m = wheel_file_re.match(wheel.filename)\n                if not m:\n                    continue\n\n                pyver = m.group(\"pyver\")\n                abi = m.group(\"abi\")\n                plat = m.group(\"plat\")\n                if abi == \"none\" and plat == \"any\":\n                    # Universal wheel\n                    if pyver == \"py2.py3\":\n                        # Any Python\n                        universal_wheel = wheel\n                    elif pyver == \"py2\":\n                        universal_python2_wheel = wheel\n                    else:\n                        universal_python3_wheel = wheel\n                else:\n                    platform_specific_wheels.append(wheel)\n\n            if universal_wheel is not None:\n                return self._get_info_from_metadata(\n                    universal_wheel\n                ) or self._get_info_from_wheel(universal_wheel)\n\n            info = None\n            if universal_python2_wheel and universal_python3_wheel:\n                info = self._get_info_from_metadata(\n                    universal_python2_wheel\n                ) or self._get_info_from_wheel(universal_python2_wheel)\n\n                py3_info = self._get_info_from_metadata(\n                    universal_python3_wheel\n                ) or self._get_info_from_wheel(universal_python3_wheel)\n\n                if info.requires_python or py3_info.requires_python:\n                    info.requires_python = str(\n                        parse_constraint(info.requires_python or \"^2.7\").union(\n                            parse_constraint(py3_info.requires_python or \"^3\")\n                        )\n                    )\n\n                if py3_info.requires_dist:\n                    if not info.requires_dist:\n                        info.requires_dist = py3_info.requires_dist\n\n                        return info\n\n                    py2_requires_dist = {\n                        Dependency.create_from_pep_508(r).to_pep_508()\n                        for r in info.requires_dist\n                    }\n                    py3_requires_dist = {\n                        Dependency.create_from_pep_508(r).to_pep_508()\n                        for r in py3_info.requires_dist\n                    }\n                    base_requires_dist = py2_requires_dist & py3_requires_dist\n                    py2_only_requires_dist = py2_requires_dist - py3_requires_dist\n                    py3_only_requires_dist = py3_requires_dist - py2_requires_dist\n\n                    # Normalizing requires_dist\n                    requires_dist = list(base_requires_dist)\n                    for requirement in py2_only_requires_dist:\n                        dep = Dependency.create_from_pep_508(requirement)\n                        dep.marker = dep.marker.intersect(\n                            parse_marker(\"python_version == '2.7'\")\n                        )\n                        requires_dist.append(dep.to_pep_508())\n\n                    for requirement in py3_only_requires_dist:\n                        dep = Dependency.create_from_pep_508(requirement)\n                        dep.marker = dep.marker.intersect(\n                            parse_marker(\"python_version >= '3'\")\n                        )\n                        requires_dist.append(dep.to_pep_508())\n\n                    info.requires_dist = sorted(set(requires_dist))\n\n            if info:\n                return info\n\n            # Prefer non platform specific wheels\n            if universal_python3_wheel:\n                return self._get_info_from_metadata(\n                    universal_python3_wheel\n                ) or self._get_info_from_wheel(universal_python3_wheel)\n\n            if universal_python2_wheel:\n                return self._get_info_from_metadata(\n                    universal_python2_wheel\n                ) or self._get_info_from_wheel(universal_python2_wheel)\n\n            if platform_specific_wheels:\n                first_wheel = platform_specific_wheels[0]\n                return self._get_info_from_metadata(\n                    first_wheel\n                ) or self._get_info_from_wheel(first_wheel)\n\n        return self._get_info_from_metadata(sdists[0]) or self._get_info_from_sdist(\n            sdists[0]\n        )\n\n    def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any]:\n        if not links:\n            raise PackageNotFoundError(\n                f'No valid distribution links found for package: \"{data.name}\" version:'\n                f' \"{data.version}\"'\n            )\n\n        files: list[PackageFile] = []\n        for link in links:\n            if link.yanked and not data.yanked:\n                # drop yanked files unless the entire release is yanked\n                continue\n\n            file_hash: str | None\n            for hash_name in (\"sha512\", \"sha384\", \"sha256\"):\n                if hash_name in link.hashes:\n                    file_hash = f\"{hash_name}:{link.hashes[hash_name]}\"\n                    break\n            else:\n                file_hash = self.calculate_sha256(link)\n\n            if file_hash is None and (\n                hash_type := get_highest_priority_hash_type(link.hashes, link.filename)\n            ):\n                file_hash = f\"{hash_type}:{link.hashes[hash_type]}\"\n\n            if file_hash is None:\n                # Is that even possible?\n                # Before introducing this warning and ignoring the file,\n                # null hashes would have been written to the lockfile,\n                # which should have been failed in the Chooser at latest.\n                self._log(\n                    f\"Failed to determine hash of {link.url}. Skipping file.\",\n                    level=\"warning\",\n                )\n            else:\n                files.append(\n                    {\n                        \"file\": link.filename,\n                        \"hash\": file_hash,\n                        \"url\": link.url_without_fragment,\n                    }\n                )\n                if link.size is not None:\n                    files[-1][\"size\"] = link.size\n                if link.upload_time_isoformat is not None:\n                    files[-1][\"upload_time\"] = link.upload_time_isoformat\n\n        if not files:\n            raise PackageNotFoundError(\n                f'Could not determine a hash for any distribution link of package: \"{data.name}\" version:'\n                f' \"{data.version}\"'\n            )\n\n        data.files = files\n\n        # drop yanked files unless the entire release is yanked\n        info = self._get_info_from_links(links, ignore_yanked=not data.yanked)\n\n        data.summary = info.summary\n        data.requires_dist = info.requires_dist\n        data.requires_python = info.requires_python\n\n        return data.asdict()\n\n    def calculate_sha256(self, link: Link) -> str | None:\n        with self._cached_or_downloaded_file(link) as filepath:\n            hash_name = get_highest_priority_hash_type(link.hashes, link.filename)\n            known_hash = None\n            with suppress(ValueError, AttributeError):\n                # Handle ValueError here as well since under FIPS environments\n                # this is what is raised (e.g., for MD5)\n                known_hash = getattr(hashlib, hash_name)() if hash_name else None\n            required_hash = hashlib.sha256()\n\n            chunksize = 4096\n            with filepath.open(\"rb\") as f:\n                while True:\n                    chunk = f.read(chunksize)\n                    if not chunk:\n                        break\n                    if known_hash:\n                        known_hash.update(chunk)\n                    required_hash.update(chunk)\n\n            if (\n                not hash_name\n                or not known_hash\n                or known_hash.hexdigest() == link.hashes[hash_name]\n            ):\n                return f\"{required_hash.name}:{required_hash.hexdigest()}\"\n        return None\n\n    def _get_response(\n        self, endpoint: str, *, headers: dict[str, str] | None = None\n    ) -> requests.Response | None:\n        url = self._url + endpoint\n        try:\n            response: requests.Response = self.session.get(\n                url, raise_for_status=False, timeout=REQUESTS_TIMEOUT, headers=headers\n            )\n            if response.status_code in (401, 403):\n                self._log(\n                    f\"Authorization error accessing {url}\",\n                    level=\"warning\",\n                )\n                return None\n            if response.status_code == 404:\n                return None\n            response.raise_for_status()\n        except requests.exceptions.HTTPError as e:\n            raise RepositoryError(e)\n\n        if response.url != url:\n            self._log(\n                f\"Response URL {response.url} differs from request URL {url}\",\n                level=\"debug\",\n            )\n        return response\n\n    def _get_prefer_json_header(self) -> dict[str, str]:\n        # Prefer json, but accept anything for backwards compatibility.\n        # Although the more specific value should be preferred to the less specific one\n        # according to https://developer.mozilla.org/en-US/docs/Glossary/Quality_values,\n        # we add a quality value because some servers still prefer html without one.\n        return {\"Accept\": \"application/vnd.pypi.simple.v1+json, */*;q=0.1\"}\n\n    def _is_json_response(self, response: requests.Response) -> bool:\n        return (\n            response.headers.get(\"Content-Type\", \"\").split(\";\")[0].strip()\n            == \"application/vnd.pypi.simple.v1+json\"\n        )\n\n    def _get_page(self, name: NormalizedName) -> LinkSource:\n        response = self._get_response(\n            f\"/{name}/\", headers=self._get_prefer_json_header()\n        )\n        if not response:\n            raise PackageNotFoundError(f\"Package [{name}] not found.\")\n        if self._is_json_response(response):\n            return SimpleJsonPage(response.url, response.json())\n        return HTMLPage(response.url, response.text)\n"
  },
  {
    "path": "src/poetry/repositories/installed_repository.py",
    "content": "from __future__ import annotations\n\nimport itertools\nimport json\nimport logging\n\nfrom importlib import metadata\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.utils.utils import is_python_project\nfrom poetry.core.packages.utils.utils import url_to_path\nfrom poetry.core.utils.helpers import module_name\n\nfrom poetry.repositories.repository import Repository\nfrom poetry.utils._compat import getencoding\nfrom poetry.utils.env import VirtualEnv\n\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n    from poetry.utils.env import Env\n\nlogger = logging.getLogger(__name__)\n\n\nclass InstalledRepository(Repository):\n    def __init__(self, packages: Sequence[Package] | None = None) -> None:\n        super().__init__(\"poetry-installed\", packages)\n        self.system_site_packages: list[Package] = []\n\n    def add_package(self, package: Package, *, is_system_site: bool = False) -> None:\n        super().add_package(package)\n        if is_system_site:\n            self.system_site_packages.append(package)\n\n    @classmethod\n    def get_package_paths(cls, env: Env, name: str) -> set[Path]:\n        \"\"\"\n        Process a .pth file within the site-packages directories, and return any valid\n        paths. We skip executable .pth files as there is no reliable means to do this\n        without side-effects to current run-time. Mo check is made that the item refers\n        to a directory rather than a file, however, in order to maintain backwards\n        compatibility, we allow non-existing paths to be discovered. The latter\n        behaviour is different to how Python's site-specific hook configuration works.\n\n        Reference: https://docs.python.org/3.8/library/site.html\n\n        :param env: The environment to search for the .pth file in.\n        :param name: The name of the package to search .pth file for.\n        :return: A `Set` of valid `Path` objects.\n        \"\"\"\n        paths = set()\n\n        # we identify the candidate pth files to check, this is done so to handle cases\n        # where the pth file for foo-bar might have been installed as either foo-bar.pth\n        # or foo_bar.pth (expected) in either pure or platform lib directories.\n        candidates = itertools.product(\n            {env.purelib, env.platlib},\n            {name, module_name(name)},\n        )\n\n        for lib, module in candidates:\n            pth_file = lib.joinpath(module).with_suffix(\".pth\")\n            if not pth_file.exists():\n                continue\n\n            with pth_file.open(encoding=getencoding()) as f:\n                for line in f:\n                    line = line.strip()\n                    if line and not line.startswith((\"#\", \"import \", \"import\\t\")):\n                        path = Path(line)\n                        if not path.is_absolute():\n                            path = lib.joinpath(path).resolve()\n                        paths.add(path)\n\n        src_path = env.path / \"src\" / name\n        if not paths and src_path.exists():\n            paths.add(src_path)\n\n        return paths\n\n    @classmethod\n    def get_package_vcs_properties_from_path(cls, src: Path) -> tuple[str, str, str]:\n        from poetry.vcs.git import Git\n\n        info = Git.info(repo=src)\n        return \"git\", info.origin, info.revision\n\n    @classmethod\n    def is_vcs_package(cls, package: Path | Package, env: Env) -> bool:\n        # A VCS dependency should have been installed\n        # in the src directory.\n        src = env.path / \"src\"\n        if isinstance(package, Package):\n            return src.joinpath(package.name).is_dir()\n\n        try:\n            package.relative_to(env.path / \"src\")\n        except ValueError:\n            return False\n        else:\n            return True\n\n    @classmethod\n    def _create_package_from_distribution(\n        cls, path: Path, dist_metadata: metadata.PackageMetadata, env: Env\n    ) -> Package:\n        # We first check for a direct_url.json file to determine\n        # the type of package.\n        if (\n            path.name.endswith(\".dist-info\")\n            and path.joinpath(\"direct_url.json\").exists()\n        ):\n            return cls._create_package_from_pep610(path, dist_metadata)\n\n        is_standard_package = env.is_path_relative_to_lib(path)\n\n        source_type = None\n        source_url = None\n        source_reference = None\n        source_resolved_reference = None\n        source_subdirectory = None\n        if is_standard_package:\n            if path.name.endswith(\".dist-info\"):\n                paths = cls.get_package_paths(env=env, name=dist_metadata[\"name\"])\n                if paths:\n                    is_editable_package = False\n                    for src in paths:\n                        if cls.is_vcs_package(src, env):\n                            (\n                                source_type,\n                                source_url,\n                                source_reference,\n                            ) = cls.get_package_vcs_properties_from_path(src)\n                            break\n\n                        if not (\n                            is_editable_package or env.is_path_relative_to_lib(src)\n                        ):\n                            is_editable_package = True\n                    else:\n                        # TODO: handle multiple source directories?\n                        if is_editable_package:\n                            source_type = \"directory\"\n                            path = paths.pop()\n                            if path.name == \"src\":\n                                path = path.parent\n                            source_url = path.as_posix()\n        elif cls.is_vcs_package(path, env):\n            (\n                source_type,\n                source_url,\n                source_reference,\n            ) = cls.get_package_vcs_properties_from_path(\n                env.path / \"src\" / canonicalize_name(dist_metadata[\"name\"])\n            )\n        elif is_python_project(path.parent):\n            source_type = \"directory\"\n            source_url = str(path.parent)\n\n        package = Package(\n            dist_metadata[\"name\"],\n            dist_metadata[\"version\"],\n            source_type=source_type,\n            source_url=source_url,\n            source_reference=source_reference,\n            source_resolved_reference=source_resolved_reference,\n            source_subdirectory=source_subdirectory,\n        )\n\n        package.description = dist_metadata.get(  # type: ignore[attr-defined]\n            \"summary\",\n            \"\",\n        )\n\n        return package\n\n    @classmethod\n    def _create_package_from_pep610(\n        cls, path: Path, dist_metadata: metadata.PackageMetadata\n    ) -> Package:\n        source_type = None\n        source_url = None\n        source_reference = None\n        source_resolved_reference = None\n        source_subdirectory = None\n        develop = False\n\n        url_reference = json.loads(\n            path.joinpath(\"direct_url.json\").read_text(encoding=\"utf-8\")\n        )\n        if \"archive_info\" in url_reference:\n            # File or URL distribution\n            if url_reference[\"url\"].startswith(\"file:\"):\n                # File distribution\n                source_type = \"file\"\n                source_url = url_to_path(url_reference[\"url\"]).as_posix()\n            else:\n                # URL distribution\n                source_type = \"url\"\n                source_url = url_reference[\"url\"]\n        elif \"dir_info\" in url_reference:\n            # Directory distribution\n            source_type = \"directory\"\n            source_url = url_to_path(url_reference[\"url\"]).as_posix()\n            develop = url_reference[\"dir_info\"].get(\"editable\", False)\n        elif \"vcs_info\" in url_reference:\n            # VCS distribution\n            source_type = url_reference[\"vcs_info\"][\"vcs\"]\n            source_url = url_reference[\"url\"]\n            source_resolved_reference = url_reference[\"vcs_info\"][\"commit_id\"]\n            source_reference = url_reference[\"vcs_info\"].get(\n                \"requested_revision\", source_resolved_reference\n            )\n        source_subdirectory = url_reference.get(\"subdirectory\")\n\n        package = Package(\n            dist_metadata[\"name\"],\n            dist_metadata[\"version\"],\n            source_type=source_type,\n            source_url=source_url,\n            source_reference=source_reference,\n            source_resolved_reference=source_resolved_reference,\n            source_subdirectory=source_subdirectory,\n            develop=develop,\n        )\n\n        package.description = dist_metadata.get(  # type: ignore[attr-defined]\n            \"summary\",\n            \"\",\n        )\n\n        return package\n\n    @classmethod\n    def load(cls, env: Env, with_dependencies: bool = False) -> InstalledRepository:\n        \"\"\"\n        Load installed packages.\n        \"\"\"\n        from poetry.core.packages.dependency import Dependency\n\n        repo = cls()\n        seen = set()\n        skipped = set()\n\n        base_env = (\n            env.parent_env\n            if isinstance(env, VirtualEnv) and env.includes_system_site_packages\n            else None\n        )\n\n        for entry in env.sys_path:\n            if not entry.strip():\n                logger.debug(\n                    \"Project environment contains an empty path in <c1>sys_path</>,\"\n                    \" ignoring.\"\n                )\n                continue\n\n            for distribution in sorted(\n                metadata.distributions(path=[entry]),\n                key=lambda d: str(d._path),  # type: ignore[attr-defined]\n            ):\n                path = Path(str(distribution._path))  # type: ignore[attr-defined]\n\n                if path in skipped:\n                    continue\n\n                dist_metadata = distribution.metadata  # type: ignore[attr-defined]\n                name = (\n                    dist_metadata.get(\"name\")  # type: ignore[attr-defined]\n                    if dist_metadata\n                    else None\n                )\n                if not dist_metadata or name is None:\n                    logger.warning(\n                        \"Project environment contains an invalid distribution\"\n                        \" (<c1>%s</>). Consider removing it manually or recreate\"\n                        \" the environment.\",\n                        path,\n                    )\n                    skipped.add(path)\n                    continue\n\n                name = canonicalize_name(name)\n\n                if name in seen:\n                    continue\n\n                package = cls._create_package_from_distribution(\n                    path, dist_metadata, env\n                )\n\n                if with_dependencies:\n                    for require in dist_metadata.get_all(\"requires-dist\", []):\n                        dep = Dependency.create_from_pep_508(require)\n                        package.add_dependency(dep)\n\n                seen.add(package.name)\n                repo.add_package(\n                    package,\n                    is_system_site=bool(\n                        base_env and base_env.is_path_relative_to_lib(path)\n                    ),\n                )\n\n        return repo\n"
  },
  {
    "path": "src/poetry/repositories/legacy_repository.py",
    "content": "from __future__ import annotations\n\nfrom contextlib import suppress\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport requests.adapters\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.http_repository import HTTPRepository\nfrom poetry.repositories.link_sources.base import SimpleRepositoryRootPage\nfrom poetry.repositories.link_sources.html import SimpleRepositoryHTMLRootPage\nfrom poetry.repositories.link_sources.json import SimpleRepositoryJsonRootPage\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import Version\n    from poetry.core.constraints.version import VersionConstraint\n    from poetry.core.packages.utils.link import Link\n\n    from poetry.config.config import Config\n\n\nclass LegacyRepository(HTTPRepository):\n    def __init__(\n        self,\n        name: str,\n        url: str,\n        *,\n        config: Config | None = None,\n        disable_cache: bool = False,\n        pool_size: int = requests.adapters.DEFAULT_POOLSIZE,\n    ) -> None:\n        if name == \"pypi\":\n            raise ValueError(\"The name [pypi] is reserved for repositories\")\n\n        super().__init__(\n            name,\n            url.rstrip(\"/\"),\n            config=config,\n            disable_cache=disable_cache,\n            pool_size=pool_size,\n        )\n\n    def package(self, name: str, version: Version) -> Package:\n        \"\"\"\n        Retrieve the release information.\n\n        This is a heavy task which takes time.\n        We have to download a package to get the dependencies.\n        We also need to download every file matching this release\n        to get the various hashes.\n\n        Note that this will be cached so the subsequent operations\n        should be much faster.\n        \"\"\"\n        try:\n            index = self._packages.index(Package(name, version))\n\n            return self._packages[index]\n        except ValueError:\n            package = super().package(name, version)\n            package._source_type = \"legacy\"\n            package._source_url = self._url\n            package._source_reference = self.name\n\n            return package\n\n    def find_links_for_package(self, package: Package) -> list[Link]:\n        try:\n            page = self.get_page(package.name)\n        except PackageNotFoundError:\n            return []\n\n        return list(page.links_for_version(package.name, package.version))\n\n    def _find_packages(\n        self, name: NormalizedName, constraint: VersionConstraint\n    ) -> list[Package]:\n        \"\"\"\n        Find packages on the remote server.\n        \"\"\"\n        try:\n            page = self.get_page(name)\n        except PackageNotFoundError:\n            self._log(f\"No packages found for {name}\", level=\"debug\")\n            return []\n\n        versions = [\n            (version, page.yanked(name, version))\n            for version in page.versions(name)\n            if constraint.allows(version)\n        ]\n\n        return [\n            Package(\n                name,\n                version,\n                source_type=\"legacy\",\n                source_reference=self.name,\n                source_url=self._url,\n                yanked=yanked,\n            )\n            for version, yanked in versions\n        ]\n\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]:\n        page = self.get_page(name)\n\n        links = list(page.links_for_version(name, version))\n        yanked = page.yanked(name, version)\n\n        return self._links_to_data(\n            links,\n            PackageInfo(\n                name=name,\n                version=version.text,\n                summary=\"\",\n                requires_dist=[],\n                requires_python=None,\n                files=[],\n                yanked=yanked,\n                cache_version=str(self.CACHE_VERSION),\n            ),\n        )\n\n    @cached_property\n    def root_page(self) -> SimpleRepositoryRootPage:\n        if not (\n            response := self._get_response(\"/\", headers=self._get_prefer_json_header())\n        ):\n            self._log(\n                f\"Unable to retrieve package listing from package source {self.name}\",\n                level=\"error\",\n            )\n            return SimpleRepositoryRootPage()\n\n        if self._is_json_response(response):\n            return SimpleRepositoryJsonRootPage(response.json())\n\n        return SimpleRepositoryHTMLRootPage(response.text)\n\n    def search(self, query: str | list[str]) -> list[Package]:\n        results: list[Package] = []\n\n        for candidate in self.root_page.search(query):\n            with suppress(PackageNotFoundError):\n                page = self.get_page(candidate)\n\n                for package in page.packages:\n                    results.append(package)\n\n        return results\n"
  },
  {
    "path": "src/poetry/repositories/link_sources/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/repositories/link_sources/base.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport re\n\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.package import Package\nfrom poetry.core.version.exceptions import InvalidVersionError\n\nfrom poetry.utils.patterns import sdist_file_re\nfrom poetry.utils.patterns import wheel_file_re\n\n\nif TYPE_CHECKING:\n    from collections import defaultdict\n    from collections.abc import Iterator\n\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.utils.link import Link\n\n    LinkCache = defaultdict[NormalizedName, defaultdict[Version, list[Link]]]\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass LinkSource:\n    VERSION_REGEX = re.compile(r\"(?i)([a-z0-9_\\-.]+?)-(?=\\d)([a-z0-9_.!+-]+)\")\n    CLEAN_REGEX = re.compile(r\"[^a-z0-9$&+,/:;=?@.#%_\\\\|-]\", re.I)\n    SUPPORTED_FORMATS: ClassVar[list[str]] = [\n        \".tar.gz\",\n        \".whl\",\n        \".zip\",\n        \".tar.bz2\",\n        \".tar.xz\",\n        \".tar.Z\",\n        \".tar\",\n    ]\n\n    def __init__(self, url: str) -> None:\n        self._url = url\n\n    @property\n    def url(self) -> str:\n        return self._url\n\n    def versions(self, name: NormalizedName) -> Iterator[Version]:\n        yield from self._link_cache[name]\n\n    @property\n    def packages(self) -> Iterator[Package]:\n        for link in self.links:\n            pkg = self.link_package_data(link)\n\n            if pkg:\n                yield pkg\n\n    @property\n    def links(self) -> Iterator[Link]:\n        for links_per_version in self._link_cache.values():\n            for links in links_per_version.values():\n                yield from links\n\n    @classmethod\n    def link_package_data(cls, link: Link) -> Package | None:\n        name: str | None = None\n        version_string: str | None = None\n        version: Version | None = None\n        m = wheel_file_re.match(link.filename) or sdist_file_re.match(link.filename)\n\n        if m:\n            name = m.group(\"name\")\n            version_string = m.group(\"ver\")\n        else:\n            info, _ext = link.splitext()\n            match = cls.VERSION_REGEX.match(info)\n            if match:\n                name = match.group(1)\n                version_string = match.group(2)\n\n        if version_string:\n            try:\n                version = Version.parse(version_string)\n            except InvalidVersionError:\n                logger.debug(\n                    \"Skipping url (%s) due to invalid version (%s)\", link.url, version\n                )\n                return None\n\n        pkg = None\n        if name and version:\n            pkg = Package(name, version, source_url=link.url)\n        return pkg\n\n    def links_for_version(\n        self, name: NormalizedName, version: Version\n    ) -> Iterator[Link]:\n        yield from self._link_cache[name][version]\n\n    def clean_link(self, url: str) -> str:\n        \"\"\"Makes sure a link is fully encoded.  That is, if a ' ' shows up in\n        the link, it will be rewritten to %20 (while not over-quoting\n        % or other characters).\"\"\"\n        return self.CLEAN_REGEX.sub(lambda match: f\"%{ord(match.group(0)):02x}\", url)\n\n    def yanked(self, name: NormalizedName, version: Version) -> str | bool:\n        reasons = set()\n        for link in self.links_for_version(name, version):\n            if link.yanked:\n                if link.yanked_reason:\n                    reasons.add(link.yanked_reason)\n            else:\n                # release is not yanked if at least one file is not yanked\n                return False\n        # if all files are yanked (or there are no files) the release is yanked\n        if reasons:\n            return \"\\n\".join(sorted(reasons))\n        return True\n\n    @cached_property\n    def _link_cache(self) -> LinkCache:\n        raise NotImplementedError()\n\n\nclass SimpleRepositoryRootPage:\n    \"\"\"\n    This class represents the parsed content of a \"simple\" repository's root page.\n    \"\"\"\n\n    def search(self, query: str | list[str]) -> list[str]:\n        results: list[str] = []\n        tokens = query if isinstance(query, list) else [query]\n\n        for name in self.package_names:\n            if any(token in name for token in tokens):\n                results.append(name)\n\n        return results\n\n    @cached_property\n    def package_names(self) -> list[str]:\n        # should be overridden in subclasses\n        return []\n"
  },
  {
    "path": "src/poetry/repositories/link_sources/html.py",
    "content": "from __future__ import annotations\n\nimport urllib.parse\n\nfrom collections import defaultdict\nfrom functools import cached_property\nfrom html import unescape\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.repositories.link_sources.base import LinkSource\nfrom poetry.repositories.link_sources.base import SimpleRepositoryRootPage\nfrom poetry.repositories.parsers.html_page_parser import HTMLPageParser\n\n\nif TYPE_CHECKING:\n    from poetry.repositories.link_sources.base import LinkCache\n\n\nclass HTMLPage(LinkSource):\n    def __init__(self, url: str, content: str) -> None:\n        super().__init__(url=url)\n\n        parser = HTMLPageParser()\n        parser.feed(content)\n        self._parsed = parser.anchors\n        self._base_url: str | None = parser.base_url\n\n    @cached_property\n    def _link_cache(self) -> LinkCache:\n        links: LinkCache = defaultdict(lambda: defaultdict(list))\n        for anchor in self._parsed:\n            if href := anchor.get(\"href\"):\n                url = self.clean_link(\n                    urllib.parse.urljoin(self._base_url or self._url, href)\n                )\n                pyrequire = anchor.get(\"data-requires-python\")\n                pyrequire = unescape(pyrequire) if pyrequire else None\n                yanked_value = anchor.get(\"data-yanked\")\n                yanked: str | bool\n                if yanked_value:\n                    yanked = unescape(yanked_value)\n                else:\n                    yanked = \"data-yanked\" in anchor\n\n                # see https://peps.python.org/pep-0714/#clients\n                # and https://peps.python.org/pep-0658/#specification\n                metadata: str | bool\n                for metadata_key in (\"data-core-metadata\", \"data-dist-info-metadata\"):\n                    metadata_value = anchor.get(metadata_key)\n                    if metadata_value:\n                        metadata = unescape(metadata_value)\n                    else:\n                        metadata = metadata_key in anchor\n                    if metadata:\n                        break\n                link = Link(\n                    url, requires_python=pyrequire, yanked=yanked, metadata=metadata\n                )\n\n                if link.ext not in self.SUPPORTED_FORMATS:\n                    continue\n\n                pkg = self.link_package_data(link)\n                if pkg:\n                    links[pkg.name][pkg.version].append(link)\n\n        return links\n\n\nclass SimpleRepositoryHTMLRootPage(SimpleRepositoryRootPage):\n    \"\"\"\n    This class represents the parsed content of the HTML version\n    of a \"simple\" repository's root page.\n    This follows the specification laid out in PEP 503.\n\n    See: https://peps.python.org/pep-0503/\n    \"\"\"\n\n    def __init__(self, content: str | None = None) -> None:\n        parser = HTMLPageParser()\n        parser.feed(content or \"\")\n        self._parsed = parser.anchors\n\n    @cached_property\n    def package_names(self) -> list[str]:\n        results: list[str] = []\n\n        for anchor in self._parsed:\n            if href := anchor.get(\"href\"):\n                results.append(href.rstrip(\"/\"))\n\n        return results\n"
  },
  {
    "path": "src/poetry/repositories/link_sources/json.py",
    "content": "from __future__ import annotations\n\nimport urllib.parse\n\nfrom collections import defaultdict\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.repositories.link_sources.base import LinkSource\nfrom poetry.repositories.link_sources.base import SimpleRepositoryRootPage\n\n\nif TYPE_CHECKING:\n    from poetry.repositories.link_sources.base import LinkCache\n\n\nclass SimpleJsonPage(LinkSource):\n    \"\"\"Links as returned by PEP 691 compatible JSON-based Simple API.\"\"\"\n\n    def __init__(self, url: str, content: dict[str, Any]) -> None:\n        super().__init__(url=url)\n        self.content = content\n\n    @cached_property\n    def _link_cache(self) -> LinkCache:\n        links: LinkCache = defaultdict(lambda: defaultdict(list))\n        for file in self.content[\"files\"]:\n            url = self.clean_link(urllib.parse.urljoin(self._url, file[\"url\"]))\n            requires_python = file.get(\"requires-python\")\n            hashes = file.get(\"hashes\", {})\n            yanked = file.get(\"yanked\", False)\n            size = file.get(\"size\")\n            upload_time = file.get(\"upload-time\")\n\n            # see https://peps.python.org/pep-0714/#clients\n            # and https://peps.python.org/pep-0691/#project-detail\n            metadata: dict[str, str] | bool = False\n            for metadata_key in (\"core-metadata\", \"dist-info-metadata\"):\n                if metadata_key in file:\n                    metadata_value = file[metadata_key]\n                    if metadata_value and isinstance(metadata_value, dict):\n                        metadata = metadata_value\n                    else:\n                        metadata = bool(metadata_value)\n                    break\n\n            link = Link(\n                url,\n                requires_python=requires_python,\n                hashes=hashes,\n                yanked=yanked,\n                metadata=metadata,\n                size=size,\n                upload_time=upload_time,\n            )\n\n            if link.ext not in self.SUPPORTED_FORMATS:\n                continue\n\n            pkg = self.link_package_data(link)\n            if pkg:\n                links[pkg.name][pkg.version].append(link)\n\n        return links\n\n\nclass SimpleRepositoryJsonRootPage(SimpleRepositoryRootPage):\n    \"\"\"\n    This class represents the parsed content of the JSON version\n    of a \"simple\" repository's root page.\n    This follows the specification laid out in PEP 691.\n\n    See: https://peps.python.org/pep-0691/\n    \"\"\"\n\n    def __init__(self, content: dict[str, Any]) -> None:\n        self._content = content\n\n    @cached_property\n    def package_names(self) -> list[str]:\n        results: list[str] = []\n\n        for project in self._content.get(\"projects\", []):\n            if name := project.get(\"name\"):\n                results.append(name)\n\n        return results\n"
  },
  {
    "path": "src/poetry/repositories/lockfile_repository.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.repositories import Repository\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n\nclass LockfileRepository(Repository):\n    \"\"\"\n    Special repository that distinguishes packages not only by name and version,\n    but also by source type, url, etc.\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__(\"poetry-lockfile\")\n\n    def has_package(self, package: Package) -> bool:\n        return any(p == package for p in self.packages)\n"
  },
  {
    "path": "src/poetry/repositories/parsers/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/repositories/parsers/html_page_parser.py",
    "content": "from __future__ import annotations\n\nfrom html.parser import HTMLParser\n\n\nclass HTMLPageParser(HTMLParser):\n    def __init__(self) -> None:\n        super().__init__()\n        self.base_url: str | None = None\n        self.anchors: list[dict[str, str | None]] = []\n\n    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:\n        if tag == \"base\" and self.base_url is None:\n            base_url = dict(attrs).get(\"href\")\n            if base_url is not None:\n                self.base_url = base_url\n        elif tag == \"a\":\n            self.anchors.append(dict(attrs))\n"
  },
  {
    "path": "src/poetry/repositories/parsers/pypi_search_parser.py",
    "content": "from __future__ import annotations\n\nimport functools\n\nfrom dataclasses import dataclass\nfrom html.parser import HTMLParser\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n\n# The following code was originally written for PDM project\n# https://github.com/pdm-project/pdm/blob/1f4f48a35cdded064def85df117bebf713f7c17a/src/pdm/models/search.py\n# and later changed to fit Poetry needs\n\n\n@dataclass\nclass Result:\n    name: str = \"\"\n    version: str = \"\"\n    description: str = \"\"\n\n\nclass SearchResultParser(HTMLParser):\n    \"\"\"A simple HTML parser for pypi.org search results.\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        self.results: list[Result] = []\n        self._current: Result | None = None\n        self._nest_anchors = 0\n        self._data_callback: Callable[[str], None] | None = None\n\n    @staticmethod\n    def _match_class(attrs: list[tuple[str, str | None]], name: str) -> bool:\n        attrs_map = dict(attrs)\n        return name in (attrs_map.get(\"class\") or \"\").split()\n\n    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:\n        if not self._current:\n            if tag == \"a\" and self._match_class(attrs, \"package-snippet\"):\n                self._current = Result()\n                self._nest_anchors = 1\n        else:\n            if tag == \"span\" and self._match_class(attrs, \"package-snippet__name\"):\n                self._data_callback = functools.partial(setattr, self._current, \"name\")\n            elif tag == \"span\" and self._match_class(attrs, \"package-snippet__version\"):\n                self._data_callback = functools.partial(\n                    setattr, self._current, \"version\"\n                )\n            elif tag == \"p\" and self._match_class(\n                attrs, \"package-snippet__description\"\n            ):\n                self._data_callback = functools.partial(\n                    setattr, self._current, \"description\"\n                )\n            elif tag == \"a\":\n                self._nest_anchors += 1\n\n    def handle_data(self, data: str) -> None:\n        if self._data_callback is not None:\n            self._data_callback(data)\n            self._data_callback = None\n\n    def handle_endtag(self, tag: str) -> None:\n        if tag != \"a\" or self._current is None:\n            return\n        self._nest_anchors -= 1\n        if self._nest_anchors == 0:\n            if self._current.name and self._current.version:\n                self.results.append(self._current)\n            self._current = None\n"
  },
  {
    "path": "src/poetry/repositories/pypi_repository.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport logging\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport requests\nimport requests.adapters\n\nfrom cachecontrol.controller import logger as cache_control_logger\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.utils.link import Link\nfrom poetry.core.version.exceptions import InvalidVersionError\nfrom poetry.core.version.requirements import InvalidRequirementError\n\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.http_repository import HTTPRepository\nfrom poetry.repositories.link_sources.json import SimpleJsonPage\nfrom poetry.repositories.parsers.pypi_search_parser import SearchResultParser\nfrom poetry.utils.constants import REQUESTS_TIMEOUT\n\n\ncache_control_logger.setLevel(logging.ERROR)\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import Version\n    from poetry.core.constraints.version import VersionConstraint\n\n    from poetry.config.config import Config\n\nSUPPORTED_PACKAGE_TYPES = {\"sdist\", \"bdist_wheel\"}\n\n\nclass PyPiRepository(HTTPRepository):\n    def __init__(\n        self,\n        url: str = \"https://pypi.org/\",\n        *,\n        config: Config | None = None,\n        disable_cache: bool = False,\n        pool_size: int = requests.adapters.DEFAULT_POOLSIZE,\n        fallback: bool = True,\n    ) -> None:\n        super().__init__(\n            \"PyPI\",\n            url.rstrip(\"/\") + \"/simple/\",\n            config=config,\n            disable_cache=disable_cache,\n            pool_size=pool_size,\n        )\n\n        self._base_url = url\n        self._fallback = fallback\n\n    def search(self, query: str | list[str]) -> list[Package]:\n        results = []\n\n        response = requests.get(\n            self._base_url + \"search\", params={\"q\": query}, timeout=REQUESTS_TIMEOUT\n        )\n        parser = SearchResultParser()\n        parser.feed(response.text)\n\n        for result in parser.results:\n            try:\n                package = Package(result.name, result.version)\n                package.description = result.description.strip()\n                results.append(package)\n            except InvalidVersionError:\n                self._log(\n                    f'Unable to parse version \"{result.version}\" for the'\n                    f\" {result.name} package, skipping\",\n                    level=\"debug\",\n                )\n\n        if not results:\n            # in cases like PyPI search might not be available, we fallback to explicit searches\n            # to allow for a nicer ux rather than finding nothing at all\n            # see: https://discuss.python.org/t/fastly-interfering-with-pypi-search/73597/6\n            #\n            tokens = query if isinstance(query, list) else [query]\n            for token in tokens:\n                with contextlib.suppress(InvalidRequirementError):\n                    results.extend(\n                        self.find_packages(Dependency.create_from_pep_508(token))\n                    )\n\n        return results\n\n    def get_package_info(self, name: NormalizedName) -> dict[str, Any]:\n        \"\"\"\n        Return the package information given its name.\n\n        The information is returned from the cache if it exists\n        or retrieved from the remote server.\n        \"\"\"\n        return self._get_package_info(name)\n\n    def _find_packages(\n        self, name: NormalizedName, constraint: VersionConstraint\n    ) -> list[Package]:\n        \"\"\"\n        Find packages on the remote server.\n        \"\"\"\n        try:\n            json_page = self.get_page(name)\n        except PackageNotFoundError:\n            self._log(f\"No packages found for {name}\", level=\"debug\")\n            return []\n\n        versions = [\n            (version, json_page.yanked(name, version))\n            for version in json_page.versions(name)\n            if constraint.allows(version)\n        ]\n\n        return [Package(name, version, yanked=yanked) for version, yanked in versions]\n\n    def _get_package_info(self, name: NormalizedName) -> dict[str, Any]:\n        headers = {\"Accept\": \"application/vnd.pypi.simple.v1+json\"}\n        info = self._get(f\"simple/{name}/\", headers=headers)\n        if info is None:\n            raise PackageNotFoundError(f\"Package [{name}] not found.\")\n\n        return info\n\n    def find_links_for_package(self, package: Package) -> list[Link]:\n        json_data = self._get(f\"pypi/{package.name}/{package.version}/json\")\n        if json_data is None:\n            return []\n\n        links = []\n        for url in json_data[\"urls\"]:\n            if url[\"packagetype\"] in SUPPORTED_PACKAGE_TYPES:\n                h = f\"sha256={url['digests']['sha256']}\"\n                links.append(Link(url[\"url\"] + \"#\" + h, yanked=self._get_yanked(url)))\n\n        return links\n\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]:\n        from poetry.inspection.info import PackageInfo\n\n        self._log(f\"Getting info for {name} ({version}) from PyPI\", \"debug\")\n\n        json_data = self._get(f\"pypi/{name}/{version}/json\")\n        if json_data is None:\n            raise PackageNotFoundError(f\"Package [{name}] not found.\")\n\n        info = json_data[\"info\"]\n\n        data = PackageInfo(\n            name=info[\"name\"],\n            version=info[\"version\"],\n            summary=info[\"summary\"],\n            requires_dist=info[\"requires_dist\"],\n            requires_python=info[\"requires_python\"],\n            yanked=self._get_yanked(info),\n            cache_version=str(self.CACHE_VERSION),\n        )\n\n        try:\n            version_info = json_data[\"urls\"]\n        except KeyError:\n            version_info = []\n\n        files = info.get(\"files\", [])\n        for file_info in version_info:\n            if file_info[\"packagetype\"] in SUPPORTED_PACKAGE_TYPES:\n                files.append(\n                    {\n                        \"file\": file_info[\"filename\"],\n                        \"hash\": \"sha256:\" + file_info[\"digests\"][\"sha256\"],\n                        \"url\": file_info[\"url\"],\n                    }\n                )\n                if (size := file_info.get(\"size\")) is not None:\n                    files[-1][\"size\"] = size\n                if upload_time := file_info.get(\"upload_time_iso_8601\"):\n                    files[-1][\"upload_time\"] = upload_time\n        data.files = files\n\n        if self._fallback and data.requires_dist is None:\n            self._log(\n                \"No dependencies found, downloading metadata and/or archives\",\n                level=\"debug\",\n            )\n            # No dependencies set (along with other information)\n            # This might be due to actually no dependencies\n            # or badly set metadata when uploading.\n            # So, we need to make sure there is actually no\n            # dependencies by introspecting packages.\n            page = self.get_page(name)\n            links = list(page.links_for_version(name, version))\n            info = self._get_info_from_links(links, ignore_yanked=not data.yanked)\n\n            data.requires_dist = info.requires_dist\n\n            if not data.requires_python:\n                data.requires_python = info.requires_python\n\n        return data.asdict()\n\n    def _get_page(self, name: NormalizedName) -> SimpleJsonPage:\n        source = self._base_url + f\"simple/{name}/\"\n        info = self.get_package_info(name)\n        return SimpleJsonPage(source, info)\n\n    def _get(\n        self, endpoint: str, headers: dict[str, str] | None = None\n    ) -> dict[str, Any] | None:\n        try:\n            json_response = self.session.get(\n                self._base_url + endpoint,\n                raise_for_status=False,\n                timeout=REQUESTS_TIMEOUT,\n                headers=headers,\n            )\n        except requests.exceptions.TooManyRedirects:\n            # Cache control redirect loop.\n            # We try to remove the cache and try again\n            self.session.delete_cache(self._base_url + endpoint)\n            json_response = self.session.get(\n                self._base_url + endpoint,\n                raise_for_status=False,\n                timeout=REQUESTS_TIMEOUT,\n                headers=headers,\n            )\n\n        if json_response.status_code != 200:\n            return None\n\n        json: dict[str, Any] = json_response.json()\n        return json\n\n    @staticmethod\n    def _get_yanked(json_data: dict[str, Any]) -> str | bool:\n        if json_data.get(\"yanked\", False):\n            return json_data.get(\"yanked_reason\") or True\n        return False\n"
  },
  {
    "path": "src/poetry/repositories/repository.py",
    "content": "from __future__ import annotations\n\nimport logging\n\nfrom typing import TYPE_CHECKING\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.repositories.abstract_repository import AbstractRepository\nfrom poetry.repositories.exceptions import PackageNotFoundError\n\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import VersionConstraint\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.utils.link import Link\n\n\nclass Repository(AbstractRepository):\n    def __init__(self, name: str, packages: Sequence[Package] | None = None) -> None:\n        super().__init__(name)\n        self._packages: list[Package] = []\n\n        for package in packages or []:\n            self.add_package(package)\n\n    @property\n    def packages(self) -> list[Package]:\n        return self._packages\n\n    def find_packages(self, dependency: Dependency) -> list[Package]:\n        packages = []\n        ignored_pre_release_packages = []\n\n        constraint = dependency.constraint\n        allow_prereleases = dependency.allows_prereleases()\n        for package in self._find_packages(dependency.name, constraint):\n            if package.yanked and not isinstance(constraint, Version):\n                # PEP 592: yanked files are always ignored, unless they are the only\n                # file that matches a version specifier that \"pins\" to an exact\n                # version\n                continue\n            if (\n                package.is_prerelease()\n                and not allow_prereleases\n                and not package.is_direct_origin()\n            ):\n                ignored_pre_release_packages.append(package)\n                continue\n\n            packages.append(package)\n\n        self._log(\n            f\"{len(packages)} packages found for {dependency.name} {constraint!s}\",\n            level=\"debug\",\n        )\n\n        if allow_prereleases is False:  # in contrast to None!\n            return packages\n        return packages or ignored_pre_release_packages\n\n    def has_package(self, package: Package) -> bool:\n        package_id = package.unique_name\n        return any(\n            package_id == repo_package.unique_name for repo_package in self.packages\n        )\n\n    def add_package(self, package: Package) -> None:\n        self._packages.append(package)\n\n    def search(self, query: str | list[str]) -> list[Package]:\n        results: list[Package] = []\n        tokens = query if isinstance(query, list) else [query]\n\n        for package in self.packages:\n            if any(token in package.name for token in tokens):\n                results.append(package)\n\n        return results\n\n    def _find_packages(\n        self, name: NormalizedName, constraint: VersionConstraint\n    ) -> list[Package]:\n        return [\n            package\n            for package in self._packages\n            if package.name == name and constraint.allows(package.version)\n        ]\n\n    def _log(self, msg: str, level: str = \"info\") -> None:\n        logger = logging.getLogger(f\"{__name__}.{self.__class__.__name__}\")\n        getattr(logger, level)(f\"<c1>Source ({self.name}):</c1> {msg}\")\n\n    def __len__(self) -> int:\n        return len(self._packages)\n\n    def find_links_for_package(self, package: Package) -> list[Link]:\n        return []\n\n    def package(self, name: str, version: Version) -> Package:\n        canonicalized_name = canonicalize_name(name)\n        for package in self.packages:\n            if canonicalized_name == package.name and package.version == version:\n                return package\n\n        raise PackageNotFoundError(f\"Package {name} ({version}) not found.\")\n"
  },
  {
    "path": "src/poetry/repositories/repository_pool.py",
    "content": "from __future__ import annotations\n\nimport enum\n\nfrom collections import OrderedDict\nfrom dataclasses import dataclass\nfrom enum import IntEnum\nfrom typing import TYPE_CHECKING\n\nfrom poetry.config.config import Config\nfrom poetry.repositories.abstract_repository import AbstractRepository\nfrom poetry.repositories.cached_repository import CachedRepository\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.repository import Repository\nfrom poetry.utils.cache import ArtifactCache\n\n\nif TYPE_CHECKING:\n    from poetry.core.constraints.version import Version\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n\n\nclass Priority(IntEnum):\n    # The order of the members below dictates the actual priority. The first member has\n    # top priority.\n    PRIMARY = enum.auto()\n    SUPPLEMENTAL = enum.auto()\n    EXPLICIT = enum.auto()\n\n\n@dataclass(frozen=True)\nclass PrioritizedRepository:\n    repository: Repository\n    priority: Priority\n\n\nclass RepositoryPool(AbstractRepository):\n    def __init__(\n        self,\n        repositories: list[Repository] | None = None,\n        *,\n        config: Config | None = None,\n    ) -> None:\n        super().__init__(\"poetry-repository-pool\")\n        self._repositories: OrderedDict[str, PrioritizedRepository] = OrderedDict()\n\n        if repositories is None:\n            repositories = []\n        for repository in repositories:\n            self.add_repository(repository)\n\n        self._artifact_cache = ArtifactCache(\n            cache_dir=(config or Config.create()).artifacts_cache_directory\n        )\n\n    @staticmethod\n    def from_packages(packages: list[Package], config: Config | None) -> RepositoryPool:\n        pool = RepositoryPool(config=config)\n        for package in packages:\n            if package.is_direct_origin():\n                continue\n\n            repo_name = package.source_reference or \"PyPI\"\n            try:\n                repo = pool.repository(repo_name)\n            except IndexError:\n                repo = Repository(repo_name)\n                pool.add_repository(repo)\n\n            if not repo.has_package(package):\n                repo.add_package(package)\n\n        return pool\n\n    @property\n    def repositories(self) -> list[Repository]:\n        \"\"\"\n        Returns the repositories in the pool,\n        in the order they will be searched for packages.\n\n        ATTENTION: For backwards compatibility and practical reasons,\n                   repositories with priority EXPLICIT are NOT included,\n                   because they will not be searched.\n        \"\"\"\n        sorted_repositories = self._sorted_repositories\n        return [\n            prio_repo.repository\n            for prio_repo in sorted_repositories\n            if prio_repo.priority is not Priority.EXPLICIT\n        ]\n\n    @property\n    def all_repositories(self) -> list[Repository]:\n        return [prio_repo.repository for prio_repo in self._sorted_repositories]\n\n    @property\n    def _sorted_repositories(self) -> list[PrioritizedRepository]:\n        return sorted(\n            self._repositories.values(), key=lambda prio_repo: prio_repo.priority\n        )\n\n    @property\n    def artifact_cache(self) -> ArtifactCache:\n        return self._artifact_cache\n\n    def has_primary_repositories(self) -> bool:\n        return self._contains_priority(Priority.PRIMARY)\n\n    def _contains_priority(self, priority: Priority) -> bool:\n        return any(\n            prio_repo.priority is priority for prio_repo in self._repositories.values()\n        )\n\n    def has_repository(self, name: str) -> bool:\n        return name.lower() in self._repositories\n\n    def repository(self, name: str) -> Repository:\n        return self._get_prioritized_repository(name).repository\n\n    def get_priority(self, name: str) -> Priority:\n        return self._get_prioritized_repository(name).priority\n\n    def _get_prioritized_repository(self, name: str) -> PrioritizedRepository:\n        name = name.lower()\n        if self.has_repository(name):\n            return self._repositories[name]\n        raise IndexError(f'Repository \"{name}\" does not exist.')\n\n    def add_repository(\n        self, repository: Repository, *, priority: Priority = Priority.PRIMARY\n    ) -> RepositoryPool:\n        \"\"\"\n        Adds a repository to the pool.\n        \"\"\"\n        repository_name = repository.name.lower()\n        if self.has_repository(repository_name):\n            raise ValueError(\n                f\"A repository with name {repository_name} was already added.\"\n            )\n\n        self._repositories[repository_name] = PrioritizedRepository(\n            repository, priority\n        )\n        return self\n\n    def remove_repository(self, name: str) -> RepositoryPool:\n        if not self.has_repository(name):\n            raise IndexError(\n                f\"RepositoryPool can not remove unknown repository '{name}'.\"\n            )\n        del self._repositories[name.lower()]\n        return self\n\n    def package(\n        self, name: str, version: Version, repository_name: str | None = None\n    ) -> Package:\n        if repository_name:\n            return self.repository(repository_name).package(name, version)\n\n        for repo in self.repositories:\n            try:\n                return repo.package(name, version)\n            except PackageNotFoundError:\n                continue\n        raise PackageNotFoundError(f\"Package {name} ({version}) not found.\")\n\n    def find_packages(self, dependency: Dependency) -> list[Package]:\n        repository_name = dependency.source_name\n        if repository_name:\n            return self.repository(repository_name).find_packages(dependency)\n\n        packages: list[Package] = []\n        for repo in self.repositories:\n            if packages and self.get_priority(repo.name) is Priority.SUPPLEMENTAL:\n                break\n            packages += repo.find_packages(dependency)\n        return packages\n\n    def search(self, query: str | list[str]) -> list[Package]:\n        results: list[Package] = []\n        for repo in self.repositories:\n            results += repo.search(query)\n        return results\n\n    def refresh(self, package: Package) -> Package:\n        repository_name = package.source_reference or \"PyPI\"\n        repo = self.repository(repository_name)\n        if isinstance(repo, CachedRepository):\n            repo.forget(package.name, package.version)\n        return repo.package(package.name, package.version)\n"
  },
  {
    "path": "src/poetry/repositories/single_page_repository.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.repositories.link_sources.html import HTMLPage\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n\n\nclass SinglePageRepository(LegacyRepository):\n    def _get_page(self, name: NormalizedName) -> HTMLPage:\n        \"\"\"\n        Single page repositories only have one page irrespective of endpoint.\n        \"\"\"\n        response = self._get_response(\"\")\n        if not response:\n            raise PackageNotFoundError(f\"Package [{name}] not found.\")\n        return HTMLPage(response.url, response.text)\n"
  },
  {
    "path": "src/poetry/toml/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.toml.exceptions import TOMLError\nfrom poetry.toml.file import TOMLFile\n\n\n__all__ = [\"TOMLError\", \"TOMLFile\"]\n"
  },
  {
    "path": "src/poetry/toml/exceptions.py",
    "content": "from __future__ import annotations\n\nfrom poetry.core.exceptions import PoetryCoreError\nfrom tomlkit.exceptions import TOMLKitError\n\n\nclass TOMLError(TOMLKitError, PoetryCoreError):\n    pass\n"
  },
  {
    "path": "src/poetry/toml/file.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom tomlkit.toml_file import TOMLFile as BaseTOMLFile\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from tomlkit.toml_document import TOMLDocument\n\n\nclass TOMLFile(BaseTOMLFile):\n    def __init__(self, path: Path) -> None:\n        super().__init__(path)\n        self.__path = path\n\n    @property\n    def path(self) -> Path:\n        return self.__path\n\n    def exists(self) -> bool:\n        return self.__path.exists()\n\n    def read(self) -> TOMLDocument:\n        from tomlkit.exceptions import TOMLKitError\n\n        from poetry.toml import TOMLError\n\n        try:\n            return super().read()\n        except (ValueError, TOMLKitError) as e:\n            raise TOMLError(f\"Invalid TOML file {self.path.as_posix()}: {e}\")\n\n    def __str__(self) -> str:\n        return self.__path.as_posix()\n"
  },
  {
    "path": "src/poetry/utils/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/utils/_compat.py",
    "content": "from __future__ import annotations\n\nimport locale\nimport sys\nimport warnings\n\nfrom contextlib import suppress\n\n\nif sys.version_info < (3, 11):\n    # compatibility for python <3.11\n    import tomli as tomllib\nelse:\n    import tomllib\n\nfrom importlib import metadata as _metadata\n\n\nWINDOWS = sys.platform == \"win32\"\n\n\ndef decode(string: bytes | str, encodings: list[str] | None = None) -> str:\n    if not isinstance(string, bytes):\n        return string\n\n    encodings = encodings or [\"utf-8\", \"latin1\", \"ascii\"]\n\n    for encoding in encodings:\n        with suppress(UnicodeEncodeError, UnicodeDecodeError):\n            return string.decode(encoding)\n\n    return string.decode(encodings[0], errors=\"ignore\")\n\n\ndef encode(string: str, encodings: list[str] | None = None) -> bytes:\n    if isinstance(string, bytes):\n        return string\n\n    encodings = encodings or [\"utf-8\", \"latin1\", \"ascii\"]\n\n    for encoding in encodings:\n        with suppress(UnicodeEncodeError, UnicodeDecodeError):\n            return string.encode(encoding)\n\n    return string.encode(encodings[0], errors=\"ignore\")\n\n\ndef getencoding() -> str:\n    if sys.version_info < (3, 11):\n        return locale.getpreferredencoding()\n    else:\n        return locale.getencoding()\n\n\ndef __getattr__(name: str) -> object:\n    if name == \"metadata\":\n        warnings.warn(\n            \"Importing `metadata` from `poetry.utils._compat` is deprecated;\"\n            \" use `importlib.metadata` directly.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        return _metadata\n    raise AttributeError\n\n\n__all__ = [\n    \"WINDOWS\",\n    \"decode\",\n    \"encode\",\n    \"getencoding\",\n    \"tomllib\",\n]\n"
  },
  {
    "path": "src/poetry/utils/authenticator.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport functools\nimport logging\nimport time\nimport urllib.parse\n\nfrom os.path import commonprefix\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport requests\nimport requests.adapters\nimport requests.auth\nimport requests.exceptions\n\nfrom cachecontrol import CacheControlAdapter\nfrom cachecontrol.caches import FileCache\nfrom requests_toolbelt import user_agent\n\nfrom poetry.__version__ import __version__\nfrom poetry.config.config import Config\nfrom poetry.console.exceptions import ConsoleMessage\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.exceptions import PoetryError\nfrom poetry.utils.constants import REQUESTS_TIMEOUT\nfrom poetry.utils.constants import RETRY_AFTER_HEADER\nfrom poetry.utils.constants import STATUS_FORCELIST\nfrom poetry.utils.password_manager import HTTPAuthCredential\nfrom poetry.utils.password_manager import PasswordManager\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclasses.dataclass(frozen=True)\nclass RepositoryCertificateConfig:\n    cert: Path | None = dataclasses.field(default=None)\n    client_cert: Path | None = dataclasses.field(default=None)\n    verify: bool = dataclasses.field(default=True)\n\n    @classmethod\n    def create(\n        cls, repository: str, config: Config | None\n    ) -> RepositoryCertificateConfig:\n        config = config if config else Config.create()\n\n        verify: str | bool = config.get(\n            f\"certificates.{repository}.verify\",\n            config.get(f\"certificates.{repository}.cert\", True),\n        )\n        client_cert: str = config.get(f\"certificates.{repository}.client-cert\")\n\n        return cls(\n            cert=Path(verify) if isinstance(verify, str) else None,\n            client_cert=Path(client_cert) if client_cert else None,\n            verify=verify if isinstance(verify, bool) else True,\n        )\n\n\n@dataclasses.dataclass\nclass AuthenticatorRepositoryConfig:\n    name: str\n    url: str\n    netloc: str = dataclasses.field(init=False)\n    path: str = dataclasses.field(init=False)\n\n    def __post_init__(self) -> None:\n        parsed_url = urllib.parse.urlsplit(self.url)\n        self.netloc = parsed_url.netloc\n        self.path = parsed_url.path\n\n    def certs(self, config: Config) -> RepositoryCertificateConfig:\n        return RepositoryCertificateConfig.create(self.name, config)\n\n    def get_http_credentials(\n        self, password_manager: PasswordManager\n    ) -> HTTPAuthCredential:\n        # try with the repository name via the password manager\n        credential = password_manager.get_http_auth(self.name)\n\n        if credential.password is not None:\n            return credential\n\n        if password_manager.use_keyring:\n            # fallback to url and netloc based keyring entries\n            credential = password_manager.get_credential(\n                self.url, self.netloc, username=credential.username\n            )\n\n        return credential\n\n\nclass Authenticator:\n    def __init__(\n        self,\n        config: Config | None = None,\n        io: IO | None = None,\n        cache_id: str | None = None,\n        disable_cache: bool = False,\n        pool_size: int = requests.adapters.DEFAULT_POOLSIZE,\n    ) -> None:\n        self._config = config or Config.create()\n        self._io = io\n        self._sessions_for_netloc: dict[str, requests.Session] = {}\n        self._credentials: dict[str, HTTPAuthCredential] = {}\n        self._certs: dict[str, RepositoryCertificateConfig] = {}\n        self._configured_repositories: (\n            dict[str, AuthenticatorRepositoryConfig] | None\n        ) = None\n        self._password_manager = PasswordManager(self._config)\n        self._cache_control = (\n            FileCache(\n                self._config.repository_cache_directory\n                / (cache_id or \"_default_cache\")\n                / \"_http\"\n            )\n            if not disable_cache\n            else None\n        )\n        self.get_repository_config_for_url = functools.lru_cache(maxsize=None)(\n            self._get_repository_config_for_url\n        )\n        self._pool_size = pool_size\n        self._user_agent = user_agent(\"poetry\", __version__)\n\n    def create_session(self) -> requests.Session:\n        session = requests.Session()\n        session.headers[\"User-Agent\"] = self._user_agent\n\n        if self._cache_control is None:\n            return session\n\n        adapter = CacheControlAdapter(\n            cache=self._cache_control,\n            pool_maxsize=self._pool_size,\n        )\n        session.mount(\"http://\", adapter)\n        session.mount(\"https://\", adapter)\n\n        return session\n\n    def get_session(self, url: str | None = None) -> requests.Session:\n        if not url:\n            return self.create_session()\n\n        parsed_url = urllib.parse.urlsplit(url)\n        netloc = parsed_url.netloc\n\n        if netloc not in self._sessions_for_netloc:\n            logger.debug(\"Creating new session for %s\", netloc)\n            self._sessions_for_netloc[netloc] = self.create_session()\n\n        return self._sessions_for_netloc[netloc]\n\n    def close(self) -> None:\n        for session in self._sessions_for_netloc.values():\n            if session is not None:\n                session.close()\n\n    def __del__(self) -> None:\n        self.close()\n\n    def delete_cache(self, url: str) -> None:\n        if self._cache_control is not None:\n            self._cache_control.delete(key=url)\n\n    def authenticated_url(self, url: str) -> str:\n        parsed = urllib.parse.urlparse(url)\n        credential = self.get_credentials_for_url(url)\n\n        if credential.username is not None and credential.password is not None:\n            username = urllib.parse.quote(credential.username, safe=\"\")\n            password = urllib.parse.quote(credential.password, safe=\"\")\n\n            return (\n                f\"{parsed.scheme}://{username}:{password}@{parsed.netloc}{parsed.path}\"\n            )\n\n        return url\n\n    def request(\n        self, method: str, url: str, raise_for_status: bool = True, **kwargs: Any\n    ) -> requests.Response:\n        headers = kwargs.get(\"headers\")\n        request = requests.Request(method, url, headers=headers)\n        credential = self.get_credentials_for_url(url)\n\n        if credential.username is not None or credential.password is not None:\n            request = requests.auth.HTTPBasicAuth(\n                credential.username or \"\", credential.password or \"\"\n            )(request)\n\n        session = self.get_session(url=url)\n        prepared_request = session.prepare_request(request)\n\n        proxies: dict[str, str] = kwargs.get(\"proxies\", {})\n        stream: bool | None = kwargs.get(\"stream\")\n\n        certs = self.get_certs_for_url(url)\n        verify: bool | str | Path = kwargs.get(\"verify\") or certs.cert or certs.verify\n        cert: str | Path | None = kwargs.get(\"cert\") or certs.client_cert\n\n        if cert is not None:\n            cert = str(cert)\n\n        verify = str(verify) if isinstance(verify, Path) else verify\n\n        settings = session.merge_environment_settings(\n            prepared_request.url, proxies, stream, verify, cert\n        )\n\n        # Send the request.\n        send_kwargs = {\n            \"timeout\": kwargs.get(\"timeout\", REQUESTS_TIMEOUT),\n            \"allow_redirects\": kwargs.get(\"allow_redirects\", True),\n        }\n        send_kwargs.update(settings)\n\n        attempt = 0\n        resp = None\n\n        while True:\n            is_last_attempt = attempt >= 5\n            try:\n                resp = session.send(prepared_request, **send_kwargs)\n            except (requests.exceptions.ConnectionError, OSError) as e:\n                if is_last_attempt:\n                    parsed_url = urllib.parse.urlsplit(url)\n                    exc = PoetryRuntimeError.create(\n                        reason=f\"<error>All attempts to connect to <c1>{parsed_url.netloc}</> failed.</>\",\n                        exception=e,\n                    )\n                    exc.append(\n                        ConsoleMessage(\n                            \"the server is not responding to requests at the moment\\n\"\n                            \"the hostname cannot be resolved by your DNS\\n\"\n                            \"your network is not connected to the internet\\n\"\n                        )\n                        .indent(\"    - \")\n                        .make_section(\"Probable Causes\")\n                        .wrap(\"warning\")\n                    )\n                    exc.append(\n                        ConsoleMessage(\n                            f\"<b>Note:</> The path requested was <c1>{parsed_url.path}</>.\",\n                            debug=True,\n                        )\n                    )\n                    raise exc\n            else:\n                if resp.status_code not in STATUS_FORCELIST or is_last_attempt:\n                    if raise_for_status:\n                        resp.raise_for_status()\n                    return resp\n\n            if not is_last_attempt:\n                attempt += 1\n                delay = self._get_backoff(resp, attempt)\n                logger.debug(\"Retrying HTTP request in %s seconds.\", delay)\n                time.sleep(delay)\n                continue\n\n        # this should never really be hit under any sane circumstance\n        raise PoetryError(f\"Failed HTTP request: {method.upper()} {url}\")\n\n    def _get_backoff(self, response: requests.Response | None, attempt: int) -> float:\n        if response is not None:\n            retry_after = response.headers.get(RETRY_AFTER_HEADER, \"\")\n            if retry_after:\n                return float(retry_after)\n\n        return 0.5 * attempt\n\n    def get(self, url: str, **kwargs: Any) -> requests.Response:\n        return self.request(\"get\", url, **kwargs)\n\n    def head(self, url: str, **kwargs: Any) -> requests.Response:\n        kwargs.setdefault(\"allow_redirects\", False)\n        return self.request(\"head\", url, **kwargs)\n\n    def post(self, url: str, **kwargs: Any) -> requests.Response:\n        return self.request(\"post\", url, **kwargs)\n\n    def _get_credentials_for_repository(\n        self, repository: AuthenticatorRepositoryConfig\n    ) -> HTTPAuthCredential:\n        # cache repository credentials by repository url to avoid multiple keyring\n        # backend queries when packages are being downloaded from the same source\n        key = repository.url\n\n        if key not in self._credentials:\n            self._credentials[key] = repository.get_http_credentials(\n                password_manager=self._password_manager\n            )\n\n        return self._credentials[key]\n\n    def _get_credentials_for_url(\n        self, url: str, exact_match: bool = False\n    ) -> HTTPAuthCredential:\n        repository = self.get_repository_config_for_url(url, exact_match)\n\n        credential = (\n            self._get_credentials_for_repository(repository=repository)\n            if repository is not None\n            else HTTPAuthCredential()\n        )\n\n        if credential.password is None:\n            parsed_url = urllib.parse.urlsplit(url)\n            netloc = parsed_url.netloc\n            credential = self._password_manager.get_credential(\n                url, netloc, username=credential.username\n            )\n\n            return HTTPAuthCredential(\n                username=credential.username, password=credential.password\n            )\n\n        return credential\n\n    def get_credentials_for_git_url(self, url: str) -> HTTPAuthCredential:\n        parsed_url = urllib.parse.urlsplit(url)\n\n        if parsed_url.scheme not in {\"http\", \"https\"}:\n            return HTTPAuthCredential()\n\n        key = f\"git+{url}\"\n\n        if key not in self._credentials:\n            self._credentials[key] = self._get_credentials_for_url(url, True)\n\n        return self._credentials[key]\n\n    def get_credentials_for_url(self, url: str) -> HTTPAuthCredential:\n        parsed_url = urllib.parse.urlsplit(url)\n        netloc = parsed_url.netloc\n\n        if url not in self._credentials:\n            if \"@\" not in netloc:\n                # no credentials were provided in the url, try finding the\n                # best repository configuration\n                self._credentials[url] = self._get_credentials_for_url(url)\n            else:\n                # Split from the right because that's how urllib.parse.urlsplit()\n                # behaves if more than one @ is present (which can be checked using\n                # the password attribute of urlsplit()'s return value).\n                auth, netloc = netloc.rsplit(\"@\", 1)\n                # Split from the left because that's how urllib.parse.urlsplit()\n                # behaves if more than one : is present (which again can be checked\n                # using the password attribute of the return value)\n                user, password = auth.split(\":\", 1) if \":\" in auth else (auth, \"\")\n                self._credentials[url] = HTTPAuthCredential(\n                    urllib.parse.unquote(user),\n                    urllib.parse.unquote(password),\n                )\n\n        return self._credentials[url]\n\n    def get_pypi_token(self, name: str) -> str | None:\n        return self._password_manager.get_pypi_token(name)\n\n    def get_http_auth(self, name: str) -> HTTPAuthCredential | None:\n        if name == \"pypi\":\n            repository = AuthenticatorRepositoryConfig(\n                name, \"https://upload.pypi.org/legacy/\"\n            )\n        else:\n            if name not in self.configured_repositories:\n                return None\n            repository = self.configured_repositories[name]\n\n        return self._get_credentials_for_repository(repository=repository)\n\n    def get_certs_for_repository(self, name: str) -> RepositoryCertificateConfig:\n        if name.lower() == \"pypi\" or name not in self.configured_repositories:\n            return RepositoryCertificateConfig()\n        return self.configured_repositories[name].certs(self._config)\n\n    @property\n    def configured_repositories(self) -> dict[str, AuthenticatorRepositoryConfig]:\n        if self._configured_repositories is None:\n            self._configured_repositories = {}\n            for repository_name in self._config.get(\"repositories\", []):\n                url = self._config.get(f\"repositories.{repository_name}.url\")\n                self._configured_repositories[repository_name] = (\n                    AuthenticatorRepositoryConfig(repository_name, url)\n                )\n\n        return self._configured_repositories\n\n    def reset_credentials_cache(self) -> None:\n        self.get_repository_config_for_url.cache_clear()\n        self._credentials = {}\n\n    def add_repository(self, name: str, url: str) -> None:\n        self.configured_repositories[name] = AuthenticatorRepositoryConfig(name, url)\n        self.reset_credentials_cache()\n\n    def get_certs_for_url(self, url: str) -> RepositoryCertificateConfig:\n        if url not in self._certs:\n            self._certs[url] = self._get_certs_for_url(url)\n        return self._certs[url]\n\n    def _get_repository_config_for_url(\n        self, url: str, exact_match: bool = False\n    ) -> AuthenticatorRepositoryConfig | None:\n        parsed_url = urllib.parse.urlsplit(url)\n        candidates_netloc_only = []\n        candidates_path_match = []\n\n        for repository in self.configured_repositories.values():\n            if exact_match:\n                if parsed_url.path == repository.path:\n                    return repository\n                continue\n\n            if repository.netloc == parsed_url.netloc:\n                if parsed_url.path.startswith(repository.path) or commonprefix(\n                    (parsed_url.path, repository.path)\n                ):\n                    candidates_path_match.append(repository)\n                    continue\n                candidates_netloc_only.append(repository)\n\n        if candidates_path_match:\n            candidates = candidates_path_match\n        elif candidates_netloc_only:\n            candidates = candidates_netloc_only\n        else:\n            return None\n\n        if len(candidates) > 1:\n            logger.debug(\n                \"Multiple source configurations found for %s - %s\",\n                parsed_url.netloc,\n                \", \".join(c.name for c in candidates),\n            )\n            # prefer the more specific path\n            candidates.sort(\n                key=lambda c: len(commonprefix([parsed_url.path, c.path])), reverse=True\n            )\n\n        return candidates[0]\n\n    def _get_certs_for_url(self, url: str) -> RepositoryCertificateConfig:\n        selected = self.get_repository_config_for_url(url)\n        if selected:\n            return selected.certs(config=self._config)\n        return RepositoryCertificateConfig()\n\n\n_authenticator: Authenticator | None = None\n\n\ndef get_default_authenticator() -> Authenticator:\n    global _authenticator\n\n    if _authenticator is None:\n        _authenticator = Authenticator()\n\n    return _authenticator\n"
  },
  {
    "path": "src/poetry/utils/cache.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport hashlib\nimport json\nimport logging\nimport shutil\nimport threading\nimport time\n\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Generic\nfrom typing import TypeVar\nfrom typing import overload\n\nfrom poetry.utils._compat import decode\nfrom poetry.utils._compat import encode\nfrom poetry.utils.helpers import get_highest_priority_hash_type\nfrom poetry.utils.wheel import InvalidWheelNameError\nfrom poetry.utils.wheel import Wheel\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from poetry.core.packages.utils.link import Link\n\n    from poetry.utils.env import Env\n\n\n# Used by FileCache for items that do not expire.\nMAX_DATE = 9999999999\nT = TypeVar(\"T\")\n\nlogger = logging.getLogger(__name__)\n\n\ndef _expiration(minutes: int) -> int:\n    \"\"\"\n    Calculates the time in seconds since epoch that occurs 'minutes' from now.\n\n    :param minutes: The number of minutes to count forward\n    \"\"\"\n    return round(time.time()) + minutes * 60\n\n\n_HASHES = {\n    \"md5\": (hashlib.md5, 2),\n    \"sha1\": (hashlib.sha1, 4),\n    \"sha256\": (hashlib.sha256, 8),\n}\n\n\n@dataclasses.dataclass(frozen=True)\nclass CacheItem(Generic[T]):\n    \"\"\"\n    Stores data and metadata for cache items.\n    \"\"\"\n\n    data: T\n    expires: int | None = None\n\n    @property\n    def expired(self) -> bool:\n        \"\"\"\n        Return true if the cache item has exceeded its expiration period.\n        \"\"\"\n        return self.expires is not None and time.time() >= self.expires\n\n\n@dataclasses.dataclass(frozen=True)\nclass FileCache(Generic[T]):\n    \"\"\"\n    Cachy-compatible minimal file cache. Stores subsequent data in a JSON format.\n\n    :param path: The path that the cache starts at.\n    :param hash_type: The hash to use for encoding keys/building directories.\n    \"\"\"\n\n    path: Path\n    hash_type: str = \"sha256\"\n\n    def __post_init__(self) -> None:\n        if self.hash_type not in _HASHES:\n            raise ValueError(\n                f\"FileCache.hash_type is unknown value: '{self.hash_type}'.\"\n            )\n\n    def get(self, key: str) -> T | None:\n        return self._get_payload(key)\n\n    def has(self, key: str) -> bool:\n        \"\"\"\n        Determine if a file exists and has not expired in the cache.\n        :param key: The cache key\n        :returns: True if the key exists in the cache\n        \"\"\"\n        return self.get(key) is not None\n\n    def put(self, key: str, value: Any, minutes: int | None = None) -> None:\n        \"\"\"\n        Store an item in the cache.\n\n        :param key: The cache key\n        :param value: The cache value\n        :param minutes: The lifetime in minutes of the cached value\n        \"\"\"\n        payload: CacheItem[Any] = CacheItem(\n            value, expires=_expiration(minutes) if minutes is not None else None\n        )\n        path = self._path(key)\n        path.parent.mkdir(parents=True, exist_ok=True)\n        with path.open(\"wb\") as f:\n            f.write(self._serialize(payload))\n\n    def forget(self, key: str) -> None:\n        \"\"\"\n        Remove an item from the cache.\n\n        :param key: The cache key\n        \"\"\"\n        path = self._path(key)\n        if path.exists():\n            path.unlink()\n\n    def flush(self) -> None:\n        \"\"\"\n        Clear the cache.\n        \"\"\"\n        shutil.rmtree(self.path)\n\n    def remember(\n        self, key: str, callback: T | Callable[[], T], minutes: int | None = None\n    ) -> T:\n        \"\"\"\n        Get an item from the cache, or use a default from callback.\n\n        :param key: The cache key\n        :param callback: Callback function providing default value\n        :param minutes: The lifetime in minutes of the cached value\n        \"\"\"\n        value = self.get(key)\n        if value is None:\n            value = callback() if callable(callback) else callback\n            self.put(key, value, minutes)\n        return value\n\n    def _get_payload(self, key: str) -> T | None:\n        path = self._path(key)\n\n        if not path.exists():\n            return None\n\n        with path.open(\"rb\") as f:\n            file_content = f.read()\n\n        try:\n            payload = self._deserialize(file_content)\n        except (json.JSONDecodeError, ValueError):\n            self.forget(key)\n            logger.warning(\"Corrupt cache file was detected and cleaned up.\")\n            return None\n\n        if payload.expired:\n            self.forget(key)\n            return None\n        else:\n            return payload.data\n\n    def _path(self, key: str) -> Path:\n        hash_type, parts_count = _HASHES[self.hash_type]\n        h = hash_type(encode(key)).hexdigest()\n        parts = [h[i : i + 2] for i in range(0, len(h), 2)][:parts_count]\n        return Path(self.path, *parts, h)\n\n    def _serialize(self, payload: CacheItem[T]) -> bytes:\n        expires = payload.expires or MAX_DATE\n        data = json.dumps(payload.data)\n        return encode(f\"{expires:010d}{data}\")\n\n    def _deserialize(self, data_raw: bytes) -> CacheItem[T]:\n        data_str = decode(data_raw)\n        data = json.loads(data_str[10:])\n        expires = int(data_str[:10])\n        return CacheItem(data, expires)\n\n\nclass ArtifactCache:\n    def __init__(self, *, cache_dir: Path) -> None:\n        self._cache_dir = cache_dir\n        self._archive_locks: defaultdict[Path, threading.Lock] = defaultdict(\n            threading.Lock\n        )\n\n    def get_cache_directory_for_link(self, link: Link) -> Path:\n        key_parts = {\"url\": link.url_without_fragment}\n\n        if hash_name := get_highest_priority_hash_type(link.hashes, link.filename):\n            key_parts[hash_name] = link.hashes[hash_name]\n\n        if link.subdirectory_fragment:\n            key_parts[\"subdirectory\"] = link.subdirectory_fragment\n\n        return self._get_directory_from_hash(key_parts)\n\n    def _get_directory_from_hash(self, key_parts: object) -> Path:\n        key = hashlib.sha256(\n            json.dumps(\n                key_parts, sort_keys=True, separators=(\",\", \":\"), ensure_ascii=True\n            ).encode(\"ascii\")\n        ).hexdigest()\n\n        split_key = [key[:2], key[2:4], key[4:6], key[6:]]\n        return self._cache_dir.joinpath(*split_key)\n\n    def get_cache_directory_for_git(\n        self, url: str, ref: str, subdirectory: str | None\n    ) -> Path:\n        key_parts = {\"url\": url, \"ref\": ref}\n        if subdirectory:\n            key_parts[\"subdirectory\"] = subdirectory\n\n        return self._get_directory_from_hash(key_parts)\n\n    @overload\n    def get_cached_archive_for_link(\n        self,\n        link: Link,\n        *,\n        strict: bool,\n        env: Env | None = ...,\n        download_func: Callable[[str, Path], None],\n    ) -> Path: ...\n\n    @overload\n    def get_cached_archive_for_link(\n        self,\n        link: Link,\n        *,\n        strict: bool,\n        env: Env | None = ...,\n        download_func: None = ...,\n    ) -> Path | None: ...\n\n    def get_cached_archive_for_link(\n        self,\n        link: Link,\n        *,\n        strict: bool,\n        env: Env | None = None,\n        download_func: Callable[[str, Path], None] | None = None,\n    ) -> Path | None:\n        cache_dir = self.get_cache_directory_for_link(link)\n\n        cached_archive = self._get_cached_archive(\n            cache_dir, strict=strict, filename=link.filename, env=env\n        )\n        if cached_archive is None and strict and download_func is not None:\n            cached_archive = cache_dir / link.filename\n            with self._archive_locks[cached_archive]:\n                # Check again if the archive exists (under the lock) to avoid\n                # duplicate downloads because it may have already been downloaded\n                # by another thread in the meantime\n                if not cached_archive.exists():\n                    cache_dir.mkdir(parents=True, exist_ok=True)\n                    try:\n                        download_func(link.url, cached_archive)\n                    except BaseException:\n                        cached_archive.unlink(missing_ok=True)\n                        raise\n\n        return cached_archive\n\n    def get_cached_archive_for_git(\n        self, url: str, reference: str, subdirectory: str | None, env: Env\n    ) -> Path | None:\n        cache_dir = self.get_cache_directory_for_git(url, reference, subdirectory)\n\n        return self._get_cached_archive(cache_dir, strict=False, env=env)\n\n    def _get_cached_archive(\n        self,\n        cache_dir: Path,\n        *,\n        strict: bool,\n        filename: str | None = None,\n        env: Env | None = None,\n    ) -> Path | None:\n        # implication \"not strict -> env must not be None\"\n        assert strict or env is not None\n        # implication \"strict -> filename must not be None\"\n        assert not strict or filename is not None\n\n        archives = self._get_cached_archives(cache_dir)\n        if not archives:\n            return None\n\n        candidates: list[tuple[float | None, Path]] = []\n\n        for archive in archives:\n            if strict:\n                # in strict mode return the original cached archive instead of the\n                # prioritized archive type.\n                if filename == archive.name:\n                    return archive\n                continue\n\n            assert env is not None\n\n            if archive.suffix != \".whl\":\n                candidates.append((float(\"inf\"), archive))\n                continue\n\n            try:\n                wheel = Wheel(archive.name)\n            except InvalidWheelNameError:\n                continue\n\n            if not wheel.is_supported_by_environment(env):\n                continue\n\n            candidates.append(\n                (wheel.get_minimum_supported_index(env.supported_tags), archive),\n            )\n\n        if not candidates:\n            return None\n\n        return min(candidates)[1]\n\n    def _get_cached_archives(self, cache_dir: Path) -> list[Path]:\n        archive_types = [\"whl\", \"tar.gz\", \"tar.bz2\", \"bz2\", \"zip\"]\n        paths: list[Path] = []\n        for archive_type in archive_types:\n            paths += cache_dir.glob(f\"*.{archive_type}\")\n\n        return paths\n"
  },
  {
    "path": "src/poetry/utils/constants.py",
    "content": "from __future__ import annotations\n\nimport os\n\n\n# Name of Poetry's own system project used by `poetry self` commands.\nPOETRY_SYSTEM_PROJECT_NAME = \"poetry-instance\"\n\n# Timeout for HTTP requests using the requests library.\nREQUESTS_TIMEOUT = int(os.getenv(\"POETRY_REQUESTS_TIMEOUT\", 15))\n\nRETRY_AFTER_HEADER = \"retry-after\"\n\n# Server response codes to retry requests on.\nSTATUS_FORCELIST = [429, 500, 501, 502, 503, 504]\n"
  },
  {
    "path": "src/poetry/utils/dependency_specification.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport re\nimport urllib.parse\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import TypeVar\nfrom typing import cast\n\nfrom poetry.core.packages.dependency import Dependency\nfrom tomlkit.items import InlineTable\n\nfrom poetry.packages.direct_origin import DirectOrigin\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.vcs_dependency import VCSDependency\n\n    from poetry.utils.cache import ArtifactCache\n    from poetry.utils.env import Env\n\n\nDependencySpec = dict[str, str | bool | dict[str, str | bool] | list[str]]\nBaseSpec = TypeVar(\"BaseSpec\", DependencySpec, InlineTable)\n\nGIT_URL_SCHEMES = {\"git+http\", \"git+https\", \"git+ssh\"}\n\n\ndef dependency_to_specification(\n    dependency: Dependency, specification: BaseSpec\n) -> BaseSpec:\n    if dependency.is_vcs():\n        dependency = cast(\"VCSDependency\", dependency)\n        assert dependency.source_url is not None\n        specification[dependency.vcs] = dependency.source_url\n        if dependency.reference:\n            specification[\"rev\"] = dependency.reference\n    elif dependency.is_file() or dependency.is_directory():\n        assert dependency.source_url is not None\n        specification[\"path\"] = dependency.source_url\n    elif dependency.is_url():\n        assert dependency.source_url is not None\n        specification[\"url\"] = dependency.source_url\n    elif dependency.pretty_constraint != \"*\" and not dependency.constraint.is_empty():\n        specification[\"version\"] = dependency.pretty_constraint\n\n    if not dependency.marker.is_any():\n        specification[\"markers\"] = str(dependency.marker)\n\n    if dependency.extras:\n        specification[\"extras\"] = sorted(dependency.extras)\n\n    return specification\n\n\nclass RequirementsParser:\n    def __init__(\n        self,\n        *,\n        artifact_cache: ArtifactCache,\n        env: Env | None = None,\n        cwd: Path | None = None,\n    ) -> None:\n        self._direct_origin = DirectOrigin(artifact_cache)\n        self._env = env\n        self._cwd = cwd or Path.cwd()\n\n    def parse(self, requirement: str) -> DependencySpec:\n        requirement = requirement.strip()\n\n        specification = self._parse_pep508(requirement)\n\n        if specification is not None:\n            return specification\n\n        extras = []\n        extras_m = re.search(r\"\\[([\\w\\d,-_ ]+)\\]$\", requirement)\n        if extras_m:\n            extras = [e.strip() for e in extras_m.group(1).split(\",\")]\n            requirement, _ = requirement.split(\"[\")\n\n        specification = (\n            self._parse_url(requirement)\n            or self._parse_path(requirement)\n            or self._parse_simple(requirement)\n        )\n\n        if specification:\n            if extras:\n                specification.setdefault(\"extras\", extras)\n            return specification\n\n        raise ValueError(f\"Invalid dependency specification: {requirement}\")\n\n    def _parse_pep508(self, requirement: str) -> DependencySpec | None:\n        if \" ; \" not in requirement and re.search(r\"@[\\^~!=<>\\d]\", requirement):\n            # this is of the form package@<semver>, do not attempt to parse it\n            return None\n\n        with contextlib.suppress(ValueError):\n            dependency = Dependency.create_from_pep_508(requirement)\n            specification: DependencySpec = {}\n            specification = dependency_to_specification(dependency, specification)\n\n            if specification:\n                specification[\"name\"] = dependency.name\n                return specification\n\n        return None\n\n    def _parse_git_url(self, requirement: str) -> DependencySpec | None:\n        from poetry.core.vcs.git import Git\n        from poetry.core.vcs.git import ParsedUrl\n\n        parsed = ParsedUrl.parse(requirement)\n        url = Git.normalize_url(requirement)\n\n        pair = {\"name\": parsed.name, \"git\": url.url}\n\n        if parsed.rev:\n            pair[\"rev\"] = url.revision\n\n        if parsed.subdirectory:\n            pair[\"subdirectory\"] = parsed.subdirectory\n\n        source_root = self._env.path.joinpath(\"src\") if self._env else None\n        package = self._direct_origin.get_package_from_vcs(\n            \"git\",\n            url=url.url,\n            rev=pair.get(\"rev\"),\n            subdirectory=parsed.subdirectory,\n            source_root=source_root,\n        )\n        pair[\"name\"] = package.name\n        return pair\n\n    def _parse_url(self, requirement: str) -> DependencySpec | None:\n        url_parsed = urllib.parse.urlparse(requirement)\n        if not (url_parsed.scheme and url_parsed.netloc):\n            return None\n\n        if url_parsed.scheme in GIT_URL_SCHEMES:\n            return self._parse_git_url(requirement)\n\n        if url_parsed.scheme in [\"http\", \"https\"]:\n            package = self._direct_origin.get_package_from_url(requirement)\n            assert package.source_url is not None\n            return {\"name\": package.name, \"url\": package.source_url}\n\n        return None\n\n    def _parse_path(self, requirement: str) -> DependencySpec | None:\n        if (os.path.sep in requirement or \"/\" in requirement) and (\n            self._cwd.joinpath(requirement).exists()\n            or (\n                Path(requirement).expanduser().exists()\n                and Path(requirement).expanduser().is_absolute()\n            )\n        ):\n            path = Path(requirement).expanduser()\n            is_absolute = path.is_absolute()\n\n            if not path.is_absolute():\n                path = self._cwd.joinpath(requirement)\n\n            if path.is_file():\n                package = self._direct_origin.get_package_from_file(path.resolve())\n            else:\n                package = self._direct_origin.get_package_from_directory(path.resolve())\n\n            return {\n                \"name\": package.name,\n                \"path\": (\n                    path.relative_to(self._cwd).as_posix()\n                    if not is_absolute\n                    else path.as_posix()\n                ),\n            }\n\n        return None\n\n    def _parse_simple(\n        self,\n        requirement: str,\n    ) -> DependencySpec | None:\n        extras: list[str] = []\n        pair = re.sub(\n            \"^([^@=: ]+)(?:@|==|(?<![<>~!])=|:| )(.*)$\", \"\\\\1 \\\\2\", requirement\n        )\n        pair = pair.strip()\n\n        require: DependencySpec = {}\n\n        if \" \" in pair:\n            name, version = pair.split(\" \", 1)\n            extras_m = re.search(r\"\\[([\\w\\d,-_]+)\\]$\", name)\n            if extras_m:\n                extras = [e.strip() for e in extras_m.group(1).split(\",\")]\n                name, _ = name.split(\"[\")\n\n            require[\"name\"] = name\n            if version != \"latest\":\n                require[\"version\"] = version\n        else:\n            m = re.match(\n                r\"^([^><=!: ]+)((?:>=|<=|>|<|!=|~=|~|\\^).*)$\", requirement.strip()\n            )\n            if m:\n                name, constraint = m.group(1), m.group(2)\n                extras_m = re.search(r\"\\[([\\w\\d,-_]+)\\]$\", name)\n                if extras_m:\n                    extras = [e.strip() for e in extras_m.group(1).split(\",\")]\n                    name, _ = name.split(\"[\")\n\n                require[\"name\"] = name\n                require[\"version\"] = constraint\n            else:\n                extras_m = re.search(r\"\\[([\\w\\d,-_]+)\\]$\", pair)\n                if extras_m:\n                    extras = [e.strip() for e in extras_m.group(1).split(\",\")]\n                    pair, _ = pair.split(\"[\")\n\n                require[\"name\"] = pair\n\n        if extras:\n            require[\"extras\"] = extras\n\n        return require\n"
  },
  {
    "path": "src/poetry/utils/env/__init__.py",
    "content": "from __future__ import annotations\n\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import TYPE_CHECKING\n\nfrom poetry.utils.env.base_env import Env\nfrom poetry.utils.env.env_manager import EnvManager\nfrom poetry.utils.env.exceptions import EnvCommandError\nfrom poetry.utils.env.exceptions import EnvError\nfrom poetry.utils.env.exceptions import IncorrectEnvError\nfrom poetry.utils.env.generic_env import GenericEnv\nfrom poetry.utils.env.mock_env import MockEnv\nfrom poetry.utils.env.null_env import NullEnv\nfrom poetry.utils.env.script_strings import GET_BASE_PREFIX\nfrom poetry.utils.env.script_strings import GET_ENV_PATH_ONELINER\nfrom poetry.utils.env.script_strings import GET_ENVIRONMENT_INFO\nfrom poetry.utils.env.script_strings import GET_PATHS\nfrom poetry.utils.env.script_strings import GET_PYTHON_VERSION_ONELINER\nfrom poetry.utils.env.script_strings import GET_SYS_PATH\nfrom poetry.utils.env.site_packages import SitePackages\nfrom poetry.utils.env.system_env import SystemEnv\nfrom poetry.utils.env.virtual_env import VirtualEnv\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n\n\n@contextmanager\ndef ephemeral_environment(\n    executable: Path | None = None,\n    flags: dict[str, str | bool] | None = None,\n) -> Iterator[VirtualEnv]:\n    with TemporaryDirectory(ignore_cleanup_errors=True) as tmp_dir:\n        # TODO: cache PEP 517 build environment corresponding to each project venv\n        venv_dir = Path(tmp_dir) / \".venv\"\n        EnvManager.build_venv(\n            path=venv_dir,\n            executable=executable,\n            flags=flags,\n        )\n        yield VirtualEnv(venv_dir, venv_dir)\n\n\n@contextmanager\ndef build_environment(\n    poetry: Poetry, env: Env | None = None, io: IO | None = None\n) -> Iterator[Env]:\n    \"\"\"\n    If a build script is specified for the project, there could be additional build\n    time dependencies, eg: cython, setuptools etc. In these cases, we create an\n    ephemeral build environment with all requirements specified under\n    `build-system.requires` and return this. Otherwise, the given default project\n    environment is returned.\n    \"\"\"\n    if not env or poetry.package.build_script:\n        with ephemeral_environment(\n            executable=env.python if env else None,\n            flags={\"no-pip\": True},\n        ) as venv:\n            if io:\n                requires = [\n                    f\"<c1>{requirement}</c1>\"\n                    for requirement in poetry.pyproject.build_system.requires\n                ]\n\n                io.write_error_line(\n                    \"<b>Preparing</b> build environment with build-system requirements\"\n                    f\" {', '.join(requires)}\"\n                )\n\n            from poetry.utils.isolated_build import IsolatedEnv\n\n            isolated_env = IsolatedEnv(venv, poetry.pool)\n            isolated_env.install(poetry.pyproject.build_system.requires)\n\n            yield venv\n    else:\n        yield env\n\n\n__all__ = [\n    \"GET_BASE_PREFIX\",\n    \"GET_ENVIRONMENT_INFO\",\n    \"GET_ENV_PATH_ONELINER\",\n    \"GET_PATHS\",\n    \"GET_PYTHON_VERSION_ONELINER\",\n    \"GET_SYS_PATH\",\n    \"Env\",\n    \"EnvCommandError\",\n    \"EnvError\",\n    \"EnvManager\",\n    \"GenericEnv\",\n    \"IncorrectEnvError\",\n    \"MockEnv\",\n    \"NullEnv\",\n    \"SitePackages\",\n    \"SystemEnv\",\n    \"VirtualEnv\",\n    \"build_environment\",\n    \"ephemeral_environment\",\n]\n"
  },
  {
    "path": "src/poetry/utils/env/base_env.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport re\nimport subprocess\nimport sys\nimport sysconfig\n\nfrom abc import ABC\nfrom abc import abstractmethod\nfrom functools import cached_property\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import TypedDict\n\nfrom installer.utils import SCHEME_NAMES\nfrom virtualenv.seed.wheels.embed import get_embed_wheel\n\nfrom poetry.utils.env.exceptions import EnvCommandError\nfrom poetry.utils.env.site_packages import SitePackages\nfrom poetry.utils.helpers import get_real_windows_path\nfrom poetry.utils.helpers import is_dir_writable\n\n\nif TYPE_CHECKING:\n    from packaging.tags import Tag\n    from poetry.core.version.markers import BaseMarker\n    from virtualenv.seed.wheels.util import Wheel\n\n    from poetry.utils.env.generic_env import GenericEnv\n\n    PythonVersion = tuple[int, int, int, str, int]\n\n\nclass MarkerEnv(TypedDict):\n    implementation_name: str\n    implementation_version: str\n    os_name: str\n    platform_machine: str\n    platform_release: str\n    platform_system: str\n    platform_version: str\n    python_full_version: str\n    platform_python_implementation: str\n    python_version: str\n    sys_platform: str\n    version_info: PythonVersion\n    interpreter_name: str\n    interpreter_version: str\n    sysconfig_platform: str\n    free_threading: bool\n\n\nclass Env(ABC):\n    \"\"\"\n    An abstract Python environment.\n    \"\"\"\n\n    def __init__(self, path: Path, base: Path | None = None) -> None:\n        self._is_windows = sys.platform == \"win32\"\n        self._is_mingw = sysconfig.get_platform().startswith(\"mingw\")\n        self._is_conda = bool(os.environ.get(\"CONDA_DEFAULT_ENV\"))\n\n        if self._is_windows:\n            path = get_real_windows_path(path)\n            base = get_real_windows_path(base) if base else None\n\n        bin_dir = \"bin\" if not self._is_windows or self._is_mingw else \"Scripts\"\n        self._path = path\n        self._bin_dir = self._path / bin_dir\n\n        self._executable = \"python\"\n        self._pip_executable = \"pip\"\n\n        self.find_executables()\n\n        self._base = base or path\n\n        self._site_packages: SitePackages | None = None\n        self._supported_tags: list[Tag] | None = None\n        self._purelib: Path | None = None\n        self._platlib: Path | None = None\n        self._script_dirs: list[Path] | None = None\n\n        self._embedded_pip_path: Path | None = None\n\n    @property\n    def bin_dir(self) -> Path:\n        return self._bin_dir\n\n    @property\n    def path(self) -> Path:\n        return self._path\n\n    @property\n    def base(self) -> Path:\n        return self._base\n\n    @property\n    def version_info(self) -> PythonVersion:\n        version_info: PythonVersion = self.marker_env[\"version_info\"]\n        return version_info\n\n    @property\n    def python_implementation(self) -> str:\n        implementation: str = self.marker_env[\"platform_python_implementation\"]\n        return implementation\n\n    @property\n    def python(self) -> Path:\n        \"\"\"\n        Path to current python executable\n        \"\"\"\n        return Path(self._bin(self._executable))\n\n    @cached_property\n    def marker_env(self) -> MarkerEnv:\n        return self.get_marker_env()\n\n    @property\n    def parent_env(self) -> GenericEnv:\n        from poetry.utils.env.generic_env import GenericEnv\n\n        return GenericEnv(self.base, child_env=self)\n\n    def _find_python_executable(self) -> None:\n        bin_dir = self._bin_dir\n\n        if self._is_windows and self._is_conda:\n            bin_dir = self._path\n\n        python_executables = sorted(\n            p.name\n            for p in bin_dir.glob(\"python*\")\n            if re.match(r\"python(?:\\d+(?:\\.\\d+)?)?(?:\\.exe)?$\", p.name)\n        )\n        if python_executables:\n            executable = python_executables[0]\n            if executable.endswith(\".exe\"):\n                executable = executable[:-4]\n\n            self._executable = executable\n\n    def _find_pip_executable(self) -> None:\n        pip_executables = sorted(\n            p.name\n            for p in self._bin_dir.glob(\"pip*\")\n            if re.match(r\"pip(?:\\d+(?:\\.\\d+)?)?(?:\\.exe)?$\", p.name)\n        )\n        if pip_executables:\n            pip_executable = pip_executables[0]\n            if pip_executable.endswith(\".exe\"):\n                pip_executable = pip_executable[:-4]\n\n            self._pip_executable = pip_executable\n\n    def find_executables(self) -> None:\n        self._find_python_executable()\n        self._find_pip_executable()\n\n    def get_embedded_wheel(self, distribution: str) -> Path:\n        wheel: Wheel = get_embed_wheel(\n            distribution, f\"{self.version_info[0]}.{self.version_info[1]}\"\n        )\n        path: Path = wheel.path\n        return path\n\n    @property\n    def pip_embedded(self) -> Path:\n        if self._embedded_pip_path is None:\n            self._embedded_pip_path = self.get_embedded_wheel(\"pip\") / \"pip\"\n        return self._embedded_pip_path\n\n    @property\n    def pip(self) -> Path:\n        \"\"\"\n        Path to current pip executable\n        \"\"\"\n        # we do not use as_posix() here due to issues with windows pathlib2\n        # implementation\n        path = Path(self._bin(self._pip_executable))\n        if not path.exists():\n            return self.pip_embedded\n        return path\n\n    @property\n    def platform(self) -> str:\n        return sys.platform\n\n    @property\n    def os(self) -> str:\n        return os.name\n\n    @property\n    def site_packages(self) -> SitePackages:\n        if self._site_packages is None:\n            self._site_packages = SitePackages(\n                self.purelib,\n                self.platlib,\n                self.fallbacks,\n            )\n        return self._site_packages\n\n    @property\n    def usersite(self) -> Path | None:\n        if \"usersite\" in self.paths:\n            return Path(self.paths[\"usersite\"])\n        return None\n\n    @property\n    def userbase(self) -> Path | None:\n        if \"userbase\" in self.paths:\n            return Path(self.paths[\"userbase\"])\n        return None\n\n    @property\n    def purelib(self) -> Path:\n        if self._purelib is None:\n            self._purelib = Path(self.paths[\"purelib\"])\n\n        return self._purelib\n\n    @property\n    def platlib(self) -> Path:\n        if self._platlib is None:\n            if \"platlib\" in self.paths:\n                self._platlib = Path(self.paths[\"platlib\"])\n            else:\n                self._platlib = self.purelib\n\n        return self._platlib\n\n    @cached_property\n    def fallbacks(self) -> list[Path]:\n        paths = [Path(path) for path in self.paths.get(\"fallbacks\", [])]\n        paths += [self.usersite] if self.usersite else []\n        return paths\n\n    def set_paths(\n        self,\n        purelib: str | Path | None = None,\n        platlib: str | Path | None = None,\n        userbase: str | Path | None = None,\n        usersite: str | Path | None = None,\n    ) -> None:\n        \"\"\"\n        A cached property aware way to set environment paths during runtime.\n\n        In some cases, like in `PluginManager._install()` method, paths are modified during execution. Direct\n        modification of `self.paths` is not safe as caches relying on are not invalidated. This helper method\n        ensures that we clear the relevant caches why paths are modified.\n        \"\"\"\n        if purelib:\n            self.paths[\"purelib\"] = str(purelib)\n\n        if platlib:\n            self.paths[\"platlib\"] = str(platlib)\n\n        if userbase:\n            self.paths[\"userbase\"] = str(userbase)\n\n        if usersite:\n            self.paths[\"usersite\"] = str(usersite)\n\n        # clear cached properties using the env paths\n        self.__dict__.pop(\"fallbacks\", None)\n        self.__dict__.pop(\"scheme_dict\", None)\n\n    @cached_property\n    def scheme_dict(self) -> dict[str, str]:\n        \"\"\"\n        This property exists to allow cases where system environment paths are not writable and\n        user site is enabled. This enables us to ensure packages (wheels) are correctly installed\n        into directories where the current user can write to.\n\n        If all candidates in `self.paths` is writable, no modification is made. If at least one path is not writable\n        and all generated writable candidates are indeed writable, these are used instead. If any candidate is not\n        writable, the original paths are returned.\n\n        Alternative writable candidates are generated by replacing discovered prefix, with \"userbase\"\n        if available. The original prefix is computed as the common path prefix of \"scripts\" and \"purelib\".\n        For example, given `{ \"purelib\": \"/usr/local/lib/python3.13/site-packages\", \"scripts\": \"/usr/local/bin\",\n        \"userbase\": \"/home/user/.local\" }`; the candidate \"purelib\" path would be\n        `/home/user/.local/lib/python3.13/site-packages`.\n        \"\"\"\n        paths = self.paths.copy()\n\n        if (\n            not self.is_venv()\n            and paths.get(\"userbase\")\n            and (\"scripts\" in paths and \"purelib\" in paths)\n        ):\n            overrides: dict[str, str] = {}\n\n            try:\n                base_path = os.path.commonpath([paths[\"scripts\"], paths[\"purelib\"]])\n            except ValueError:\n                return paths\n\n            scheme_names = [key for key in SCHEME_NAMES if key in self.paths]\n\n            for key in scheme_names:\n                if not is_dir_writable(path=Path(paths[key]), create=True):\n                    # there is at least one path that is not writable\n                    break\n            else:\n                # all paths are writable, return early\n                return paths\n\n            for key in scheme_names:\n                candidate = paths[key].replace(base_path, paths[\"userbase\"])\n                if not is_dir_writable(path=Path(candidate), create=True):\n                    # at least one candidate is not writable, we cannot do much here\n                    return paths\n\n                overrides[key] = candidate\n\n            paths.update(overrides)\n\n        return paths\n\n    def _get_lib_dirs(self) -> list[Path]:\n        return [self.purelib, self.platlib, *self.fallbacks]\n\n    def is_path_relative_to_lib(self, path: Path) -> bool:\n        for lib_path in self._get_lib_dirs():\n            with contextlib.suppress(ValueError):\n                path.relative_to(lib_path)\n                return True\n\n        return False\n\n    @property\n    @abstractmethod\n    def sys_path(self) -> list[str]: ...\n\n    @cached_property\n    def paths(self) -> dict[str, str]:\n        paths = self.get_paths()\n\n        if self.is_venv():\n            # We copy pip's logic here for the `include` path\n            paths[\"include\"] = str(\n                self.path.joinpath(\n                    \"include\",\n                    \"site\",\n                    f\"python{self.version_info[0]}.{self.version_info[1]}\",\n                )\n            )\n        return paths\n\n    @property\n    def supported_tags(self) -> list[Tag]:\n        if self._supported_tags is None:\n            self._supported_tags = self.get_supported_tags()\n\n        return self._supported_tags\n\n    @classmethod\n    def get_base_prefix(cls) -> Path:\n        real_prefix = getattr(sys, \"real_prefix\", None)\n        if real_prefix is not None:\n            return Path(real_prefix)\n\n        base_prefix = getattr(sys, \"base_prefix\", None)\n        if base_prefix is not None:\n            return Path(base_prefix)\n\n        return Path(sys.prefix)\n\n    @abstractmethod\n    def get_marker_env(self) -> MarkerEnv: ...\n\n    def get_pip_command(self, embedded: bool = False) -> list[str]:\n        if embedded or not Path(self._bin(self._pip_executable)).exists():\n            return [str(self.python), str(self.pip_embedded)]\n        # run as module so that pip can update itself on Windows\n        return [str(self.python), \"-m\", \"pip\"]\n\n    @abstractmethod\n    def get_supported_tags(self) -> list[Tag]: ...\n\n    @abstractmethod\n    def get_paths(self) -> dict[str, str]: ...\n\n    def is_valid_for_marker(self, marker: BaseMarker) -> bool:\n        valid: bool = marker.validate(self.marker_env)\n        return valid\n\n    def is_sane(self) -> bool:\n        \"\"\"\n        Checks whether the current environment is sane or not.\n        \"\"\"\n        return True\n\n    def get_command_from_bin(self, bin: str) -> list[str]:\n        if bin == \"pip\":\n            # when pip is required we need to ensure that we fall back to\n            # embedded pip when pip is not available in the environment\n            return self.get_pip_command()\n\n        return [self._bin(bin)]\n\n    def run(self, bin: str, *args: str, **kwargs: Any) -> str:\n        cmd = self.get_command_from_bin(bin) + list(args)\n        return self._run(cmd, **kwargs)\n\n    def run_pip(self, *args: str, **kwargs: Any) -> str:\n        pip = self.get_pip_command()\n        cmd = pip + list(args)\n        return self._run(cmd, **kwargs)\n\n    def run_python_script(self, content: str, **kwargs: Any) -> str:\n        # Options Used:\n        #     -I        : Run Python in isolated mode. (#6627)\n        #     -W ignore : Suppress warnings.\n        #\n        # TODO: Consider replacing (-I) with (-EP) once support for managing Python <3.11 environments dropped.\n        # This is useful to prevent user site being disabled over zealously.\n\n        return self.run(\n            self._executable,\n            \"-I\",\n            \"-W\",\n            \"ignore\",\n            \"-c\",\n            content,\n            stderr=subprocess.PIPE,\n            **kwargs,\n        )\n\n    def _run(self, cmd: list[str], **kwargs: Any) -> str:\n        \"\"\"\n        Run a command inside the Python environment.\n        \"\"\"\n        call = kwargs.pop(\"call\", False)\n        env = kwargs.pop(\"env\", dict(os.environ))\n        stderr = kwargs.pop(\"stderr\", subprocess.STDOUT)\n\n        try:\n            if call:\n                assert stderr != subprocess.PIPE\n                subprocess.check_call(cmd, stderr=stderr, env=env, **kwargs)\n                output = \"\"\n            else:\n                output = subprocess.check_output(\n                    cmd, stderr=stderr, env=env, text=True, encoding=\"locale\", **kwargs\n                )\n        except CalledProcessError as e:\n            raise EnvCommandError(e)\n\n        return output\n\n    def execute(self, bin: str, *args: str, **kwargs: Any) -> int:\n        command = self.get_command_from_bin(bin) + list(args)\n        env = kwargs.pop(\"env\", dict(os.environ))\n\n        if not self._is_windows:\n            return os.execvpe(command[0], command, env=env)\n\n        kwargs[\"shell\"] = True\n        exe = subprocess.Popen(command, env=env, **kwargs)\n        exe.communicate()\n        return exe.returncode\n\n    @abstractmethod\n    def is_venv(self) -> bool: ...\n\n    @property\n    def script_dirs(self) -> list[Path]:\n        if self._script_dirs is None:\n            scripts = self.paths.get(\"scripts\")\n            self._script_dirs = [\n                Path(scripts) if scripts is not None else self._bin_dir\n            ]\n            if self.userbase:\n                self._script_dirs.append(self.userbase / self._script_dirs[0].name)\n        return self._script_dirs\n\n    def _bin(self, bin: str) -> str:\n        \"\"\"\n        Return path to the given executable.\n        \"\"\"\n        if self._is_windows and not bin.endswith(\".exe\"):\n            bin_path = self._bin_dir / (bin + \".exe\")\n        else:\n            bin_path = self._bin_dir / bin\n\n        if not bin_path.exists():\n            # On Windows, some executables can be in the base path\n            # This is especially true when installing Python with\n            # the official installer, where python.exe will be at\n            # the root of the env path.\n            if self._is_windows:\n                if not bin.endswith(\".exe\"):\n                    bin_path = self._path / (bin + \".exe\")\n                else:\n                    bin_path = self._path / bin\n\n                if bin_path.exists():\n                    return str(bin_path)\n\n            return bin\n\n        return str(bin_path)\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Env):\n            return False\n\n        return other.__class__ == self.__class__ and other.path == self.path\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}(\"{self._path}\")'\n"
  },
  {
    "path": "src/poetry/utils/env/env_manager.py",
    "content": "from __future__ import annotations\n\nimport base64\nimport hashlib\nimport os\nimport plistlib\nimport re\nimport subprocess\nimport sys\n\nfrom functools import cached_property\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\n\nimport tomlkit\nimport virtualenv\n\nfrom cleo.io.null_io import NullIO\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.console.exceptions import PoetryConsoleError\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils._compat import WINDOWS\nfrom poetry.utils._compat import encode\nfrom poetry.utils.env.exceptions import EnvCommandError\nfrom poetry.utils.env.exceptions import IncorrectEnvError\nfrom poetry.utils.env.generic_env import GenericEnv\nfrom poetry.utils.env.python import Python\nfrom poetry.utils.env.python.exceptions import InvalidCurrentPythonVersionError\nfrom poetry.utils.env.python.exceptions import NoCompatiblePythonVersionFoundError\nfrom poetry.utils.env.python.exceptions import PythonVersionNotFoundError\nfrom poetry.utils.env.script_strings import GET_ENV_PATH_ONELINER\nfrom poetry.utils.env.script_strings import GET_PYTHON_VERSION_ONELINER\nfrom poetry.utils.env.system_env import SystemEnv\nfrom poetry.utils.env.virtual_env import VirtualEnv\nfrom poetry.utils.helpers import get_real_windows_path\nfrom poetry.utils.helpers import remove_directory\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env.base_env import Env\n\n\nclass EnvsFile(TOMLFile):\n    \"\"\"\n    This file contains one section per project with the project's base env name\n    as section name. Each section contains the minor and patch version of the\n    python executable used to create the currently active virtualenv.\n\n    Example:\n\n    [poetry-QRErDmmj]\n    minor = \"3.9\"\n    patch = \"3.9.13\"\n\n    [poetry-core-m5r7DkRA]\n    minor = \"3.11\"\n    patch = \"3.11.6\"\n    \"\"\"\n\n    def remove_section(self, name: str, minor: str | None = None) -> str | None:\n        \"\"\"\n        Remove a section from the envs file.\n\n        If \"minor\" is given, the section is only removed if its minor value\n        matches \"minor\".\n\n        Returns the \"minor\" value of the removed section.\n        \"\"\"\n        envs = self.read()\n        current_env = envs.get(name)\n        if current_env is not None and (not minor or current_env[\"minor\"] == minor):\n            del envs[name]\n            self.write(envs)\n            minor = current_env[\"minor\"]\n            assert isinstance(minor, str)\n            return minor\n\n        return None\n\n\nclass EnvManager:\n    \"\"\"\n    Environments manager\n    \"\"\"\n\n    _env = None\n\n    ENVS_FILE = \"envs.toml\"\n\n    def __init__(self, poetry: Poetry, io: None | IO = None) -> None:\n        self._poetry = poetry\n        self._io = io or NullIO()\n\n    @property\n    def in_project_venv(self) -> Path:\n        venv: Path = self._poetry.file.path.parent / \".venv\"\n        return venv\n\n    @cached_property\n    def envs_file(self) -> EnvsFile:\n        return EnvsFile(self._poetry.config.virtualenvs_path / self.ENVS_FILE)\n\n    @cached_property\n    def base_env_name(self) -> str:\n        return self.generate_env_name(\n            self._poetry.package.name,\n            str(self._poetry.file.path.parent),\n        )\n\n    def activate(self, python: str) -> Env:\n        venv_path = self._poetry.config.virtualenvs_path\n\n        python_instance = Python.get_by_name(python)\n        if python_instance is None:\n            raise PythonVersionNotFoundError(python)\n\n        create = False\n        # If we are required to create the virtual environment in the project directory,\n        # create or recreate it if needed\n        if self.use_in_project_venv():\n            create = False\n            venv = self.in_project_venv\n            if venv.exists():\n                # We need to check if the patch version is correct\n                _venv = VirtualEnv(venv)\n                current_patch = \".\".join(str(v) for v in _venv.version_info[:3])\n\n                if python_instance.patch_version.to_string() != current_patch:\n                    create = True\n\n            self.create_venv(python=python_instance, force=create)\n\n            return self.get(reload=True)\n\n        envs = tomlkit.document()\n        if self.envs_file.exists():\n            envs = self.envs_file.read()\n            current_env = envs.get(self.base_env_name)\n            if current_env is not None:\n                current_minor = current_env[\"minor\"]\n                current_patch = current_env[\"patch\"]\n\n                if (\n                    current_minor == python_instance.minor_version.to_string()\n                    and current_patch != python_instance.patch_version.to_string()\n                ):\n                    # We need to recreate\n                    create = True\n\n        venv = (\n            venv_path\n            / f\"{self.base_env_name}-py{python_instance.minor_version.to_string()}\"\n        )\n\n        # Create if needed\n        if not venv.exists() or create:\n            in_venv = os.environ.get(\"VIRTUAL_ENV\") is not None\n            if in_venv or not venv.exists():\n                create = True\n\n            if venv.exists():\n                # We need to check if the patch version is correct\n                _venv = VirtualEnv(venv)\n                current_patch = \".\".join(str(v) for v in _venv.version_info[:3])\n\n                if python_instance.patch_version.to_string() != current_patch:\n                    create = True\n\n            self.create_venv(python=python_instance, force=create)\n\n        # Activate\n        envs[self.base_env_name] = {\n            \"minor\": python_instance.minor_version.to_string(),\n            \"patch\": python_instance.patch_version.to_string(),\n        }\n        self.envs_file.write(envs)\n\n        return self.get(reload=True)\n\n    def deactivate(self) -> None:\n        venv_path = self._poetry.config.virtualenvs_path\n\n        if self.envs_file.exists() and (\n            minor := self.envs_file.remove_section(self.base_env_name)\n        ):\n            venv = venv_path / f\"{self.base_env_name}-py{minor}\"\n            self._io.write_error_line(\n                f\"Deactivating virtualenv: <comment>{venv}</comment>\"\n            )\n\n    def get(self, reload: bool = False) -> Env:\n        if self._env is not None and not reload:\n            return self._env\n\n        python_minor: str | None = None\n\n        env = None\n        envs = None\n        if self.envs_file.exists():\n            envs = self.envs_file.read()\n            env = envs.get(self.base_env_name)\n            if env:\n                python_minor = env[\"minor\"]\n\n        # Check if we are inside a virtualenv or not\n        # Conda sets CONDA_PREFIX in its envs, see\n        # https://github.com/conda/conda/issues/2764\n        env_prefix = os.environ.get(\"VIRTUAL_ENV\", os.environ.get(\"CONDA_PREFIX\"))\n        conda_env_name = os.environ.get(\"CONDA_DEFAULT_ENV\")\n        # It's probably not a good idea to pollute Conda's global \"base\" env, since\n        # most users have it activated all the time.\n        in_venv = env_prefix is not None and conda_env_name != \"base\"\n\n        if not in_venv or env is not None:\n            # Checking if a local virtualenv exists\n            if self.in_project_venv_exists():\n                venv = self.in_project_venv\n\n                return VirtualEnv(venv)\n\n            create_venv = self._poetry.config.get(\"virtualenvs.create\", True)\n\n            if not create_venv:\n                return self.get_system_env()\n\n            venv_path = self._poetry.config.virtualenvs_path\n\n            if python_minor is None:\n                # we only need to discover python version in this case\n                python = Python.get_preferred_python(\n                    config=self._poetry.config, io=self._io\n                )\n                python_minor = python.minor_version.to_string()\n\n            name = f\"{self.base_env_name}-py{python_minor.strip()}\"\n\n            venv = venv_path / name\n\n            if not venv.exists():\n                if env and envs:\n                    del envs[self.base_env_name]\n                    self.envs_file.write(envs)\n                return self.get_system_env()\n\n            return VirtualEnv(venv)\n\n        if env_prefix is not None:\n            prefix = Path(env_prefix)\n            base_prefix = None\n        else:\n            prefix = Path(sys.prefix)\n            base_prefix = self.get_base_prefix()\n\n        return VirtualEnv(prefix, base_prefix)\n\n    def list(self, name: str | None = None) -> list[VirtualEnv]:\n        if name is None:\n            name = self._poetry.package.name\n\n        venv_name = self.generate_env_name(name, str(self._poetry.file.path.parent))\n        venv_path = self._poetry.config.virtualenvs_path\n        env_list = [VirtualEnv(p) for p in sorted(venv_path.glob(f\"{venv_name}-py*\"))]\n\n        if self.in_project_venv_exists():\n            venv = self.in_project_venv\n            env_list.insert(0, VirtualEnv(venv))\n        return env_list\n\n    @staticmethod\n    def check_env_is_for_current_project(env: str, base_env_name: str) -> bool:\n        \"\"\"\n        Check if env name starts with projects name.\n\n        This is done to prevent action on other project's envs.\n        \"\"\"\n        return env.startswith(base_env_name)\n\n    def remove(self, python: str) -> Env:\n        python_path = Path(python)\n        if python_path.is_file():\n            # Validate env name if provided env is a full path to python\n            try:\n                env_dir = subprocess.check_output(\n                    [python, \"-c\", GET_ENV_PATH_ONELINER], text=True, encoding=\"locale\"\n                ).strip(\"\\n\")\n                env_name = Path(env_dir).name\n                if not self.check_env_is_for_current_project(\n                    env_name, self.base_env_name\n                ):\n                    raise IncorrectEnvError(env_name)\n            except CalledProcessError as e:\n                raise EnvCommandError(e)\n\n        if self.check_env_is_for_current_project(python, self.base_env_name):\n            venvs = self.list()\n            for venv in venvs:\n                if venv.path.name == python:\n                    # Exact virtualenv name\n                    if self.envs_file.exists():\n                        venv_minor = \".\".join(str(v) for v in venv.version_info[:2])\n                        self.envs_file.remove_section(self.base_env_name, venv_minor)\n\n                    self.remove_venv(venv.path)\n\n                    return venv\n\n            raise ValueError(\n                f'<warning>Environment \"{python}\" does not exist.</warning>'\n            )\n        else:\n            venv_path = self._poetry.config.virtualenvs_path\n            # Get all the poetry envs, even for other projects\n            env_names = [p.name for p in sorted(venv_path.glob(\"*-*-py*\"))]\n            if python in env_names:\n                raise IncorrectEnvError(python)\n\n        try:\n            python_version = Version.parse(python)\n            python = f\"python{python_version.major}\"\n            if python_version.precision > 1:\n                python += f\".{python_version.minor}\"\n        except ValueError:\n            # Executable in PATH or full executable path\n            pass\n\n        try:\n            python_version_string = subprocess.check_output(\n                [python, \"-c\", GET_PYTHON_VERSION_ONELINER],\n                text=True,\n                encoding=\"locale\",\n            )\n        except CalledProcessError as e:\n            raise EnvCommandError(e)\n\n        python_version = Version.parse(python_version_string.strip())\n        minor = f\"{python_version.major}.{python_version.minor}\"\n\n        name = f\"{self.base_env_name}-py{minor}\"\n        venv_path = venv_path / name\n\n        if not venv_path.exists():\n            raise ValueError(f'<warning>Environment \"{name}\" does not exist.</warning>')\n\n        if self.envs_file.exists():\n            self.envs_file.remove_section(self.base_env_name, minor)\n\n        self.remove_venv(venv_path)\n\n        return VirtualEnv(venv_path, venv_path)\n\n    def use_in_project_venv(self) -> bool:\n        in_project: bool | None = self._poetry.config.get(\"virtualenvs.in-project\")\n        if in_project is not None:\n            return in_project\n\n        return self.in_project_venv.is_dir()\n\n    def in_project_venv_exists(self) -> bool:\n        in_project: bool | None = self._poetry.config.get(\"virtualenvs.in-project\")\n        if in_project is False:\n            return False\n\n        return self.in_project_venv.is_dir()\n\n    def create_venv(\n        self,\n        name: str | None = None,\n        python: Python | None = None,\n        force: bool = False,\n    ) -> Env:\n        if self._env is not None and not force:\n            return self._env\n\n        cwd = self._poetry.file.path.parent\n        env = self.get(reload=True)\n\n        if not env.is_sane():\n            force = True\n\n        if env.is_venv() and not force:\n            # Already inside a virtualenv.\n            current_python = Version.parse(\n                \".\".join(str(c) for c in env.version_info[:3])\n            )\n            if not self._poetry.package.python_constraint.allows(current_python):\n                raise InvalidCurrentPythonVersionError(\n                    self._poetry.package.python_versions, str(current_python)\n                )\n            return env\n\n        create_venv = self._poetry.config.get(\"virtualenvs.create\")\n        in_project_venv = self.use_in_project_venv()\n        venv_prompt = self._poetry.config.get(\"virtualenvs.prompt\")\n\n        specific_python_requested = python is not None\n        if not python:\n            python = Python.get_preferred_python(\n                config=self._poetry.config, io=self._io\n            )\n\n        venv_path = (\n            self.in_project_venv\n            if in_project_venv\n            else self._poetry.config.virtualenvs_path\n        )\n        if not name:\n            name = self._poetry.package.name\n\n        supported_python = self._poetry.package.python_constraint\n        if not supported_python.allows(python.patch_version):\n            # The currently activated or chosen Python version\n            # is not compatible with the Python constraint specified\n            # for the project.\n            # If an executable has been specified, we stop there\n            # and notify the user of the incompatibility.\n            # Otherwise, we try to find a compatible Python version.\n            if specific_python_requested:\n                raise NoCompatiblePythonVersionFoundError(\n                    self._poetry.package.python_versions,\n                    python.patch_version.to_string(),\n                )\n\n            self._io.write_error_line(\n                f\"<warning>The currently activated Python version {python.patch_version.to_string()} is not\"\n                f\" supported by the project ({self._poetry.package.python_versions}).\\n\"\n                \"Trying to find and use a compatible version.</warning> \"\n            )\n\n            python = Python.get_compatible_python(poetry=self._poetry, io=self._io)\n\n        if in_project_venv:\n            venv = venv_path\n        else:\n            name = self.generate_env_name(name, str(cwd))\n            name = f\"{name}-py{python.minor_version.to_string()}\"\n            venv = venv_path / name\n\n        if venv_prompt is not None:\n            try:\n                venv_prompt = venv_prompt.format(\n                    project_name=self._poetry.package.name or \"virtualenv\",\n                    python_version=python.minor_version.to_string(),\n                )\n            except KeyError as e:\n                raise PoetryConsoleError(\n                    f\"Invalid template variable '{e.args[0]}' in 'virtualenvs.prompt' setting.\\n\"\n                    f\"Valid variables are: {{project_name}}, {{python_version}}\"\n                ) from e\n            except ValueError as e:\n                raise PoetryConsoleError(\n                    f\"Invalid template string in 'virtualenvs.prompt' setting: {e}\"\n                ) from e\n\n        if not venv.exists():\n            if create_venv is False:\n                self._io.write_error_line(\n                    \"<fg=black;bg=yellow>\"\n                    \"Skipping virtualenv creation, \"\n                    \"as specified in config file.\"\n                    \"</>\"\n                )\n\n                return self.get_system_env()\n\n            self._io.write_error_line(\n                f\"Creating virtualenv <c1>{name}</> in\"\n                f\" {venv_path if not WINDOWS else get_real_windows_path(venv_path)!s}\"\n            )\n        else:\n            create_venv = False\n            if force:\n                if not env.is_sane():\n                    self._io.write_error_line(\n                        f\"<warning>The virtual environment found in {env.path} seems to\"\n                        \" be broken.</warning>\"\n                    )\n                self._io.write_error_line(\n                    f\"Recreating virtualenv <c1>{name}</> in {venv!s}\"\n                )\n                self.remove_venv(venv)\n                create_venv = True\n            elif self._io.is_very_verbose():\n                self._io.write_error_line(f\"Virtualenv <c1>{name}</> already exists.\")\n\n        if create_venv:\n            self.build_venv(\n                venv,\n                executable=python.executable,\n                flags=self._poetry.config.get(\"virtualenvs.options\"),\n                prompt=venv_prompt,\n            )\n\n        # venv detection:\n        # stdlib venv may symlink sys.executable, so we can't use realpath.\n        # but others can symlink *to* the venv Python,\n        # so we can't just use sys.executable.\n        # So we just check every item in the symlink tree (generally <= 3)\n        p = os.path.normcase(sys.executable)\n        paths = [p]\n        while os.path.islink(p):\n            p = os.path.normcase(os.path.join(os.path.dirname(p), os.readlink(p)))\n            paths.append(p)\n\n        p_venv = os.path.normcase(str(venv))\n        if any(p.startswith(p_venv) for p in paths):\n            # Running properly in the virtualenv, don't need to do anything\n            return self.get_system_env()\n\n        return VirtualEnv(venv)\n\n    @classmethod\n    def build_venv(\n        cls,\n        path: Path,\n        executable: Path | None = None,\n        flags: dict[str, str | bool] | None = None,\n        with_pip: bool | None = None,\n        prompt: str | None = None,\n    ) -> virtualenv.run.session.Session:\n        flags = flags or {}\n\n        if with_pip is not None:\n            flags[\"no-pip\"] = not with_pip\n\n        flags.setdefault(\"no-pip\", True)\n        flags.setdefault(\"no-setuptools\", True)\n        flags.setdefault(\"no-wheel\", True)\n\n        if WINDOWS:\n            path = get_real_windows_path(path)\n            executable = get_real_windows_path(executable) if executable else None\n\n        executable_str = None if executable is None else executable.resolve().as_posix()\n\n        args = [\n            \"--no-download\",\n            \"--no-periodic-update\",\n            \"--python\",\n            executable_str or sys.executable,\n        ]\n\n        if prompt is not None:\n            args.extend([\"--prompt\", prompt])\n\n        for flag, value in flags.items():\n            if value is True:\n                args.append(f\"--{flag}\")\n\n            elif value is not False:\n                args.append(f\"--{flag}={value}\")\n\n        args.append(str(path))\n\n        cli_result = virtualenv.cli_run(args, setup_logging=False)\n\n        # Exclude the venv folder from from macOS Time Machine backups\n        # TODO: Add backup-ignore markers for other platforms too\n        if sys.platform == \"darwin\":\n            import xattr\n\n            xattr.setxattr(\n                str(path),\n                \"com.apple.metadata:com_apple_backup_excludeItem\",\n                plistlib.dumps(\"com.apple.backupd\", fmt=plistlib.FMT_BINARY),\n            )\n\n        return cli_result\n\n    @classmethod\n    def remove_venv(cls, path: Path) -> None:\n        assert path.is_dir()\n        try:\n            remove_directory(path)\n            return\n        except OSError as e:\n            # Continue only if e.errno == 16\n            if e.errno != 16:  # ERRNO 16: Device or resource busy\n                raise e\n\n        # Delete all files and folders but the toplevel one. This is because sometimes\n        # the venv folder is mounted by the OS, such as in a docker volume. In such\n        # cases, an attempt to delete the folder itself will result in an `OSError`.\n        # See https://github.com/python-poetry/poetry/pull/2064\n        for file_path in path.iterdir():\n            if file_path.is_file() or file_path.is_symlink():\n                file_path.unlink()\n            elif file_path.is_dir():\n                remove_directory(file_path, force=True)\n\n    @classmethod\n    def get_system_env(cls, naive: bool = False) -> Env:\n        \"\"\"\n        Retrieve the current Python environment.\n\n        This can be the base Python environment or an activated virtual environment.\n\n        This method also workaround the issue that the virtual environment\n        used by Poetry internally (when installed via the custom installer)\n        is incorrectly detected as the system environment. Note that this workaround\n        happens only when `naive` is False since there are times where we actually\n        want to retrieve Poetry's custom virtual environment\n        (e.g. plugin installation or self update).\n        \"\"\"\n        prefix, base_prefix = Path(sys.prefix), Path(cls.get_base_prefix())\n        env: Env = SystemEnv(prefix)\n        if not naive:\n            env = GenericEnv(base_prefix, child_env=env)\n\n        return env\n\n    @classmethod\n    def get_base_prefix(cls) -> Path:\n        real_prefix = getattr(sys, \"real_prefix\", None)\n        if real_prefix is not None:\n            return Path(real_prefix)\n\n        base_prefix = getattr(sys, \"base_prefix\", None)\n        if base_prefix is not None:\n            return Path(base_prefix)\n\n        return Path(sys.prefix)\n\n    @classmethod\n    def generate_env_name(cls, name: str, cwd: str) -> str:\n        name = name.lower()\n        sanitized_name = re.sub(r'[ $`!*@\"\\\\\\r\\n\\t]', \"_\", name)[:42]\n        normalized_cwd = os.path.normcase(os.path.realpath(cwd))\n        h_bytes = hashlib.sha256(encode(normalized_cwd)).digest()\n        h_str = base64.urlsafe_b64encode(h_bytes).decode()[:8]\n\n        return f\"{sanitized_name}-{h_str}\"\n"
  },
  {
    "path": "src/poetry/utils/env/exceptions.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.utils._compat import decode\n\n\nif TYPE_CHECKING:\n    from subprocess import CalledProcessError\n\n\nclass EnvError(Exception):\n    pass\n\n\nclass IncorrectEnvError(EnvError):\n    def __init__(self, env_name: str) -> None:\n        message = f\"Env {env_name} doesn't belong to this project.\"\n        super().__init__(message)\n\n\nclass EnvCommandError(EnvError):\n    def __init__(self, e: CalledProcessError) -> None:\n        self.e = e\n\n        message_parts = [\n            f\"Command {e.cmd} errored with the following return code {e.returncode}\"\n        ]\n        if e.output:\n            message_parts.append(f\"Output:\\n{decode(e.output)}\")\n        if e.stderr:\n            message_parts.append(f\"Error output:\\n{decode(e.stderr)}\")\n        super().__init__(\"\\n\\n\".join(message_parts))\n"
  },
  {
    "path": "src/poetry/utils/env/generic_env.py",
    "content": "from __future__ import annotations\n\nimport json\nimport os\nimport re\nimport subprocess\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.utils._compat import WINDOWS\nfrom poetry.utils.env.script_strings import GET_PATHS\nfrom poetry.utils.env.virtual_env import VirtualEnv\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from poetry.utils.env.base_env import Env\n\n\nclass GenericEnv(VirtualEnv):\n    def __init__(\n        self, path: Path, base: Path | None = None, child_env: Env | None = None\n    ) -> None:\n        self._child_env = child_env\n\n        super().__init__(path, base=base)\n\n    def find_executables(self) -> None:\n        patterns = [(\"python*\", \"pip*\")]\n\n        if self._child_env:\n            minor_version = (\n                f\"{self._child_env.version_info[0]}.{self._child_env.version_info[1]}\"\n            )\n            major_version = f\"{self._child_env.version_info[0]}\"\n            patterns = [\n                (f\"python{minor_version}\", f\"pip{minor_version}\"),\n                (f\"python{major_version}\", f\"pip{major_version}\"),\n            ]\n\n        if WINDOWS:\n            patterns = [(f\"{p[0]}.exe\", f\"{p[1]}.exe\") for p in patterns]\n\n        python_executable = None\n        pip_executable = None\n\n        for python_pattern, pip_pattern in patterns:\n            if python_executable and pip_executable:\n                break\n\n            if not python_executable:\n                python_executables = sorted(\n                    p.name\n                    for p in self._bin_dir.glob(python_pattern)\n                    if re.match(r\"python(?:\\d+(?:\\.\\d+)?)?(?:\\.exe)?$\", p.name)\n                )\n\n                if python_executables:\n                    executable = python_executables[0]\n                    if executable.endswith(\".exe\"):\n                        executable = executable[:-4]\n\n                    python_executable = executable\n\n            if not pip_executable:\n                pip_executables = sorted(\n                    p.name\n                    for p in self._bin_dir.glob(pip_pattern)\n                    if re.match(r\"pip(?:\\d+(?:\\.\\d+)?)?(?:\\.exe)?$\", p.name)\n                )\n                if pip_executables:\n                    pip_executable = pip_executables[0]\n                    if pip_executable.endswith(\".exe\"):\n                        pip_executable = pip_executable[:-4]\n\n            if python_executable:\n                self._executable = python_executable\n\n            if pip_executable:\n                self._pip_executable = pip_executable\n\n    def get_paths(self) -> dict[str, str]:\n        output = self.run_python_script(GET_PATHS)\n\n        paths: dict[str, str] = json.loads(output)\n        return paths\n\n    def execute(self, bin: str, *args: str, **kwargs: Any) -> int:\n        command = self.get_command_from_bin(bin) + list(args)\n        env = kwargs.pop(\"env\", dict(os.environ))\n\n        if not self._is_windows:\n            return os.execvpe(command[0], command, env=env)\n\n        exe = subprocess.Popen(command, env=env, **kwargs)\n        exe.communicate()\n\n        return exe.returncode\n\n    def _run(self, cmd: list[str], **kwargs: Any) -> str:\n        return super(VirtualEnv, self)._run(cmd, **kwargs)\n\n    def is_venv(self) -> bool:\n        return self._path != self._base\n"
  },
  {
    "path": "src/poetry/utils/env/mock_env.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.utils.env.null_env import NullEnv\n\n\nif TYPE_CHECKING:\n    from packaging.tags import Tag\n\n    from poetry.utils.env.base_env import MarkerEnv\n    from poetry.utils.env.base_env import PythonVersion\n\n\nclass MockEnv(NullEnv):\n    def __init__(\n        self,\n        version_info: tuple[int, int, int] | PythonVersion = (3, 7, 0),\n        *,\n        python_implementation: str = \"CPython\",\n        platform: str = \"darwin\",\n        platform_machine: str = \"amd64\",\n        os_name: str = \"posix\",\n        is_venv: bool = False,\n        sys_path: list[str] | None = None,\n        marker_env: dict[str, Any] | None = None,\n        supported_tags: list[Tag] | None = None,\n        **kwargs: Any,\n    ) -> None:\n        super().__init__(**kwargs)\n\n        if len(version_info) == 3:\n            version_info = (*version_info, \"final\", 0)\n        self._version_info = version_info\n        self._python_implementation = python_implementation\n        self._platform = platform\n        self._platform_machine = platform_machine\n        self._os_name = os_name\n        self._is_venv = is_venv\n        self._sys_path = sys_path\n        self._mock_marker_env = marker_env\n        self._supported_tags = supported_tags\n\n    @property\n    def platform(self) -> str:\n        return self._platform\n\n    @property\n    def platform_machine(self) -> str:\n        return self._platform_machine\n\n    @property\n    def os(self) -> str:\n        return self._os_name\n\n    @property\n    def sys_path(self) -> list[str]:\n        if self._sys_path is None:\n            return super().sys_path\n\n        return self._sys_path\n\n    def get_marker_env(self) -> MarkerEnv:\n        marker_env = super().get_marker_env()\n        marker_env[\"version_info\"] = self._version_info\n        marker_env[\"python_version\"] = \".\".join(str(v) for v in self._version_info[:2])\n        marker_env[\"python_full_version\"] = \".\".join(\n            str(v) for v in self._version_info[:3]\n        )\n        marker_env[\"sys_platform\"] = self._platform\n        marker_env[\"platform_machine\"] = self._platform_machine\n        marker_env[\"interpreter_name\"] = self._python_implementation.lower()\n        marker_env[\"interpreter_version\"] = \"cp\" + \"\".join(\n            str(v) for v in self._version_info[:2]\n        )\n\n        if self._mock_marker_env is not None:\n            for key, value in self._mock_marker_env.items():\n                marker_env[key] = value  # type: ignore[literal-required]\n\n        return marker_env\n\n    def is_venv(self) -> bool:\n        return self._is_venv\n"
  },
  {
    "path": "src/poetry/utils/env/null_env.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom functools import cached_property\nfrom pathlib import Path\nfrom typing import Any\n\nfrom poetry.utils.env.system_env import SystemEnv\n\n\nclass NullEnv(SystemEnv):\n    def __init__(\n        self, path: Path | None = None, base: Path | None = None, execute: bool = False\n    ) -> None:\n        if path is None:\n            path = Path(sys.prefix)\n\n        super().__init__(path, base=base)\n\n        self._execute = execute\n        self.executed: list[list[str]] = []\n\n    @cached_property\n    def paths(self) -> dict[str, str]:\n        paths = self.get_paths()\n        paths[\"platlib\"] = str(self._path / \"platlib\")\n        paths[\"purelib\"] = str(self._path / \"purelib\")\n        paths[\"scripts\"] = str(self._path / \"scripts\")\n        paths[\"data\"] = str(self._path / \"data\")\n        return paths\n\n    def _run(self, cmd: list[str], **kwargs: Any) -> str:\n        self.executed.append(cmd)\n\n        if self._execute:\n            return super()._run(cmd, **kwargs)\n        return \"\"\n\n    def execute(self, bin: str, *args: str, **kwargs: Any) -> int:\n        self.executed.append([bin, *list(args)])\n\n        if self._execute:\n            return super().execute(bin, *args, **kwargs)\n        return 0\n\n    def _bin(self, bin: str) -> str:\n        return bin\n"
  },
  {
    "path": "src/poetry/utils/env/python/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.utils.env.python.manager import Python\n\n\n__all__ = [\"Python\"]\n"
  },
  {
    "path": "src/poetry/utils/env/python/exceptions.py",
    "content": "from __future__ import annotations\n\n\nclass PythonVersionError(Exception):\n    pass\n\n\nclass PythonVersionNotFoundError(PythonVersionError):\n    def __init__(self, expected: str) -> None:\n        super().__init__(f\"Could not find the python executable {expected}\")\n\n\nclass NoCompatiblePythonVersionFoundError(PythonVersionError):\n    def __init__(self, expected: str, given: str | None = None) -> None:\n        if given:\n            message = (\n                f\"The specified Python version ({given}) \"\n                f\"is not supported by the project ({expected}).\\n\"\n                \"Please choose a compatible version \"\n                \"or loosen the python constraint specified \"\n                \"in the pyproject.toml file.\"\n            )\n        else:\n            message = (\n                \"Poetry was unable to find a compatible version. \"\n                \"If you have one, you can explicitly use it \"\n                'via the \"env use\" command.'\n            )\n\n        super().__init__(message)\n\n\nclass InvalidCurrentPythonVersionError(PythonVersionError):\n    def __init__(self, expected: str, given: str) -> None:\n        message = (\n            f\"Current Python version ({given}) \"\n            f\"is not allowed by the project ({expected}).\\n\"\n            'Please change python executable via the \"env use\" command.'\n        )\n\n        super().__init__(message)\n"
  },
  {
    "path": "src/poetry/utils/env/python/installer.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\n\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom typing import Literal\n\nimport pbs_installer as pbi\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.config.config import Config\nfrom poetry.console.exceptions import ConsoleMessage\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.env.python import Python\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\nBAD_PYTHON_INSTALL_INFO = [\n    \"This could happen because you are missing platform dependencies required.\",\n    \"Please refer to https://gregoryszorc.com/docs/python-build-standalone/main/running.html#runtime-requirements \"\n    \"for more information about the necessary requirements.\",\n    \"Please remove the failing Python installation using <c1>poetry python remove <version></> before continuing.\",\n]\n\n\nclass PythonInstallerError(Exception):\n    pass\n\n\nclass PythonDownloadNotFoundError(PythonInstallerError, ValueError):\n    pass\n\n\nclass PythonInstallationError(PythonInstallerError, ValueError):\n    pass\n\n\n@dataclasses.dataclass(frozen=True)\nclass PythonInstaller:\n    request: str\n    implementation: Literal[\"cpython\", \"pypy\"] = dataclasses.field(default=\"cpython\")\n    free_threaded: bool = dataclasses.field(default=False)\n    installation_directory: Path = dataclasses.field(\n        init=False, default_factory=lambda: Config.create().python_installation_dir\n    )\n\n    @property\n    def version(self) -> Version:\n        try:\n            pyver, _ = pbi.get_download_link(\n                self.request,\n                implementation=self.implementation,\n                free_threaded=self.free_threaded,\n            )\n            return Version.from_parts(\n                major=pyver.major, minor=pyver.minor, patch=pyver.micro\n            )\n        except ValueError:\n            raise PythonDownloadNotFoundError(\n                \"No suitable standalone build found for the requested Python version.\"\n            )\n\n    def exists(self) -> bool:\n        version = self.version\n        bad_executables = set()\n\n        for python in Python.find_poetry_managed_pythons():\n            try:\n                if python.implementation.lower() != self.implementation:\n                    continue\n                if python.free_threaded != self.free_threaded:\n                    continue\n\n                if version == python.version:\n                    return True\n            except CalledProcessError:\n                bad_executables.add(python.executable)\n\n        if bad_executables:\n            raise PoetryRuntimeError(\n                reason=\"One or more installed version do not work on your system. This is not a Poetry issue.\",\n                messages=[\n                    ConsoleMessage(\"\\n\".join(e.as_posix() for e in bad_executables))\n                    .indent(\"  - \")\n                    .make_section(\"Failing Executables\")\n                    .wrap(\"info\"),\n                    *[\n                        ConsoleMessage(m).wrap(\"warning\")\n                        for m in BAD_PYTHON_INSTALL_INFO\n                    ],\n                ],\n            )\n\n        return False\n\n    def install(self) -> None:\n        try:\n            # this can be broken into download, and install_file if required to make\n            # use of Poetry's own mechanics for download and unpack\n            pbi.install(\n                self.request,\n                self.installation_directory,\n                version_dir=True,\n                implementation=self.implementation,\n                free_threaded=self.free_threaded,\n            )\n        except ValueError:\n            raise PythonInstallationError(\n                \"Failed to download and install requested version of Python.\"\n            )\n"
  },
  {
    "path": "src/poetry/utils/env/python/manager.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport sys\n\nfrom functools import cached_property\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import NamedTuple\nfrom typing import cast\nfrom typing import overload\n\nimport findpython\nimport packaging.version\n\nfrom cleo.io.null_io import NullIO\nfrom cleo.io.outputs.output import Verbosity\nfrom pbs_installer._install import THIS_ARCH\nfrom pbs_installer._install import THIS_PLATFORM\nfrom pbs_installer._versions import PYTHON_VERSIONS\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import VersionConstraint\nfrom poetry.core.constraints.version import parse_constraint\n\nfrom poetry.utils.env.python.exceptions import NoCompatiblePythonVersionFoundError\nfrom poetry.utils.env.python.providers import PoetryPythonPathProvider\nfrom poetry.utils.env.python.providers import ShutilWhichPythonProvider\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from cleo.io.io import IO\n\n    from poetry.config.config import Config\n    from poetry.poetry import Poetry\n\n# register default providers\nfindpython.register_provider(PoetryPythonPathProvider)\n\n\nclass PythonInfo(NamedTuple):\n    major: int\n    minor: int\n    patch: int\n    implementation: str\n    free_threaded: bool\n    executable: Path | None\n\n\nclass Python:\n    @overload\n    def __init__(self, *, python: findpython.PythonVersion) -> None: ...\n\n    @overload\n    def __init__(\n        self, executable: str | Path, version: Version | None = None\n    ) -> None: ...\n\n    # we overload __init__ to ensure we do not break any downstream plugins\n    # that use the this\n    def __init__(\n        self,\n        executable: str | Path | None = None,\n        version: Version | None = None,\n        python: findpython.PythonVersion | None = None,\n    ) -> None:\n        if python and (executable or version):\n            raise ValueError(\n                \"When python is provided, neither executable or version must be specified\"\n            )\n\n        if python:\n            self._python = python\n        elif executable:\n            self._python = findpython.PythonVersion(\n                executable=Path(executable),\n                _version=packaging.version.Version(str(version)) if version else None,\n            )\n        else:\n            raise ValueError(\"Either python or executable must be provided\")\n\n    @classmethod\n    def find_all(cls) -> Iterator[Python]:\n        venv_path: Path | None = (\n            Path(os.environ[\"VIRTUAL_ENV\"]) if \"VIRTUAL_ENV\" in os.environ else None\n        )\n        for python in findpython.find_all():\n            if venv_path and python.executable.is_relative_to(venv_path):\n                continue\n            yield cls(python=python)\n\n    @classmethod\n    def find_poetry_managed_pythons(cls) -> Iterator[Python]:\n        finder = findpython.Finder(\n            selected_providers=[PoetryPythonPathProvider.name()],\n        )\n        for python in finder.find_all():\n            yield cls(python=python)\n\n    @classmethod\n    def find_all_versions(\n        cls,\n        constraint: VersionConstraint | str | None = None,\n        implementation: str | None = None,\n        free_threaded: bool | None = None,\n    ) -> Iterator[PythonInfo]:\n        if isinstance(constraint, str):\n            constraint = parse_constraint(constraint)\n        constraint = constraint or parse_constraint(\"*\")\n        if implementation:\n            implementation = implementation.lower()\n\n        seen = set()\n        for python in cls.find_all():\n            if (\n                python.executable in seen\n                or not constraint.allows(python.version)\n                or (implementation and python.implementation.lower() != implementation)\n                or (\n                    free_threaded is not None\n                    and python.free_threaded is not free_threaded\n                )\n            ):\n                continue\n\n            seen.add(python.executable)\n            yield PythonInfo(\n                major=python.major,\n                minor=python.minor,\n                patch=python.patch,\n                implementation=python.implementation.lower(),\n                free_threaded=python.free_threaded,\n                executable=python.executable,\n            )\n\n    @classmethod\n    def find_downloadable_versions(\n        cls,\n        constraint: VersionConstraint | str | None = None,\n        *,\n        include_incompatible: bool = False,\n    ) -> Iterator[PythonInfo]:\n        if isinstance(constraint, str):\n            constraint = parse_constraint(constraint)\n        constraint = constraint or parse_constraint(\"*\")\n\n        for pv in PYTHON_VERSIONS:\n            for _ in {\n                k[1]\n                for k in PYTHON_VERSIONS[pv]\n                if include_incompatible or (k[0], k[1]) == (THIS_PLATFORM, THIS_ARCH)\n            }:\n                if not constraint.allows(\n                    Version.from_parts(pv.major, pv.minor, pv.micro)\n                ):\n                    continue\n\n                yield PythonInfo(\n                    major=pv.major,\n                    minor=pv.minor,\n                    patch=pv.micro,\n                    implementation=pv.implementation.lower(),\n                    free_threaded=pv.freethreaded,\n                    executable=None,\n                )\n\n    @property\n    def python(self) -> findpython.PythonVersion:\n        return self._python\n\n    @property\n    def name(self) -> str:\n        return cast(\"str\", self._python.name)\n\n    @property\n    def executable(self) -> Path:\n        return cast(\"Path\", self._python.interpreter)\n\n    @property\n    def implementation(self) -> str:\n        return cast(\"str\", self._python.implementation.lower())\n\n    @property\n    def free_threaded(self) -> bool:\n        return cast(\"bool\", self._python.freethreaded)\n\n    @property\n    def major(self) -> int:\n        return cast(\"int\", self._python.major)\n\n    @property\n    def minor(self) -> int:\n        return cast(\"int\", self._python.minor)\n\n    @property\n    def patch(self) -> int:\n        return cast(\"int\", self._python.patch)\n\n    @property\n    def version(self) -> Version:\n        return Version.parse(str(self._python.version))\n\n    @cached_property\n    def patch_version(self) -> Version:\n        return Version.from_parts(\n            major=self.version.major,\n            minor=self.version.minor,\n            patch=self.version.patch,\n        )\n\n    @cached_property\n    def minor_version(self) -> Version:\n        return Version.from_parts(major=self.version.major, minor=self.version.minor)\n\n    @classmethod\n    def get_active_python(cls) -> Python | None:\n        \"\"\"\n        Fetches the active Python interpreter from available system paths or falls\n        back to finding the first valid Python executable named \"python\".\n\n        An \"active Python interpreter\" in this context is an executable (or a symlink)\n        with the name `python`. This is done so to detect cases where pyenv or\n        alternatives are used.\n\n        This method first uses the `ShutilWhichPythonProvider` to detect Python\n        executables in the path. If no interpreter is found using, it attempts\n        to locate a Python binary named \"python\" via the `findpython` library.\n\n        :return: An instance representing the detected active Python,\n                 or None if no valid environment is found.\n        \"\"\"\n        for python in ShutilWhichPythonProvider().find_pythons():\n            return cls(python=python)\n\n        # fallback to findpython, restrict to finding only executables\n        # named \"python\" as the intention here is just that, nothing more\n        if python := findpython.find(\"python\"):\n            return cls(python=python)\n\n        return None\n\n    @classmethod\n    def get_system_python(cls) -> Python:\n        \"\"\"\n        Creates and returns an instance of the class representing the Poetry's Python executable.\n        \"\"\"\n        return cls(\n            python=findpython.PythonVersion(\n                executable=Path(sys.executable),\n                _version=packaging.version.Version(\n                    \".\".join(str(v) for v in sys.version_info[:3])\n                ),\n            )\n        )\n\n    @classmethod\n    def get_by_name(cls, python_name: str) -> Python | None:\n        # Ignore broken installations.\n        with contextlib.suppress(ValueError):\n            if python := ShutilWhichPythonProvider.find_python_by_name(python_name):\n                return cls(python=python)\n\n        if python := findpython.find(python_name):\n            return cls(python=python)\n\n        return None\n\n    @classmethod\n    def get_preferred_python(cls, config: Config, io: IO | None = None) -> Python:\n        \"\"\"\n        Determine and return the \"preferred\" Python interpreter based on the provided\n        configuration and optional input/output stream.\n\n        This method first attempts to get the active Python interpreter if the configuration\n        does not mandate using Poetry's Python. If an active interpreter is found, it is returned.\n        Otherwise, the method defaults to retrieving the Poetry's Python interpreter (System Python).\n\n        This method **does not** attempt to sort versions or determine Python version constraint\n        compatibility.\n        \"\"\"\n        io = io or NullIO()\n\n        if not config.get(\"virtualenvs.use-poetry-python\") and (\n            active_python := Python.get_active_python()\n        ):\n            io.write_error_line(\n                f\"Found: {active_python.executable}\", verbosity=Verbosity.VERBOSE\n            )\n            return active_python\n\n        return cls.get_system_python()\n\n    @classmethod\n    def get_compatible_python(cls, poetry: Poetry, io: IO | None = None) -> Python:\n        \"\"\"\n        Retrieve a compatible Python version based on the given poetry configuration\n        and Python constraints derived from the project.\n\n        This method iterates through all available Python candidates and checks if any\n        match the supported Python constraint as defined in the specified poetry package.\n\n        :param poetry: The poetry configuration containing package information,\n                       including Python constraints.\n        :param io: The input/output stream for error and status messages. Defaults\n                   to a null I/O if not provided.\n        :return: A Python instance representing a compatible Python version.\n        :raises NoCompatiblePythonVersionFoundError: If no Python version matches\n                the supported constraint.\n        \"\"\"\n        io = io or NullIO()\n        supported_python = poetry.package.python_constraint\n\n        for python in cls.find_all():\n            if python.version.allows_any(supported_python):\n                io.write_error_line(\n                    f\"Using <c1>{python.name}</c1> ({python.patch_version})\"\n                )\n                return python\n\n        raise NoCompatiblePythonVersionFoundError(poetry.package.python_versions)\n"
  },
  {
    "path": "src/poetry/utils/env/python/providers.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport shutil\nimport sysconfig\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport findpython\n\nfrom findpython.providers.path import PathProvider\n\nfrom poetry.config.config import Config\nfrom poetry.utils._compat import WINDOWS\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from poetry.core.constraints.version import Version\n    from typing_extensions import Self\n\n\nclass ShutilWhichPythonProvider(findpython.BaseProvider):  # type: ignore[misc]\n    @classmethod\n    def create(cls) -> Self | None:\n        return cls()\n\n    def find_pythons(self) -> Iterable[findpython.PythonVersion]:\n        if python := self.find_python_by_name(\"python\"):\n            return [python]\n        return []\n\n    @classmethod\n    def find_python_by_name(cls, name: str) -> findpython.PythonVersion | None:\n        if path := shutil.which(name):\n            return findpython.PythonVersion(executable=Path(path))\n        return None\n\n\n@dataclasses.dataclass\nclass PoetryPythonPathProvider(PathProvider):  # type: ignore[misc]\n    @classmethod\n    def installation_dir(\n        cls, version: Version, implementation: str, free_threaded: bool\n    ) -> Path:\n        dir_name = f\"{implementation}@{version}\"\n        if free_threaded:\n            dir_name += \"t\"\n        return Config.create().python_installation_dir / dir_name\n\n    @classmethod\n    def _make_bin_paths(cls, base: Path | None = None) -> list[Path]:\n        # Attention:\n        # There are two versions of pbs builds,\n        # - one like a normal Python installation and\n        # - one with an additional level of folders where the expected files\n        #   are in an \"install\" directory.\n        # If both versions exist, the first one is preferred.\n        # However, sometimes (especially for free-threaded Python),\n        # only the second version exists!\n        install_dir = base or Config.create().python_installation_dir\n        if WINDOWS and not sysconfig.get_platform().startswith(\"mingw\"):\n            # On Windows Python executables are top level.\n            # (Only in virtualenvs, they are in the Scripts directory.)\n            # A python-build-standalone PyPy has no Scripts directory!\n            if base:\n                if not base.is_dir():\n                    return []\n                if (install_dir := base / \"install\").is_dir():\n                    return [install_dir]\n                return [base]\n            return [\n                *(\n                    pi if (pi := p / \"install\").exists() else p\n                    for p in Path.glob(install_dir, \"*\")\n                    if p.is_dir()\n                ),\n            ]\n        return list(Path.glob(install_dir, \"**/bin\"))\n\n    @classmethod\n    def installation_bin_paths(\n        cls, version: Version, implementation: str, free_threaded: bool = False\n    ) -> list[Path]:\n        return cls._make_bin_paths(\n            cls.installation_dir(version, implementation, free_threaded)\n        )\n\n    @classmethod\n    def create(cls) -> Self | None:\n        return cls(cls._make_bin_paths())\n"
  },
  {
    "path": "src/poetry/utils/env/script_strings.py",
    "content": "from __future__ import annotations\n\nimport packaging.tags\n\n\nGET_PLATFORMS = f\"\"\"\nimport importlib.util\nimport json\nimport sys\n\nfrom pathlib import Path\n\nspec = importlib.util.spec_from_file_location(\n    \"packaging\", Path(r\"{packaging.__file__}\")\n)\npackaging = importlib.util.module_from_spec(spec)\nsys.modules[spec.name] = packaging\n\nspec = importlib.util.spec_from_file_location(\n    \"packaging.tags\", Path(r\"{packaging.tags.__file__}\")\n)\npackaging_tags = importlib.util.module_from_spec(spec)\nspec.loader.exec_module(packaging_tags)\n\nprint(json.dumps(list(packaging_tags.platform_tags())))\n\"\"\"\n\nGET_ENVIRONMENT_INFO = \"\"\"\\\nimport json\nimport os\nimport platform\nimport sys\nimport sysconfig\n\nINTERPRETER_SHORT_NAMES = {\n    \"python\": \"py\",\n    \"cpython\": \"cp\",\n    \"pypy\": \"pp\",\n    \"ironpython\": \"ip\",\n    \"jython\": \"jy\",\n}\n\n\ndef interpreter_version():\n    version = sysconfig.get_config_var(\"interpreter_version\")\n    if version:\n        version = str(version)\n    else:\n        version = _version_nodot(sys.version_info[:2])\n\n    return version\n\n\ndef _version_nodot(version):\n    return \"\".join(map(str, version))\n\n\nif hasattr(sys, \"implementation\"):\n    info = sys.implementation.version\n    iver = \"{0.major}.{0.minor}.{0.micro}\".format(info)\n    kind = info.releaselevel\n    if kind != \"final\":\n        iver += kind[0] + str(info.serial)\n\n    implementation_name = sys.implementation.name\nelse:\n    iver = \"0\"\n    implementation_name = platform.python_implementation().lower()\n\nenv = {\n    \"implementation_name\": implementation_name,\n    \"implementation_version\": iver,\n    \"os_name\": os.name,\n    \"platform_machine\": platform.machine(),\n    \"platform_release\": platform.release(),\n    \"platform_system\": platform.system(),\n    \"platform_version\": platform.version(),\n    \"python_full_version\": platform.python_version().rstrip(\"+\"),\n    \"platform_python_implementation\": platform.python_implementation(),\n    \"python_version\": \".\".join(platform.python_version_tuple()[:2]),\n    \"sys_platform\": sys.platform,\n    \"version_info\": tuple(sys.version_info),\n    # Extra information\n    \"interpreter_name\": INTERPRETER_SHORT_NAMES.get(\n        implementation_name, implementation_name\n    ),\n    \"interpreter_version\": interpreter_version(),\n    \"sysconfig_platform\": sysconfig.get_platform(),\n    \"free_threading\": bool(sysconfig.get_config_var(\"Py_GIL_DISABLED\")),\n}\n\nprint(json.dumps(env))\n\"\"\"\n\nGET_BASE_PREFIX = \"\"\"\\\nimport sys\n\nif hasattr(sys, \"real_prefix\"):\n    print(sys.real_prefix)\nelif hasattr(sys, \"base_prefix\"):\n    print(sys.base_prefix)\nelse:\n    print(sys.prefix)\n\"\"\"\n\nGET_PYTHON_VERSION_ONELINER = (\n    \"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"\n)\n\nGET_ENV_PATH_ONELINER = \"import sys; print(sys.prefix)\"\n\nGET_SYS_PATH = \"\"\"\\\nimport json\nimport sys\n\nprint(json.dumps(sys.path))\n\"\"\"\n\nGET_PATHS = \"\"\"\\\nimport json\nimport site\nimport sysconfig\n\npaths = sysconfig.get_paths().copy()\n\npaths[\"fallbacks\"] = [\n    p for p in site.getsitepackages()\n    if p and p not in {paths.get(\"purelib\"), paths.get(\"platlib\")}\n]\n\nif site.check_enableusersite():\n    paths[\"usersite\"] = site.getusersitepackages()\n\npaths[\"userbase\"] = site.getuserbase()\n\nprint(json.dumps(paths))\n\"\"\"\n"
  },
  {
    "path": "src/poetry/utils/env/site_packages.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport itertools\n\nfrom importlib import metadata\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Literal\nfrom typing import overload\n\nfrom poetry.utils.helpers import is_dir_writable\nfrom poetry.utils.helpers import paths_csv\nfrom poetry.utils.helpers import remove_directory\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n\nclass SitePackages:\n    def __init__(\n        self,\n        purelib: Path,\n        platlib: Path | None = None,\n        fallbacks: list[Path] | None = None,\n    ) -> None:\n        self._purelib = purelib\n        self._platlib = platlib or purelib\n\n        if platlib and platlib.resolve() == purelib.resolve():\n            self._platlib = purelib\n\n        self._fallbacks = fallbacks or []\n\n        self._candidates: list[Path] = []\n        for path in itertools.chain([self._purelib, self._platlib], self._fallbacks):\n            if path not in self._candidates:\n                self._candidates.append(path)\n\n        self._writable_candidates: list[Path] | None = None\n\n    @property\n    def path(self) -> Path:\n        return self._purelib\n\n    @property\n    def purelib(self) -> Path:\n        return self._purelib\n\n    @property\n    def platlib(self) -> Path:\n        return self._platlib\n\n    @property\n    def candidates(self) -> list[Path]:\n        return self._candidates\n\n    @property\n    def writable_candidates(self) -> list[Path]:\n        if self._writable_candidates is not None:\n            return self._writable_candidates\n\n        self._writable_candidates = []\n        for candidate in self._candidates:\n            if not is_dir_writable(path=candidate, create=True):\n                continue\n            self._writable_candidates.append(candidate)\n\n        return self._writable_candidates\n\n    def make_candidates(\n        self, path: Path, writable_only: bool = False, strict: bool = False\n    ) -> list[Path]:\n        candidates = self._candidates if not writable_only else self.writable_candidates\n        if path.is_absolute():\n            for candidate in candidates:\n                with contextlib.suppress(ValueError):\n                    path.relative_to(candidate)\n                    return [path]\n            site_type = \"writable \" if writable_only else \"\"\n            raise ValueError(\n                f\"{path} is not relative to any discovered {site_type}sites\"\n            )\n\n        results = [candidate / path for candidate in candidates]\n\n        if not results and strict:\n            raise RuntimeError(\n                f'Unable to find a suitable destination for \"{path}\" in'\n                f\" {paths_csv(self._candidates)}\"\n            )\n\n        return results\n\n    def distributions(\n        self, name: str | None = None, writable_only: bool = False\n    ) -> Iterable[metadata.Distribution]:\n        path = list(\n            map(\n                str, self._candidates if not writable_only else self.writable_candidates\n            )\n        )\n\n        yield from metadata.PathDistribution.discover(name=name, path=path)\n\n    def find_distribution(\n        self, name: str, writable_only: bool = False\n    ) -> metadata.Distribution | None:\n        for distribution in self.distributions(name=name, writable_only=writable_only):\n            return distribution\n        return None\n\n    def find_distribution_files_with_name(\n        self, distribution_name: str, name: str, writable_only: bool = False\n    ) -> Iterable[Path]:\n        for distribution in self.distributions(\n            name=distribution_name, writable_only=writable_only\n        ):\n            files = [] if distribution.files is None else distribution.files\n            for file in files:\n                if file.name == name:\n                    path = distribution.locate_file(file)\n                    assert isinstance(path, Path)\n                    yield path\n\n    def find_distribution_direct_url_json_files(\n        self, distribution_name: str, writable_only: bool = False\n    ) -> Iterable[Path]:\n        return self.find_distribution_files_with_name(\n            distribution_name=distribution_name,\n            name=\"direct_url.json\",\n            writable_only=writable_only,\n        )\n\n    def remove_distribution_files(self, distribution_name: str) -> list[Path]:\n        paths = []\n\n        for distribution in self.distributions(\n            name=distribution_name, writable_only=True\n        ):\n            files = [] if distribution.files is None else distribution.files\n            for file in files:\n                path = distribution.locate_file(file)\n                assert isinstance(path, Path)\n                path.unlink(missing_ok=True)\n\n            distribution_path: Path = distribution._path  # type: ignore[attr-defined]\n            if distribution_path.exists():\n                remove_directory(distribution_path, force=True)\n\n            paths.append(distribution_path)\n\n        return paths\n\n    @overload\n    def _path_method_wrapper(\n        self,\n        path: Path,\n        method: str,\n        *args: Any,\n        return_first: Literal[False],\n        writable_only: bool = False,\n        **kwargs: Any,\n    ) -> list[tuple[Path, Any]]: ...\n\n    @overload\n    def _path_method_wrapper(\n        self,\n        path: Path,\n        method: str,\n        *args: Any,\n        return_first: bool = True,\n        writable_only: bool = False,\n        **kwargs: Any,\n    ) -> tuple[Path, Any]: ...\n\n    def _path_method_wrapper(\n        self,\n        path: Path,\n        method: str,\n        *args: Any,\n        return_first: bool = True,\n        writable_only: bool = False,\n        **kwargs: Any,\n    ) -> tuple[Path, Any] | list[tuple[Path, Any]]:\n        candidates = self.make_candidates(\n            path, writable_only=writable_only, strict=True\n        )\n\n        results = []\n\n        for candidate in candidates:\n            with contextlib.suppress(OSError):\n                result = candidate, getattr(candidate, method)(*args, **kwargs)\n                if return_first:\n                    return result\n                results.append(result)\n\n        if results:\n            return results\n\n        raise OSError(f\"Unable to access any of {paths_csv(candidates)}\")\n\n    def write_text(self, path: Path, *args: Any, **kwargs: Any) -> Path:\n        paths: tuple[Path, Any] = self._path_method_wrapper(\n            path, \"write_text\", *args, **kwargs\n        )\n        return paths[0]\n\n    def mkdir(self, path: Path, *args: Any, **kwargs: Any) -> Path:\n        paths: tuple[Path, Any] = self._path_method_wrapper(\n            path, \"mkdir\", *args, **kwargs\n        )\n        return paths[0]\n\n    def exists(self, path: Path) -> bool:\n        return any(\n            value[-1]\n            for value in self._path_method_wrapper(path, \"exists\", return_first=False)\n        )\n\n    def find(\n        self,\n        path: Path,\n        writable_only: bool = False,\n    ) -> list[Path]:\n        return [\n            value[0]\n            for value in self._path_method_wrapper(\n                path, \"exists\", return_first=False, writable_only=writable_only\n            )\n            if value[-1] is True\n        ]\n"
  },
  {
    "path": "src/poetry/utils/env/system_env.py",
    "content": "from __future__ import annotations\n\nimport os\nimport platform\nimport site\nimport sys\nimport sysconfig\n\nfrom pathlib import Path\n\nfrom packaging.tags import Tag\nfrom packaging.tags import interpreter_name\nfrom packaging.tags import interpreter_version\nfrom packaging.tags import sys_tags\n\nfrom poetry.utils.env.base_env import Env\nfrom poetry.utils.env.base_env import MarkerEnv\n\n\nclass SystemEnv(Env):\n    \"\"\"\n    A system (i.e. not a virtualenv) Python environment.\n    \"\"\"\n\n    @property\n    def python(self) -> Path:\n        return Path(sys.executable)\n\n    @property\n    def sys_path(self) -> list[str]:\n        return sys.path\n\n    def get_paths(self) -> dict[str, str]:\n        import site\n\n        paths = sysconfig.get_paths().copy()\n\n        if site.check_enableusersite():\n            paths[\"usersite\"] = site.getusersitepackages()\n            paths[\"userbase\"] = site.getuserbase()\n\n        return paths\n\n    def get_supported_tags(self) -> list[Tag]:\n        return list(sys_tags())\n\n    def get_marker_env(self) -> MarkerEnv:\n        if hasattr(sys, \"implementation\"):\n            info = sys.implementation.version\n            iver = f\"{info.major}.{info.minor}.{info.micro}\"\n            kind = info.releaselevel\n            if kind != \"final\":\n                iver += kind[0] + str(info.serial)\n\n            implementation_name = sys.implementation.name\n        else:\n            iver = \"0\"\n            implementation_name = \"\"\n\n        return {\n            \"implementation_name\": implementation_name,\n            \"implementation_version\": iver,\n            \"os_name\": os.name,\n            \"platform_machine\": platform.machine(),\n            \"platform_release\": platform.release(),\n            \"platform_system\": platform.system(),\n            \"platform_version\": platform.version(),\n            # Workaround for https://github.com/python/cpython/issues/99968\n            \"python_full_version\": platform.python_version().rstrip(\"+\"),\n            \"platform_python_implementation\": platform.python_implementation(),\n            \"python_version\": \".\".join(platform.python_version().split(\".\")[:2]),\n            \"sys_platform\": sys.platform,\n            \"version_info\": sys.version_info,\n            \"interpreter_name\": interpreter_name(),\n            \"interpreter_version\": interpreter_version(),\n            \"sysconfig_platform\": sysconfig.get_platform(),\n            \"free_threading\": bool(sysconfig.get_config_var(\"Py_GIL_DISABLED\")),\n        }\n\n    def is_venv(self) -> bool:\n        return self._path != self._base\n\n    def _get_lib_dirs(self) -> list[Path]:\n        return super()._get_lib_dirs() + [Path(d) for d in site.getsitepackages()]\n"
  },
  {
    "path": "src/poetry/utils/env/virtual_env.py",
    "content": "from __future__ import annotations\n\nimport json\nimport os\nimport re\nimport sysconfig\n\nfrom contextlib import contextmanager\nfrom copy import deepcopy\nfrom functools import cached_property\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.utils.env.base_env import Env\nfrom poetry.utils.env.base_env import MarkerEnv\nfrom poetry.utils.env.script_strings import GET_BASE_PREFIX\nfrom poetry.utils.env.script_strings import GET_ENVIRONMENT_INFO\nfrom poetry.utils.env.script_strings import GET_PATHS\nfrom poetry.utils.env.script_strings import GET_PLATFORMS\nfrom poetry.utils.env.script_strings import GET_SYS_PATH\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from packaging.tags import Tag\n\n\nclass VirtualEnv(Env):\n    \"\"\"\n    A virtual Python environment.\n    \"\"\"\n\n    def __init__(self, path: Path, base: Path | None = None) -> None:\n        super().__init__(path, base)\n\n        # If base is None, it probably means this is\n        # a virtualenv created from VIRTUAL_ENV.\n        # In this case we need to get sys.base_prefix\n        # from inside the virtualenv.\n        if base is None:\n            output = self.run_python_script(GET_BASE_PREFIX)\n            self._base = Path(output.strip())\n\n    @property\n    def sys_path(self) -> list[str]:\n        output = self.run_python_script(GET_SYS_PATH)\n        paths: list[str] = json.loads(output)\n        return paths\n\n    def get_supported_tags(self) -> list[Tag]:\n        from packaging.tags import compatible_tags\n        from packaging.tags import cpython_tags\n        from packaging.tags import generic_tags\n\n        python = self.version_info[:3]\n        interpreter_name = self.marker_env[\"interpreter_name\"]\n        interpreter_version = self.marker_env[\"interpreter_version\"]\n        sysconfig_platform = self.marker_env[\"sysconfig_platform\"]\n        free_threading = self.marker_env[\"free_threading\"]\n\n        abis: list[str] | None = None\n        if interpreter_name == \"pp\":\n            interpreter = \"pp3\"\n        elif interpreter_name == \"cp\":\n            interpreter = f\"{interpreter_name}{interpreter_version}\"\n            if free_threading:\n                abis = [f\"{interpreter}t\"]\n        else:\n            interpreter = None\n\n        # Why using sysconfig.get_platform() and not ...\n        # ... platform.machine()\n        #  This one is also different for x86_64 Linux and aarch64 Linux,\n        #  but it is the same for a 32 Bit and a 64 Bit Python on Windows!\n        # ... platform.architecture()\n        #  This one is also different for a 32 Bit and a 64 Bit Python on Windows,\n        #  but it is the same for x86_64 Linux and aarch64 Linux!\n        platforms = None\n        if sysconfig_platform != sysconfig.get_platform():\n            # Relevant for the following use cases, for example:\n            # - using a 32 Bit Python on a 64 Bit Windows\n            # - using an emulated aarch Python on an x86_64 Linux\n            output = self.run_python_script(GET_PLATFORMS)\n            platforms = json.loads(output)\n\n        return [\n            *(\n                cpython_tags(python, abis=abis, platforms=platforms)\n                if interpreter_name == \"cp\"\n                else generic_tags(platforms=platforms)\n            ),\n            *compatible_tags(python, interpreter=interpreter, platforms=platforms),\n        ]\n\n    def get_marker_env(self) -> MarkerEnv:\n        output = self.run_python_script(GET_ENVIRONMENT_INFO)\n\n        env: MarkerEnv = json.loads(output)\n        # Lists and tuples are the same in JSON and loaded as list.\n        env[\"version_info\"] = tuple(env[\"version_info\"])  # type: ignore[typeddict-item]\n        return env\n\n    def get_paths(self) -> dict[str, str]:\n        output = self.run_python_script(GET_PATHS)\n        paths: dict[str, str] = json.loads(output)\n        return paths\n\n    def is_venv(self) -> bool:\n        return True\n\n    def is_sane(self) -> bool:\n        # A virtualenv is considered sane if \"python\" exists.\n        return os.path.exists(self.python)\n\n    def _run(self, cmd: list[str], **kwargs: Any) -> str:\n        kwargs[\"env\"] = self.get_temp_environ(environ=kwargs.get(\"env\"))\n        return super()._run(cmd, **kwargs)\n\n    def get_temp_environ(\n        self,\n        environ: dict[str, str] | None = None,\n        exclude: list[str] | None = None,\n        **kwargs: str,\n    ) -> dict[str, str]:\n        exclude = exclude or []\n        exclude.extend([\"PYTHONHOME\", \"__PYVENV_LAUNCHER__\"])\n\n        if environ:\n            environ = deepcopy(environ)\n            for key in exclude:\n                environ.pop(key, None)\n        else:\n            environ = {k: v for k, v in os.environ.items() if k not in exclude}\n\n        environ.update(kwargs)\n\n        environ[\"PATH\"] = self._updated_path()\n        environ[\"VIRTUAL_ENV\"] = str(self._path)\n\n        return environ\n\n    def execute(self, bin: str, *args: str, **kwargs: Any) -> int:\n        kwargs[\"env\"] = self.get_temp_environ(environ=kwargs.get(\"env\"))\n        return super().execute(bin, *args, **kwargs)\n\n    @contextmanager\n    def temp_environ(self) -> Iterator[None]:\n        environ = dict(os.environ)\n        try:\n            yield\n        finally:\n            os.environ.clear()\n            os.environ.update(environ)\n\n    def _updated_path(self) -> str:\n        return os.pathsep.join([str(self._bin_dir), os.environ.get(\"PATH\", \"\")])\n\n    @cached_property\n    def includes_system_site_packages(self) -> bool:\n        pyvenv_cfg = self._path / \"pyvenv.cfg\"\n        return pyvenv_cfg.exists() and (\n            re.search(\n                r\"^\\s*include-system-site-packages\\s*=\\s*true\\s*$\",\n                pyvenv_cfg.read_text(encoding=\"utf-8\"),\n                re.IGNORECASE | re.MULTILINE,\n            )\n            is not None\n        )\n\n    def is_path_relative_to_lib(self, path: Path) -> bool:\n        return super().is_path_relative_to_lib(path) or (\n            self.includes_system_site_packages\n            and self.parent_env.is_path_relative_to_lib(path)\n        )\n"
  },
  {
    "path": "src/poetry/utils/extras.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n    from collections.abc import Iterable\n    from collections.abc import Mapping\n\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import Package\n\n\ndef get_extra_package_names(\n    packages: Iterable[Package],\n    extras: Mapping[NormalizedName, Iterable[NormalizedName]],\n    extra_names: Collection[NormalizedName],\n) -> set[NormalizedName]:\n    \"\"\"\n    Returns all package names required by the given extras.\n\n    :param packages: A collection of packages, such as from Repository.packages\n    :param extras: A mapping of `extras` names to lists of package names, as defined\n        in the `extras` section of `poetry.lock`.\n    :param extra_names: A list of strings specifying names of extra groups to resolve.\n    \"\"\"\n    if not extra_names:\n        return set()\n\n    # lookup for packages by name, faster than looping over packages repeatedly\n    packages_by_name = {package.name: package for package in packages}\n\n    # Depth-first search, with our entry points being the packages directly required by\n    # extras.\n    seen_package_names = set()\n    stack = [\n        extra_package_name\n        for extra_name in extra_names\n        for extra_package_name in extras.get(extra_name, ())\n    ]\n\n    while stack:\n        package_name = stack.pop()\n\n        # We expect to find all packages, but can just carry on if we don't.\n        package = packages_by_name.get(package_name)\n        if package is None or package.name in seen_package_names:\n            continue\n\n        seen_package_names.add(package.name)\n\n        stack += [dependency.name for dependency in package.requires]\n\n    return seen_package_names\n"
  },
  {
    "path": "src/poetry/utils/helpers.py",
    "content": "from __future__ import annotations\n\nimport hashlib\nimport io\nimport logging\nimport os\nimport shutil\nimport stat\nimport sys\nimport tarfile\nimport tempfile\nimport zipfile\n\nfrom collections.abc import Mapping\nfrom contextlib import contextmanager\nfrom contextlib import suppress\nfrom functools import cached_property\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import overload\n\nfrom requests.exceptions import ChunkedEncodingError\nfrom requests.exceptions import ConnectionError\nfrom requests.utils import atomic_open\n\nfrom poetry.utils.authenticator import get_default_authenticator\nfrom poetry.utils.constants import REQUESTS_TIMEOUT\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Collection\n    from collections.abc import Iterator\n    from types import TracebackType\n\n    from poetry.core.packages.package import Package\n    from requests import Response\n    from requests import Session\n\n    from poetry.utils.authenticator import Authenticator\n\nlogger = logging.getLogger(__name__)\nprioritised_hash_types: tuple[str, ...] = tuple(\n    t\n    for t in [\n        \"sha3_512\",\n        \"sha3_384\",\n        \"sha3_256\",\n        \"sha3_224\",\n        \"sha512\",\n        \"sha384\",\n        \"sha256\",\n        \"sha224\",\n        \"shake_256\",\n        \"shake_128\",\n        \"blake2s\",\n        \"blake2b\",\n    ]\n    if t in hashlib.algorithms_available\n)\nnon_prioritised_available_hash_types: frozenset[str] = frozenset(\n    set(hashlib.algorithms_available).difference(prioritised_hash_types)\n)\n\n\n@contextmanager\ndef directory(path: Path) -> Iterator[Path]:\n    cwd = Path.cwd()\n    try:\n        os.chdir(path)\n        yield path\n    finally:\n        os.chdir(cwd)\n\n\n# Correct type signature when used as `shutil.rmtree(..., onexc=_on_rm_error)`.\n@overload\ndef _on_rm_error(\n    func: Callable[[str], None], path: str, exc_info: Exception\n) -> None: ...\n\n\n# Correct type signature when used as `shutil.rmtree(..., onerror=_on_rm_error)`.\n@overload\ndef _on_rm_error(\n    func: Callable[[str], None],\n    path: str,\n    exc_info: tuple[type[BaseException], BaseException, TracebackType],\n) -> None: ...\n\n\ndef _on_rm_error(func: Callable[[str], None], path: str, exc_info: Any) -> None:\n    if not os.path.exists(path):\n        return\n\n    os.chmod(path, stat.S_IWRITE)\n    func(path)\n\n\ndef remove_directory(path: Path, force: bool = False) -> None:\n    \"\"\"\n    Helper function handle safe removal, and optionally forces stubborn file removal.\n    This is particularly useful when dist files are read-only or git writes read-only\n    files on Windows.\n\n    Internally, all arguments are passed to `shutil.rmtree`.\n    \"\"\"\n    if path.is_symlink():\n        return os.unlink(path)\n\n    kwargs: dict[str, Any] = {}\n    if force:\n        onexc = \"onexc\" if sys.version_info >= (3, 12) else \"onerror\"\n        kwargs[onexc] = _on_rm_error\n\n    shutil.rmtree(path, **kwargs)\n\n\ndef merge_dicts(d1: dict[str, Any], d2: dict[str, Any]) -> None:\n    for k in d2:\n        if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], Mapping):\n            merge_dicts(d1[k], d2[k])\n        else:\n            d1[k] = d2[k]\n\n\nclass HTTPRangeRequestSupportedError(Exception):\n    \"\"\"Raised when server unexpectedly supports byte ranges.\"\"\"\n\n\ndef download_file(\n    url: str,\n    dest: Path,\n    *,\n    session: Authenticator | Session | None = None,\n    chunk_size: int = 1024,\n    raise_accepts_ranges: bool = False,\n    max_retries: int = 0,\n) -> None:\n    from poetry.puzzle.provider import Indicator\n\n    downloader = Downloader(url, dest, session, max_retries=max_retries)\n\n    if raise_accepts_ranges and downloader.accepts_ranges:\n        raise HTTPRangeRequestSupportedError(f\"URL {url} supports range requests.\")\n\n    set_indicator = False\n    with Indicator.context() as update_context:\n        update_context(f\"Downloading {url}\")\n\n        total_size = downloader.total_size\n        if total_size > 0:\n            fetched_size = 0\n            last_percent = 0\n\n            # if less than 1MB, we simply show that we're downloading\n            # but skip the updating\n            set_indicator = total_size > 1024 * 1024\n\n        for fetched_size in downloader.download_with_progress(chunk_size):\n            if set_indicator:\n                percent = (fetched_size * 100) // total_size\n                if percent > last_percent:\n                    last_percent = percent\n                    update_context(f\"Downloading {url} {percent:3}%\")\n\n\nclass Downloader:\n    def __init__(\n        self,\n        url: str,\n        dest: Path,\n        session: Authenticator | Session | None = None,\n        max_retries: int = 0,\n    ):\n        self._dest = dest\n        self._max_retries = max_retries\n        self._session = session or get_default_authenticator()\n        self._url = url\n        self._response = self._get()\n\n    @cached_property\n    def accepts_ranges(self) -> bool:\n        return self._response.headers.get(\"Accept-Ranges\") == \"bytes\"\n\n    @cached_property\n    def total_size(self) -> int:\n        total_size = 0\n        if \"Content-Length\" in self._response.headers:\n            with suppress(ValueError):\n                total_size = int(self._response.headers[\"Content-Length\"])\n        return total_size\n\n    def _get(self, start: int = 0) -> Response:\n        headers = {\"Accept-Encoding\": \"Identity\"}\n        if start > 0:\n            headers[\"Range\"] = f\"bytes={start}-\"\n\n        response = self._session.get(\n            self._url, stream=True, headers=headers, timeout=REQUESTS_TIMEOUT\n        )\n        try:\n            response.raise_for_status()\n            return response\n        except BaseException:\n            response.close()\n            raise\n\n    def _iter_content_with_resume(self, chunk_size: int) -> Iterator[bytes]:\n        fetched_size = 0\n        retries = 0\n        while True:\n            try:\n                with self._response:\n                    for chunk in self._response.iter_content(chunk_size=chunk_size):\n                        yield chunk\n                        fetched_size += len(chunk)\n            except (ChunkedEncodingError, ConnectionError):\n                if (\n                    retries < self._max_retries\n                    and self.accepts_ranges\n                    and fetched_size > 0\n                ):\n                    # only retry if server supports byte ranges\n                    # and we have fetched at least one chunk\n                    # otherwise, we should just fail\n                    retries += 1\n                    self._response = self._get(fetched_size)\n                    continue\n                raise\n            else:\n                break\n\n    def download_with_progress(self, chunk_size: int = 1024) -> Iterator[int]:\n        fetched_size = 0\n        with atomic_open(self._dest) as f:\n            for chunk in self._iter_content_with_resume(chunk_size=chunk_size):\n                if chunk:\n                    f.write(chunk)\n                    fetched_size += len(chunk)\n                    yield fetched_size\n\n\ndef get_package_version_display_string(\n    package: Package, root: Path | None = None\n) -> str:\n    if package.source_type in [\"file\", \"directory\"] and root:\n        assert package.source_url is not None\n        path = Path(os.path.relpath(package.source_url, root)).as_posix()\n        return f\"{package.version} {path}\"\n\n    pretty_version: str = package.full_pretty_version\n    return pretty_version\n\n\ndef paths_csv(paths: list[Path]) -> str:\n    return \", \".join(f'\"{c!s}\"' for c in paths)\n\n\ndef ensure_path(path: str | Path, is_directory: bool = False) -> Path:\n    if isinstance(path, str):\n        path = Path(path)\n\n    if path.exists() and path.is_dir() == is_directory:\n        return path\n\n    raise ValueError(\n        f\"Specified path '{path}' is not a valid {'directory' if is_directory else 'file'}.\"\n    )\n\n\ndef is_dir_writable(path: Path, create: bool = False) -> bool:\n    try:\n        if not path.exists():\n            if not create:\n                return False\n            path.mkdir(parents=True, exist_ok=True)\n\n        with tempfile.TemporaryFile(dir=str(path)):\n            pass\n    except OSError:\n        return False\n    else:\n        return True\n\n\ndef pluralize(count: int, word: str = \"\") -> str:\n    if count == 1:\n        return word\n    return word + \"s\"\n\n\ndef _get_win_folder_from_registry(csidl_name: str) -> str:\n    if sys.platform != \"win32\":\n        raise RuntimeError(\"Method can only be called on Windows.\")\n\n    import winreg as _winreg\n\n    shell_folder_name = {\n        \"CSIDL_APPDATA\": \"AppData\",\n        \"CSIDL_COMMON_APPDATA\": \"Common AppData\",\n        \"CSIDL_LOCAL_APPDATA\": \"Local AppData\",\n        \"CSIDL_PROGRAM_FILES\": \"Program Files\",\n    }[csidl_name]\n\n    key = _winreg.OpenKey(\n        _winreg.HKEY_CURRENT_USER,\n        r\"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\",\n    )\n    dir, _type = _winreg.QueryValueEx(key, shell_folder_name)\n\n    assert isinstance(dir, str)\n    return dir\n\n\ndef _get_win_folder_with_ctypes(csidl_name: str) -> str:\n    if sys.platform != \"win32\":\n        raise RuntimeError(\"Method can only be called on Windows.\")\n\n    import ctypes\n\n    csidl_const = {\n        \"CSIDL_APPDATA\": 26,\n        \"CSIDL_COMMON_APPDATA\": 35,\n        \"CSIDL_LOCAL_APPDATA\": 28,\n        \"CSIDL_PROGRAM_FILES\": 38,\n    }[csidl_name]\n\n    buf = ctypes.create_unicode_buffer(1024)\n    ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)\n\n    # Downgrade to short path name if have highbit chars. See\n    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.\n    has_high_char = False\n    for c in buf:\n        if ord(c) > 255:\n            has_high_char = True\n            break\n    if has_high_char:\n        buf2 = ctypes.create_unicode_buffer(1024)\n        if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):\n            buf = buf2\n\n    return buf.value\n\n\ndef get_win_folder(csidl_name: str) -> Path:\n    if sys.platform == \"win32\":\n        try:\n            from ctypes import windll  # noqa: F401\n\n            _get_win_folder = _get_win_folder_with_ctypes\n        except ImportError:\n            _get_win_folder = _get_win_folder_from_registry\n\n        return Path(_get_win_folder(csidl_name))\n\n    raise RuntimeError(\"Method can only be called on Windows.\")\n\n\ndef get_real_windows_path(path: Path) -> Path:\n    program_files = get_win_folder(\"CSIDL_PROGRAM_FILES\")\n    local_appdata = get_win_folder(\"CSIDL_LOCAL_APPDATA\")\n\n    path = Path(\n        str(path).replace(\n            str(program_files / \"WindowsApps\"),\n            str(local_appdata / \"Microsoft/WindowsApps\"),\n        )\n    )\n\n    if path.as_posix().startswith(local_appdata.as_posix()):\n        path = path.resolve()\n\n    return path\n\n\ndef get_file_hash(path: Path, hash_name: str = \"sha256\") -> str:\n    h = hashlib.new(hash_name)\n    with path.open(\"rb\") as fp:\n        for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b\"\"):\n            h.update(content)\n\n    return h.hexdigest()\n\n\ndef get_highest_priority_hash_type(\n    hash_types: Collection[str], archive_name: str\n) -> str | None:\n    if not hash_types:\n        return None\n\n    for prioritised_hash_type in prioritised_hash_types:\n        if prioritised_hash_type in hash_types:\n            return prioritised_hash_type\n\n    logger.debug(\n        f\"There are no known hash types for {archive_name} that are prioritised (known\"\n        f\" hash types: {hash_types!s})\"\n    )\n\n    for available_hash_type in non_prioritised_available_hash_types:\n        if available_hash_type in hash_types:\n            return available_hash_type\n\n    return None\n\n\ndef extractall(source: Path, dest: Path, zip: bool) -> None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        # These versions of python shipped with a broken tarfile data_filter, per\n        # https://github.com/python/cpython/issues/107845.\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                archive.extractall(dest)\n"
  },
  {
    "path": "src/poetry/utils/isolated_build.py",
    "content": "from __future__ import annotations\n\nimport os\nimport subprocess\n\nfrom contextlib import contextmanager\nfrom contextlib import redirect_stdout\nfrom io import StringIO\nfrom typing import TYPE_CHECKING\n\nfrom build import BuildBackendException\nfrom build.env import IsolatedEnv as BaseIsolatedEnv\nfrom poetry.core.packages.dependency_group import DependencyGroup\n\nfrom poetry.utils._compat import decode\nfrom poetry.utils.env import Env\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import ephemeral_environment\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n    from collections.abc import Iterator\n    from pathlib import Path\n\n    from build import DistributionType\n    from build import ProjectBuilder\n    from poetry.core.packages.dependency import Dependency\n\n    from poetry.repositories import RepositoryPool\n\n\nCONSTRAINTS_GROUP_NAME = \"constraints\"\n\n\nclass IsolatedBuildBaseError(Exception): ...\n\n\nclass IsolatedBuildBackendError(IsolatedBuildBaseError):\n    def __init__(self, source: Path, exception: BuildBackendException) -> None:\n        super().__init__()\n        self.source = source\n        self.exception = exception\n\n    def generate_message(\n        self, source_string: str | None = None, build_command: str | None = None\n    ) -> str:\n        e = self.exception.exception\n        source_string = source_string or self.source.as_posix()\n        build_command = (\n            build_command\n            or f'pip wheel --no-cache-dir --use-pep517 \"{self.source.as_posix()}\"'\n        )\n\n        reasons = [\"PEP517 build of a dependency failed\", str(self.exception)]\n\n        if isinstance(e, subprocess.CalledProcessError):\n            inner_traceback = decode(e.stderr or e.stdout or e.output).strip()\n            inner_reason = \"\\n    | \".join(\n                [\"\", str(e), \"\", *inner_traceback.split(\"\\n\")]\n            ).lstrip(\"\\n\")\n            reasons.append(f\"<warning>{inner_reason}</warning>\")\n\n        reasons.append(\n            \"<info>\"\n            \"<options=bold>Note:</> This error originates from the build backend, and is likely not a \"\n            f\"problem with poetry but one of the following issues with {source_string}\\n\\n\"\n            \"  - not supporting PEP 517 builds\\n\"\n            \"  - not specifying PEP 517 build requirements correctly\\n\"\n            \"  - the build requirements are incompatible with your operating system or Python version\\n\"\n            \"  - the build requirements are missing system dependencies (eg: compilers, libraries, headers).\\n\\n\"\n            f\"You can verify this by running <c1>{build_command}</c1>.\"\n            \"</info>\"\n        )\n\n        return \"\\n\\n\".join(reasons)\n\n    def __str__(self) -> str:\n        return self.generate_message()\n\n\nclass IsolatedBuildInstallError(IsolatedBuildBaseError):\n    def __init__(self, requirements: Collection[str], output: str, error: str) -> None:\n        message = \"\\n\\n\".join(\n            (\n                f\"Failed to install {', '.join(requirements)}.\",\n                f\"Output:\\n{output}\",\n                f\"Error:\\n{error}\",\n            )\n        )\n        super().__init__(message)\n        self._requirements = requirements\n\n    @property\n    def requirements(self) -> Collection[str]:\n        return self._requirements\n\n\nclass IsolatedEnv(BaseIsolatedEnv):\n    def __init__(self, env: Env, pool: RepositoryPool) -> None:\n        self._env = env\n        self._pool = pool\n\n    @property\n    def python_executable(self) -> str:\n        return str(self._env.python)\n\n    def make_extra_environ(self) -> dict[str, str]:\n        path = os.environ.get(\"PATH\")\n        scripts_dir = str(self._env._bin_dir)\n        return {\n            \"PATH\": (\n                os.pathsep.join([scripts_dir, path])\n                if path is not None\n                else scripts_dir\n            )\n        }\n\n    def install(\n        self,\n        requirements: Collection[str],\n        *,\n        constraints: list[Dependency] | None = None,\n    ) -> None:\n        from cleo.io.buffered_io import BufferedIO\n        from poetry.core.packages.dependency import Dependency\n        from poetry.core.packages.project_package import ProjectPackage\n\n        from poetry.config.config import Config\n        from poetry.installation.installer import Installer\n        from poetry.packages.locker import Locker\n        from poetry.repositories.installed_repository import InstalledRepository\n\n        # We build Poetry dependencies from the requirements\n        package = ProjectPackage(\"__root__\", \"0.0.0\")\n        package.python_versions = \".\".join(str(v) for v in self._env.version_info[:3])\n        env_markers = self._env.get_marker_env()\n\n        for requirement in requirements:\n            dependency = Dependency.create_from_pep_508(requirement)\n\n            if dependency.marker.is_empty() or dependency.marker.validate(env_markers):\n                # we ignore dependencies that are not valid for this environment\n                # this ensures that we do not end up with unnecessary constraint\n                # errors when solving build system requirements; this is assumed\n                # safe as this environment is ephemeral\n                package.add_dependency(dependency)\n\n        if constraints:\n            constraints_group = DependencyGroup(CONSTRAINTS_GROUP_NAME, optional=True)\n            for constraint in constraints:\n                if constraint.marker.validate(env_markers):\n                    constraints_group.add_dependency(constraint)\n            package.add_dependency_group(constraints_group)\n\n        io = BufferedIO()\n\n        installer = Installer(\n            io,\n            self._env,\n            package,\n            Locker(self._env.path.joinpath(\"poetry.lock\"), {}),\n            self._pool,\n            Config.create(),\n            InstalledRepository.load(self._env),\n        )\n        installer.update(True)\n\n        if installer.run() != 0:\n            raise IsolatedBuildInstallError(\n                requirements, io.fetch_output(), io.fetch_error()\n            )\n\n\n@contextmanager\ndef isolated_builder(\n    source: Path,\n    distribution: DistributionType = \"wheel\",\n    python_executable: Path | None = None,\n    pool: RepositoryPool | None = None,\n    *,\n    build_constraints: list[Dependency] | None = None,\n) -> Iterator[ProjectBuilder]:\n    from build import ProjectBuilder\n    from pyproject_hooks import quiet_subprocess_runner\n\n    from poetry.factory import Factory\n\n    try:\n        # we recreate the project's Poetry instance in order to retrieve the correct repository pool\n        # when a pool is not provided\n        pool = pool or Factory().create_poetry().pool\n    except RuntimeError:\n        # the context manager is not being called within a Poetry project context\n        # fallback to a default pool using only PyPI as source\n        from poetry.repositories import RepositoryPool\n        from poetry.repositories.pypi_repository import PyPiRepository\n\n        # fallback to using only PyPI\n        pool = RepositoryPool(repositories=[PyPiRepository()])\n\n    python_executable = (\n        python_executable or EnvManager.get_system_env(naive=True).python\n    )\n\n    with ephemeral_environment(\n        executable=python_executable,\n        flags={\"no-pip\": True},\n    ) as venv:\n        env = IsolatedEnv(venv, pool)\n        stdout = StringIO()\n        try:\n            builder = ProjectBuilder.from_isolated_env(\n                env, source, runner=quiet_subprocess_runner\n            )\n\n            with redirect_stdout(stdout):\n                env.install(\n                    builder.build_system_requires, constraints=build_constraints\n                )\n\n                # we repeat the build system requirements to avoid poetry installer from removing them\n                env.install(\n                    builder.build_system_requires\n                    | builder.get_requires_for_build(distribution),\n                    constraints=build_constraints,\n                )\n\n                yield builder\n        except BuildBackendException as e:\n            raise IsolatedBuildBackendError(source, e) from None\n"
  },
  {
    "path": "src/poetry/utils/log_utils.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n    from poetry.utils.env import Env\n\n\ndef format_build_wheel_log(package: Package, env: Env) -> str:\n    \"\"\"Format a log message indicating\n    that a wheel is being built for the given package and environment.\"\"\"\n    marker_env = env.marker_env\n\n    python_version_info = marker_env.get(\n        \"version_info\", (\"<unknown>\", \"<unknown>\", \"<unknown>\")\n    )\n    python_version = (\n        f\"{python_version_info[0]}.{python_version_info[1]}.{python_version_info[2]}\"\n    )\n    platform = marker_env.get(\"sys_platform\", \"<unknown-platform>\")\n    architecture = marker_env.get(\"platform_machine\", \"<unknown-arch>\")\n\n    message = (\n        f\" <info>Building a wheel file for {package.pretty_name} \"\n        f\"for Python {python_version} on {platform}-{architecture}</info>\"\n    )\n    return message\n"
  },
  {
    "path": "src/poetry/utils/password_manager.py",
    "content": "from __future__ import annotations\n\nimport dataclasses\nimport functools\nimport logging\n\nfrom contextlib import suppress\nfrom typing import TYPE_CHECKING\n\nfrom poetry.config.config import Config\nfrom poetry.utils.threading import atomic_cached_property\n\n\nif TYPE_CHECKING:\n    import keyring.backend\n\n    from cleo.io.io import IO\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass PoetryKeyringError(Exception):\n    pass\n\n\n@dataclasses.dataclass\nclass HTTPAuthCredential:\n    username: str | None = dataclasses.field(default=None)\n    password: str | None = dataclasses.field(default=None)\n\n\nclass PoetryKeyring:\n    # some private sources expect tokens to be provided as passwords with empty usernames\n    # we use a fixed literal to ensure that this can be stored in keyring (jaraco/keyring#687)\n    #\n    # Note: If this is changed, users with passwords stored with empty usernames will have to\n    # re-add the config.\n    _EMPTY_USERNAME_KEY = \"__poetry_source_empty_username__\"\n\n    def __init__(self, namespace: str) -> None:\n        self._namespace = namespace\n\n    @staticmethod\n    def preflight_check(io: IO | None = None, config: Config | None = None) -> None:\n        \"\"\"\n        Performs a preflight check to determine the availability of the keyring service\n        and logs the status if verbosity is enabled. This method is used to validate\n        the configuration setup related to the keyring functionality.\n\n        :param io: An optional input/output handler used to log messages during the\n            preflight check. If not provided, logging will be skipped.\n        :param config: An optional configuration object. If not provided, a new\n            configuration instance will be created using the default factory method.\n        :return: None\n        \"\"\"\n        config = config or Config.create()\n\n        if config.get(\"keyring.enabled\"):\n            if io and io.is_verbose():\n                io.write(\"Checking keyring availability: \")\n\n            message = \"<fg=yellow;options=bold>Unavailable</>\"\n\n            with suppress(RuntimeError, ValueError):\n                if PoetryKeyring.is_available():\n                    message = \"<fg=green;options=bold>Available</>\"\n\n            if io and io.is_verbose():\n                io.write(message)\n                io.write_line(\"\")\n\n    def get_credential(\n        self, *names: str, username: str | None = None\n    ) -> HTTPAuthCredential:\n        import keyring\n\n        from keyring.errors import KeyringError\n        from keyring.errors import KeyringLocked\n\n        for name in names:\n            credential = None\n            try:\n                # we do default to empty username string here since credentials support empty usernames\n                credential = keyring.get_credential(name, username)\n            except KeyringLocked:\n                logger.debug(\"Keyring %s is locked\", name)\n            except (KeyringError, RuntimeError):\n                logger.debug(\"Accessing keyring %s failed\", name, exc_info=True)\n\n            if credential:\n                return HTTPAuthCredential(\n                    username=credential.username, password=credential.password\n                )\n\n        return HTTPAuthCredential(username=username, password=None)\n\n    def get_password(self, name: str, username: str) -> str | None:\n        import keyring\n        import keyring.errors\n\n        name = self.get_entry_name(name)\n\n        try:\n            return keyring.get_password(name, username or self._EMPTY_USERNAME_KEY)\n        except (RuntimeError, keyring.errors.KeyringError) as e:\n            raise PoetryKeyringError(\n                f\"Unable to retrieve the password for {name} from the key ring {e}\"\n            )\n\n    def set_password(self, name: str, username: str, password: str) -> None:\n        import keyring\n        import keyring.errors\n\n        name = self.get_entry_name(name)\n\n        try:\n            keyring.set_password(name, username or self._EMPTY_USERNAME_KEY, password)\n        except (RuntimeError, keyring.errors.KeyringError) as e:\n            raise PoetryKeyringError(\n                f\"Unable to store the password for {name} in the key ring: {e}\"\n            )\n\n    def delete_password(self, name: str, username: str) -> None:\n        import keyring.errors\n\n        name = self.get_entry_name(name)\n\n        try:\n            keyring.delete_password(name, username or self._EMPTY_USERNAME_KEY)\n        except (RuntimeError, keyring.errors.KeyringError):\n            raise PoetryKeyringError(\n                f\"Unable to delete the password for {name} from the key ring\"\n            )\n\n    def get_entry_name(self, name: str) -> str:\n        return f\"{self._namespace}-{name}\"\n\n    @classmethod\n    @functools.cache\n    def is_available(cls) -> bool:\n        logger.debug(\"Checking if keyring is available\")\n        try:\n            import keyring\n            import keyring.backend\n            import keyring.errors\n        except ImportError as e:\n            logger.debug(\"An error occurred while importing keyring: %s\", e)\n            return False\n\n        def backend_name(backend: keyring.backend.KeyringBackend) -> str:\n            name: str = backend.name\n            return name.split(\" \")[0]\n\n        def backend_is_valid(backend: keyring.backend.KeyringBackend) -> bool:\n            name = backend_name(backend)\n            if name in (\"chainer\", \"fail\", \"null\"):\n                logger.debug(f\"Backend {backend.name!r} is not suitable\")\n                return False\n            elif \"plaintext\" in backend.name.lower():\n                logger.debug(f\"Not using plaintext keyring backend {backend.name!r}\")\n                return False\n\n            return True\n\n        backend = keyring.get_keyring()\n        if backend_name(backend) == \"chainer\":\n            backends = keyring.backend.get_all_keyring()\n            valid_backend = next((b for b in backends if backend_is_valid(b)), None)\n        else:\n            valid_backend = backend if backend_is_valid(backend) else None\n\n        if valid_backend is None:\n            logger.debug(\"No valid keyring backend was found\")\n            return False\n\n        logger.debug(f\"Using keyring backend {backend.name!r}\")\n\n        try:\n            # unfortunately there is no clean way of checking if keyring is unlocked\n            keyring.get_password(\"python-poetry-check\", \"python-poetry\")\n        except (RuntimeError, keyring.errors.KeyringError):\n            logger.debug(\n                \"Accessing keyring failed during availability check\", exc_info=True\n            )\n            return False\n\n        return True\n\n\nclass PasswordManager:\n    def __init__(self, config: Config) -> None:\n        self._config = config\n\n    @atomic_cached_property\n    def use_keyring(self) -> bool:\n        return self._config.get(\"keyring.enabled\") and PoetryKeyring.is_available()\n\n    @atomic_cached_property\n    def keyring(self) -> PoetryKeyring:\n        if not self.use_keyring:\n            raise PoetryKeyringError(\n                \"Access to keyring was requested, but it is not available\"\n            )\n\n        return PoetryKeyring(\"poetry-repository\")\n\n    @staticmethod\n    def warn_plaintext_credentials_stored() -> None:\n        logger.warning(\"Using a plaintext file to store credentials\")\n\n    def set_pypi_token(self, repo_name: str, token: str) -> None:\n        if not self.use_keyring:\n            self.warn_plaintext_credentials_stored()\n            self._config.auth_config_source.add_property(\n                f\"pypi-token.{repo_name}\", token\n            )\n        else:\n            self.keyring.set_password(repo_name, \"__token__\", token)\n\n    def get_pypi_token(self, repo_name: str) -> str | None:\n        \"\"\"Get PyPi token.\n\n        First checks the environment variables for a token,\n        then the configured username/password and the\n        available keyring.\n\n        :param repo_name:  Name of repository.\n        :return: Returns a token as a string if found, otherwise None.\n        \"\"\"\n        token: str | None = self._config.get(f\"pypi-token.{repo_name}\")\n        if token:\n            return token\n\n        if self.use_keyring:\n            return self.keyring.get_password(repo_name, \"__token__\")\n        else:\n            return None\n\n    def delete_pypi_token(self, repo_name: str) -> None:\n        if not self.use_keyring:\n            return self._config.auth_config_source.remove_property(\n                f\"pypi-token.{repo_name}\"\n            )\n\n        self.keyring.delete_password(repo_name, \"__token__\")\n\n    def get_http_auth(self, repo_name: str) -> HTTPAuthCredential:\n        username = self._config.get(f\"http-basic.{repo_name}.username\")\n        password = self._config.get(f\"http-basic.{repo_name}.password\")\n\n        if password is None and self.use_keyring:\n            password = self.keyring.get_password(repo_name, username)\n\n        # we use `or None` here to ensure that empty strings are passed as None\n        return HTTPAuthCredential(username=username or None, password=password or None)\n\n    def set_http_password(self, repo_name: str, username: str, password: str) -> None:\n        auth = {\"username\": username}\n\n        if not self.use_keyring:\n            self.warn_plaintext_credentials_stored()\n            auth[\"password\"] = password\n        else:\n            self.keyring.set_password(repo_name, username, password)\n\n        self._config.auth_config_source.add_property(f\"http-basic.{repo_name}\", auth)\n\n    def delete_http_password(self, repo_name: str) -> None:\n        auth = self.get_http_auth(repo_name)\n\n        if auth.username is None:\n            return\n\n        with suppress(PoetryKeyringError):\n            self.keyring.delete_password(repo_name, auth.username)\n\n        self._config.auth_config_source.remove_property(f\"http-basic.{repo_name}\")\n\n    def get_credential(\n        self, *names: str, username: str | None = None\n    ) -> HTTPAuthCredential:\n        if self.use_keyring:\n            return self.keyring.get_credential(*names, username=username)\n\n        return HTTPAuthCredential(username=username, password=None)\n"
  },
  {
    "path": "src/poetry/utils/patterns.py",
    "content": "from __future__ import annotations\n\nimport re\n\n\nwheel_file_re = re.compile(\n    r\"^(?P<namever>(?P<name>.+?)-(?P<ver>\\d[^-]*))\"\n    r\"(-(?P<build>\\d[^-]*))?\"\n    r\"-(?P<pyver>[^-]+)\"\n    r\"-(?P<abi>[^-]+)\"\n    r\"-(?P<plat>[^-]+)\"\n    r\"\\.whl$\",\n    re.VERBOSE,\n)\n\nsdist_file_re = re.compile(\n    r\"^(?P<namever>(?P<name>.+?)-(?P<ver>\\d[^-]*?))\"\n    r\"(\\.sdist)?\\.(?P<format>(zip|tar(\\.(gz|bz2|xz|Z))?))$\"\n)\n"
  },
  {
    "path": "src/poetry/utils/pip.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.exceptions import PoetryError\nfrom poetry.utils.env import EnvCommandError\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from poetry.utils.env import Env\n\n\ndef pip_install(\n    path: Path,\n    environment: Env,\n    editable: bool = False,\n    deps: bool = False,\n    upgrade: bool = False,\n) -> str:\n    is_wheel = path.suffix == \".whl\"\n\n    # We disable version check here as we are already pinning to version available in\n    # either the virtual environment or the virtualenv package embedded wheel. Version\n    # checks are a wasteful network call that adds a lot of wait time when installing a\n    # lot of packages.\n    args = [\n        \"install\",\n        \"--disable-pip-version-check\",\n        \"--isolated\",\n        \"--no-input\",\n        \"--prefix\",\n        str(environment.path),\n    ]\n\n    if not is_wheel and not editable:\n        args.insert(1, \"--use-pep517\")\n\n    if upgrade:\n        args.append(\"--upgrade\")\n\n    if not deps:\n        args.append(\"--no-deps\")\n\n    if editable:\n        if not path.is_dir():\n            raise PoetryError(\n                \"Cannot install non directory dependencies in editable mode\"\n            )\n        args.append(\"-e\")\n\n    args.append(str(path))\n\n    try:\n        return environment.run_pip(*args)\n    except EnvCommandError as e:\n        raise PoetryError(f\"Failed to install {path}\") from e\n"
  },
  {
    "path": "src/poetry/utils/threading.py",
    "content": "from __future__ import annotations\n\nimport functools\nimport threading\n\nfrom typing import TYPE_CHECKING\nfrom typing import TypeVar\nfrom typing import overload\nfrom weakref import WeakKeyDictionary\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from typing import Any\n\n\nT = TypeVar(\"T\")\nC = TypeVar(\"C\", bound=object)\n\n\nclass AtomicCachedProperty(functools.cached_property[T]):\n    def __init__(self, func: Callable[[C], T]) -> None:\n        super().__init__(func)\n        self._semaphore = threading.BoundedSemaphore()\n        self._locks: WeakKeyDictionary[object, threading.Lock] = WeakKeyDictionary()\n\n    @overload\n    def __get__(\n        self, instance: None, owner: type[Any] | None = ...\n    ) -> AtomicCachedProperty[T]: ...\n    @overload\n    def __get__(self, instance: object, owner: type[Any] | None = ...) -> T: ...\n\n    def __get__(\n        self, instance: C | None, owner: type[Any] | None = None\n    ) -> AtomicCachedProperty[T] | T:\n        # If there's no instance, return the descriptor itself\n        if instance is None:\n            return self\n\n        if instance not in self._locks:\n            with self._semaphore:\n                # we double-check the lock has not been created by another thread\n                if instance not in self._locks:\n                    self._locks[instance] = threading.Lock()\n\n        # Use a thread-safe lock to ensure the property is computed only once\n        with self._locks[instance]:\n            return super().__get__(instance, owner)\n\n\ndef atomic_cached_property(func: Callable[[C], T]) -> AtomicCachedProperty[T]:\n    \"\"\"\n    A thread-safe implementation of functools.cached_property that ensures lazily-computed\n    properties are calculated only once, even in multithreaded environments.\n\n    This property decorator works similar to functools.cached_property but employs\n    thread locks and a bounded semaphore to handle concurrent access safely.\n\n    The computed value is cached on the instance itself and is reused for subsequent\n    accesses unless explicitly invalidated. The added thread-safety makes it ideal for\n    situations where multiple threads might access and compute the property simultaneously.\n\n    Note:\n    - The cache is stored in the instance dictionary just like `functools.cached_property`.\n\n    :param func: The function to be turned into a thread-safe cached property.\n    \"\"\"\n    return AtomicCachedProperty(func)\n"
  },
  {
    "path": "src/poetry/utils/wheel.py",
    "content": "from __future__ import annotations\n\nimport logging\n\nfrom typing import TYPE_CHECKING\n\nfrom packaging.tags import Tag\n\nfrom poetry.utils.patterns import wheel_file_re\n\n\nif TYPE_CHECKING:\n    from poetry.utils.env import Env\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass InvalidWheelNameError(Exception):\n    pass\n\n\nclass Wheel:\n    def __init__(self, filename: str) -> None:\n        wheel_info = wheel_file_re.match(filename)\n        if not wheel_info:\n            raise InvalidWheelNameError(f\"{filename} is not a valid wheel filename.\")\n\n        self.filename = filename\n        self.name = wheel_info.group(\"name\").replace(\"_\", \"-\")\n        self.version = wheel_info.group(\"ver\").replace(\"_\", \"-\")\n        self.build_tag = wheel_info.group(\"build\")\n        self.pyversions = wheel_info.group(\"pyver\").split(\".\")\n        self.abis = wheel_info.group(\"abi\").split(\".\")\n        self.plats = wheel_info.group(\"plat\").split(\".\")\n\n        self.tags = {\n            Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats\n        }\n\n    def get_minimum_supported_index(self, tags: list[Tag]) -> int | None:\n        indexes = [tags.index(t) for t in self.tags if t in tags]\n\n        return min(indexes) if indexes else None\n\n    def is_supported_by_environment(self, env: Env) -> bool:\n        return bool(set(env.supported_tags).intersection(self.tags))\n"
  },
  {
    "path": "src/poetry/vcs/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/vcs/git/__init__.py",
    "content": "from __future__ import annotations\n\nfrom poetry.vcs.git.backend import Git\n\n\n__all__ = [\"Git\"]\n"
  },
  {
    "path": "src/poetry/vcs/git/backend.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport dataclasses\nimport logging\nimport os\nimport re\n\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom urllib.parse import urljoin\nfrom urllib.parse import urlparse\nfrom urllib.parse import urlunparse\n\nfrom dulwich import porcelain\nfrom dulwich.client import HTTPUnauthorized\nfrom dulwich.client import get_transport_and_path\nfrom dulwich.config import ConfigFile\nfrom dulwich.config import parse_submodules\nfrom dulwich.errors import NotGitRepository\nfrom dulwich.file import FileLocked\nfrom dulwich.index import IndexEntry\nfrom dulwich.object_store import peel_sha\nfrom dulwich.objects import ObjectID\nfrom dulwich.protocol import PEELED_TAG_SUFFIX\nfrom dulwich.refs import Ref\nfrom dulwich.repo import Repo\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.authenticator import get_default_authenticator\nfrom poetry.utils.helpers import remove_directory\n\n\nif TYPE_CHECKING:\n    from dulwich.client import FetchPackResult\n    from dulwich.client import GitClient\n\n\nlogger = logging.getLogger(__name__)\n\n# A relative URL by definition starts with ../ or ./\nRELATIVE_SUBMODULE_REGEX = re.compile(r\"^\\.{1,2}/\")\n\n# Common error messages\nERROR_MESSAGE_NOTE = (\n    \"<b>Note:</> This error arises from interacting with \"\n    \"the specified vcs source and is likely not a \"\n    \"Poetry issue.\"\n)\nERROR_MESSAGE_PROBLEMS_SECTION_START = (\n    \"This issue could be caused by any of the following;\\n\"\n)\nERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES = (\n    f\"{ERROR_MESSAGE_PROBLEMS_SECTION_START}\\n\"\n    \"- there are network issues in this environment\"\n)\nERROR_MESSAGE_BAD_REVISION = (\n    \"- the revision ({revision}) you have specified\\n\"\n    \"    - was misspelled\\n\"\n    \"    - is invalid (must be a sha or symref)\\n\"\n    \"    - is not present on remote\"\n)\nERROR_MESSAGE_BAD_REMOTE = (\n    \"- the remote ({remote}) you have specified\\n\"\n    \"    - was misspelled\\n\"\n    \"    - does not exist\\n\"\n    \"    - requires credentials that were either not configured or are incorrect\\n\"\n    \"    - contains Git submodules that require credentials that were either not configured or are incorrect\\n\"\n    \"    - is inaccessible due to network issues\"\n)\nERROR_MESSAGE_FILE_LOCK = (\n    \"- another process is holding the file lock\\n\"\n    \"- another process crashed while holding the file lock\\n\\n\"\n    \"Try again later or remove the {lock_file} manually\"\n    \" if you are sure no other process is holding it.\"\n)\n\n\ndef is_revision_sha(revision: str | None) -> bool:\n    return re.match(r\"^\\b[0-9a-f]{5,40}\\b$\", revision or \"\") is not None\n\n\ndef peeled_tag(ref: str | bytes) -> Ref:\n    if isinstance(ref, str):\n        ref = ref.encode(\"utf-8\")\n    return Ref(ref + PEELED_TAG_SUFFIX)\n\n\n@dataclasses.dataclass\nclass GitRefSpec:\n    branch: str | None = None\n    revision: str | None = None\n    tag: str | None = None\n    ref: Ref = dataclasses.field(default_factory=lambda: Ref(b\"HEAD\"))\n\n    def resolve(self, remote_refs: FetchPackResult, repo: Repo) -> None:\n        \"\"\"\n        Resolve the ref using the provided remote refs.\n        \"\"\"\n        self._normalise(remote_refs=remote_refs, repo=repo)\n        self._set_head(remote_refs=remote_refs, repo=repo)\n\n    def _normalise(self, remote_refs: FetchPackResult, repo: Repo) -> None:\n        \"\"\"\n        Internal helper method to determine if given revision is\n            1. a branch or tag; if so, set corresponding properties.\n            2. a short sha; if so, resolve full sha and set as revision\n        \"\"\"\n        if self.revision:\n            ref = f\"refs/tags/{self.revision}\".encode()\n            if ref in remote_refs.refs or peeled_tag(ref) in remote_refs.refs:\n                # this is a tag, incorrectly specified as a revision, tags take priority\n                self.tag = self.revision\n                self.revision = None\n            elif (\n                self.revision.encode(\"utf-8\") in remote_refs.refs\n                or f\"refs/heads/{self.revision}\".encode() in remote_refs.refs\n            ):\n                # this is most likely a ref spec or a branch incorrectly specified\n                self.branch = self.revision\n                self.revision = None\n        elif (\n            self.branch\n            and f\"refs/heads/{self.branch}\".encode() not in remote_refs.refs\n            and (\n                f\"refs/tags/{self.branch}\".encode() in remote_refs.refs\n                or peeled_tag(f\"refs/tags/{self.branch}\") in remote_refs.refs\n            )\n        ):\n            # this is a tag incorrectly specified as a branch\n            self.tag = self.branch\n            self.branch = None\n\n        if self.revision and self.is_sha_short:\n            # revision is a short sha, resolve to full sha\n            short_sha = self.revision.encode(\"utf-8\")\n            for sha in remote_refs.refs.values():\n                if sha is not None and sha.startswith(short_sha):\n                    self.revision = sha.decode(\"utf-8\")\n                    return\n\n            # no heads with such SHA, let's check all objects\n            for sha in repo.object_store.iter_prefix(short_sha):\n                self.revision = sha.decode(\"utf-8\")\n                return\n\n    def _set_head(self, remote_refs: FetchPackResult, repo: Repo) -> None:\n        \"\"\"\n        Internal helper method to populate ref and set it's sha as the remote's head\n        and default ref.\n        \"\"\"\n        self.ref = remote_refs.symrefs[Ref(b\"HEAD\")]\n\n        head: ObjectID | None\n        if self.revision:\n            head = ObjectID(self.revision.encode(\"utf-8\"))\n        else:\n            if self.tag:\n                ref = Ref(f\"refs/tags/{self.tag}\".encode())\n                peeled = peeled_tag(ref)\n                self.ref = peeled if peeled in remote_refs.refs else ref\n            elif self.branch:\n                self.ref = (\n                    Ref(self.branch.encode(\"utf-8\"))\n                    if self.is_ref\n                    else Ref(f\"refs/heads/{self.branch}\".encode())\n                )\n            head = remote_refs.refs[self.ref]\n\n            # Peel tag objects to get the underlying commit SHA.\n            # Annotated tags are Tag objects, not Commit objects. Operations like\n            # reset_index() expect HEAD to point to a Commit, so we must peel tags\n            # to extract the commit SHA they reference.\n            # Object not in store yet will be handled during fetch\n            if head is not None:\n                with contextlib.suppress(KeyError):\n                    head = peel_sha(repo.object_store, head)[1].id\n\n        remote_refs.refs[self.ref] = remote_refs.refs[Ref(b\"HEAD\")] = head\n\n    @property\n    def key(self) -> str:\n        return self.revision or self.branch or self.tag or self.ref.decode(\"utf-8\")\n\n    @property\n    def is_sha(self) -> bool:\n        return is_revision_sha(revision=self.revision)\n\n    @property\n    def is_ref(self) -> bool:\n        return self.branch is not None and (\n            self.branch.startswith(\"refs/\") or self.branch == \"HEAD\"\n        )\n\n    @property\n    def is_sha_short(self) -> bool:\n        return self.revision is not None and self.is_sha and len(self.revision) < 40\n\n\n@dataclasses.dataclass\nclass GitRepoLocalInfo:\n    repo: dataclasses.InitVar[Repo | Path]\n    origin: str = dataclasses.field(init=False)\n    revision: str = dataclasses.field(init=False)\n\n    def __post_init__(self, repo: Repo | Path) -> None:\n        repo = Git.as_repo(repo=repo) if not isinstance(repo, Repo) else repo\n        self.origin = Git.get_remote_url(repo=repo, remote=\"origin\")\n        self.revision = Git.get_revision(repo=repo)\n\n\nclass Git:\n    @staticmethod\n    def as_repo(repo: Path) -> Repo:\n        return Repo(str(repo))\n\n    @staticmethod\n    def get_remote_url(repo: Repo, remote: str = \"origin\") -> str:\n        with repo:\n            config = repo.get_config()\n            section = (b\"remote\", remote.encode(\"utf-8\"))\n\n            url = \"\"\n            if config.has_section(section):\n                value = config.get(section, b\"url\")\n                url = value.decode(\"utf-8\")\n\n            return url\n\n    @staticmethod\n    def get_revision(repo: Repo) -> str:\n        with repo:\n            return repo.get_peeled(Ref(b\"HEAD\")).decode(\"utf-8\")\n\n    @classmethod\n    def info(cls, repo: Repo | Path) -> GitRepoLocalInfo:\n        return GitRepoLocalInfo(repo=repo)\n\n    @staticmethod\n    def get_name_from_source_url(url: str) -> str:\n        return re.sub(r\"(.git)?$\", \"\", url.rstrip(\"/\").rsplit(\"/\", 1)[-1])\n\n    @classmethod\n    def _fetch_remote_refs(cls, url: str, local: Repo) -> FetchPackResult:\n        \"\"\"\n        Helper method to fetch remote refs.\n        \"\"\"\n        client: GitClient\n        path: str\n\n        credentials = get_default_authenticator().get_credentials_for_git_url(url=url)\n\n        username = None\n        password = None\n        if credentials.password and credentials.username:\n            # we do this conditionally as otherwise, dulwich might complain if these\n            # parameters are passed in for an ssh url\n            username = credentials.username\n            password = credentials.password\n\n        config = local.get_config_stack()\n        client, path = get_transport_and_path(\n            url, config=config, username=username, password=password\n        )\n\n        with local:\n            result: FetchPackResult = client.fetch(\n                path,\n                local,\n                determine_wants=local.object_store.determine_wants_all,\n            )\n            return result\n\n    @staticmethod\n    def _clone_legacy(url: str, refspec: GitRefSpec, target: Path) -> Repo:\n        \"\"\"\n        Helper method to facilitate fallback to using system provided git client via\n        subprocess calls.\n        \"\"\"\n        from poetry.vcs.git.system import SystemGit\n\n        logger.debug(\"Cloning '%s' using system git client\", url)\n\n        if target.exists():\n            remove_directory(path=target, force=True)\n\n        revision = refspec.tag or refspec.branch or refspec.revision or \"HEAD\"\n\n        try:\n            SystemGit.clone(url, target)\n        except CalledProcessError as e:\n            raise PoetryRuntimeError.create(\n                reason=f\"<error>Failed to clone <info>{url}</>, check your git configuration and permissions for this repository.</>\",\n                exception=e,\n                info=[\n                    ERROR_MESSAGE_NOTE,\n                    ERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES,\n                    ERROR_MESSAGE_BAD_REMOTE.format(remote=url),\n                ],\n            )\n\n        if revision:\n            revision.replace(\"refs/head/\", \"\")\n            revision.replace(\"refs/tags/\", \"\")\n\n        try:\n            SystemGit.checkout(revision, target)\n        except CalledProcessError as e:\n            raise PoetryRuntimeError.create(\n                reason=f\"<error>Failed to checkout {url} at '{revision}'.</>\",\n                exception=e,\n                info=[\n                    ERROR_MESSAGE_NOTE,\n                    ERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES,\n                    ERROR_MESSAGE_BAD_REVISION.format(revision=revision),\n                ],\n            )\n\n        repo = Repo(str(target))\n        return repo\n\n    @classmethod\n    def _clone(cls, url: str, refspec: GitRefSpec, target: Path) -> Repo:\n        \"\"\"\n        Helper method to clone a remove repository at the given `url` at the specified\n        ref spec.\n        \"\"\"\n        local: Repo\n        if not target.exists():\n            local = Repo.init(str(target), mkdir=True)\n            porcelain.remote_add(local, \"origin\", url)\n        else:\n            local = Repo(str(target))\n\n        remote_refs = cls._fetch_remote_refs(url=url, local=local)\n\n        logger.debug(\n            \"Cloning <c2>%s</> at '<c2>%s</>' to <c1>%s</>\", url, refspec.key, target\n        )\n\n        try:\n            refspec.resolve(remote_refs=remote_refs, repo=local)\n        except KeyError:  # branch / ref does not exist\n            raise PoetryRuntimeError.create(\n                reason=f\"<error>Failed to clone {url} at '{refspec.key}', verify ref exists on remote.</>\",\n                info=[\n                    ERROR_MESSAGE_NOTE,\n                    ERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES,\n                    ERROR_MESSAGE_BAD_REVISION.format(revision=refspec.key),\n                ],\n            )\n\n        try:\n            # ensure local HEAD matches remote\n            ref = remote_refs.refs[Ref(b\"HEAD\")]\n            if ref is not None:\n                local.refs[Ref(b\"HEAD\")] = ref\n        except ValueError:\n            raise PoetryRuntimeError.create(\n                reason=f\"<error>Failed to clone {url} at '{refspec.key}', verify ref exists on remote.</>\",\n                info=[\n                    ERROR_MESSAGE_NOTE,\n                    ERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES,\n                    ERROR_MESSAGE_BAD_REVISION.format(revision=refspec.key),\n                    f\"\\nThis particular error is prevalent when {refspec.key} could not be resolved to a specific commit sha.\",\n                ],\n            )\n\n        if refspec.is_ref:\n            # set ref to current HEAD\n            local.refs[refspec.ref] = local.refs[Ref(b\"HEAD\")]\n\n        for base, prefix in {\n            (Ref(b\"refs/remotes/origin\"), b\"refs/heads/\"),\n            (Ref(b\"refs/tags\"), b\"refs/tags\"),\n        }:\n            try:\n                local.refs.import_refs(\n                    base=base,\n                    other={\n                        Ref(n[len(prefix) :]): v\n                        for (n, v) in remote_refs.refs.items()\n                        if n.startswith(prefix)\n                        and not n.endswith(PEELED_TAG_SUFFIX)\n                        and v is not None\n                    },\n                )\n            except FileLocked as e:\n\n                def to_str(path: bytes | str) -> str:\n                    if isinstance(path, bytes):\n                        path = path.decode()\n                    return path.replace(os.sep * 2, os.sep)\n\n                raise PoetryRuntimeError.create(\n                    # <https://github.com/jelmer/dulwich/pull/2045> should clean up the\n                    # ignore.\n                    reason=(\n                        f\"<error>Failed to clone {url} at '{refspec.key}',\"\n                        f\" unable to acquire file lock for {to_str(e.filename)}.</>\"\n                    ),\n                    info=[\n                        ERROR_MESSAGE_NOTE,\n                        ERROR_MESSAGE_PROBLEMS_SECTION_START,\n                        ERROR_MESSAGE_FILE_LOCK.format(\n                            lock_file=to_str(e.lockfilename)\n                        ),\n                    ],\n                )\n\n        try:\n            with local:\n                local.get_worktree().reset_index()\n        except (AssertionError, KeyError) as e:\n            # this implies the ref we need does not exist or is invalid\n            if isinstance(e, KeyError):\n                # the local copy is at a bad state, lets remove it\n                logger.debug(\n                    \"Removing local clone (<c1>%s</>) of repository as it is in a\"\n                    \" broken state.\",\n                    local.path,\n                )\n                remove_directory(Path(local.path), force=True)\n\n            if isinstance(e, AssertionError) and \"Invalid object name\" not in str(e):\n                raise\n\n            raise PoetryRuntimeError.create(\n                reason=f\"<error>Failed to clone {url} at '{refspec.key}', verify ref exists on remote.</>\",\n                info=[\n                    ERROR_MESSAGE_NOTE,\n                    ERROR_MESSAGE_PROBLEMS_SECTION_START_NETWORK_ISSUES,\n                    ERROR_MESSAGE_BAD_REVISION.format(revision=refspec.key),\n                ],\n                exception=e,\n            )\n\n        return local\n\n    @classmethod\n    def _clone_submodules(cls, repo: Repo) -> None:\n        \"\"\"\n        Helper method to identify configured submodules and clone them recursively.\n        \"\"\"\n        repo_root = Path(repo.path)\n        for submodule in cls._get_submodules(repo):\n            path_absolute = repo_root / submodule.path\n            source_root = path_absolute.parent\n            source_root.mkdir(parents=True, exist_ok=True)\n            cls.clone(\n                url=submodule.url,\n                source_root=source_root,\n                name=path_absolute.name,\n                revision=submodule.revision,\n                clean=path_absolute.exists()\n                and not path_absolute.joinpath(\".git\").is_dir(),\n            )\n\n    @classmethod\n    def _get_submodules(cls, repo: Repo) -> list[SubmoduleInfo]:\n        modules_config = Path(repo.path, \".gitmodules\")\n\n        if not modules_config.exists():\n            return []\n\n        config = ConfigFile.from_path(str(modules_config))\n\n        submodules: list[SubmoduleInfo] = []\n        for path, url, name in parse_submodules(config):\n            url_str = url.decode(\"utf-8\")\n            path_str = path.decode(\"utf-8\")\n            name_str = name.decode(\"utf-8\")\n\n            if RELATIVE_SUBMODULE_REGEX.search(url_str):\n                url_str = urlpathjoin(f\"{cls.get_remote_url(repo)}/\", url_str)\n\n            with repo:\n                index = repo.open_index()\n\n                try:\n                    entry = index[path]\n                except KeyError:\n                    logger.debug(\n                        \"Skip submodule %s in %s, path %s not found\",\n                        name,\n                        repo.path,\n                        path,\n                    )\n                    continue\n\n                assert isinstance(entry, IndexEntry)\n                revision = entry.sha.decode(\"utf-8\")\n\n            submodules.append(\n                SubmoduleInfo(\n                    path=path_str,\n                    url=url_str,\n                    name=name_str,\n                    revision=revision,\n                )\n            )\n\n        return submodules\n\n    @staticmethod\n    def is_using_legacy_client() -> bool:\n        from poetry.config.config import Config\n\n        legacy_client: bool = Config.create().get(\"system-git-client\", False)\n        return legacy_client\n\n    @staticmethod\n    def get_default_source_root() -> Path:\n        from poetry.config.config import Config\n\n        return Path(Config.create().get(\"cache-dir\")) / \"src\"\n\n    @classmethod\n    def clone(\n        cls,\n        url: str,\n        name: str | None = None,\n        branch: str | None = None,\n        tag: str | None = None,\n        revision: str | None = None,\n        source_root: Path | None = None,\n        clean: bool = False,\n    ) -> Repo:\n        source_root = source_root or cls.get_default_source_root()\n        source_root.mkdir(parents=True, exist_ok=True)\n\n        name = name or cls.get_name_from_source_url(url=url)\n        target = source_root / name\n        refspec = GitRefSpec(branch=branch, revision=revision, tag=tag)\n\n        if target.exists():\n            if clean:\n                # force clean the local copy if it exists, do not reuse\n                remove_directory(target, force=True)\n            else:\n                # check if the current local copy matches the requested ref spec\n                try:\n                    current_repo = Repo(str(target))\n\n                    with current_repo:\n                        # we use peeled sha here to ensure tags are resolved consistently\n                        current_sha = current_repo.get_peeled(Ref(b\"HEAD\")).decode(\n                            \"utf-8\"\n                        )\n                except (NotGitRepository, AssertionError, KeyError):\n                    # something is wrong with the current checkout, clean it\n                    remove_directory(target, force=True)\n                else:\n                    if not is_revision_sha(revision=current_sha):\n                        # head is not a sha, this will cause issues later, lets reset\n                        remove_directory(target, force=True)\n                    elif (\n                        refspec.is_sha\n                        and refspec.revision is not None\n                        and current_sha.startswith(refspec.revision)\n                    ):\n                        # if revision is used short-circuit remote fetch head matches\n                        return current_repo\n\n        try:\n            if not cls.is_using_legacy_client():\n                local = cls._clone(url=url, refspec=refspec, target=target)\n                cls._clone_submodules(repo=local)\n                return local\n        except HTTPUnauthorized:\n            # we do this here to handle http authenticated repositories as dulwich\n            # does not currently support using credentials from git-credential helpers.\n            # upstream issue: https://github.com/jelmer/dulwich/issues/873\n            #\n            # this is a little inefficient, however preferred as this is transparent\n            # without additional configuration or changes for existing projects that\n            # use http basic auth credentials.\n            logger.debug(\n                \"Unable to fetch from private repository '%s', falling back to\"\n                \" system git\",\n                url,\n            )\n\n        # fallback to legacy git client\n        return cls._clone_legacy(url=url, refspec=refspec, target=target)\n\n\ndef urlpathjoin(base: str, path: str) -> str:\n    \"\"\"\n    Allow any URL to be joined with a path\n\n    This works around an issue with urllib.parse.urljoin where it only handles\n    relative URLs for protocols contained in urllib.parse.uses_relative. As it\n    happens common protocols used with git, like ssh or git+ssh are not in that\n    list.\n\n    Thus we need to implement our own version of urljoin that handles all URLs\n    protocols. This is accomplished by using urlparse and urlunparse to split\n    the URL into its components, join the path, and then reassemble the URL.\n\n    See: https://github.com/python-poetry/poetry/issues/6499#issuecomment-1564712609\n    \"\"\"\n    parsed_base = urlparse(base)\n    new = parsed_base._replace(path=urljoin(parsed_base.path, path))\n    return urlunparse(new)\n\n\n@dataclasses.dataclass\nclass SubmoduleInfo:\n    path: str\n    url: str\n    name: str\n    revision: str\n"
  },
  {
    "path": "src/poetry/vcs/git/system.py",
    "content": "from __future__ import annotations\n\nimport os\nimport subprocess\n\nfrom typing import TYPE_CHECKING\n\nfrom dulwich.client import find_git_command\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n    from typing import Any\n\n\nclass SystemGit:\n    @classmethod\n    def clone(cls, repository: str, dest: Path) -> None:\n        cls._check_parameter(repository)\n\n        cls.run(\"clone\", \"--recurse-submodules\", \"--\", repository, str(dest))\n\n    @classmethod\n    def checkout(cls, rev: str, target: Path | None = None) -> None:\n        cls._check_parameter(rev)\n        cls.run(\"checkout\", rev, folder=target)\n\n    @staticmethod\n    def run(*args: Any, **kwargs: Any) -> None:\n        folder = kwargs.pop(\"folder\", None)\n        if folder:\n            args = (\n                \"--git-dir\",\n                (folder / \".git\").as_posix(),\n                \"--work-tree\",\n                folder.as_posix(),\n                *args,\n            )\n\n        git_command = find_git_command()\n        env = os.environ.copy()\n        env[\"GIT_TERMINAL_PROMPT\"] = \"0\"\n\n        subprocess.run(\n            git_command + list(args),\n            capture_output=True,\n            env=env,\n            text=True,\n            encoding=\"utf-8\",\n            check=True,\n        )\n\n    @staticmethod\n    def _check_parameter(parameter: str) -> None:\n        \"\"\"\n        Checks a git parameter to avoid unwanted code execution.\n        \"\"\"\n        if parameter.strip().startswith(\"-\"):\n            raise RuntimeError(f\"Invalid Git parameter: {parameter}\")\n"
  },
  {
    "path": "src/poetry/version/__init__.py",
    "content": ""
  },
  {
    "path": "src/poetry/version/version_selector.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import Package\n\n    from poetry.repositories import RepositoryPool\n\n\nclass VersionSelector:\n    def __init__(self, pool: RepositoryPool) -> None:\n        self._pool = pool\n\n    def find_best_candidate(\n        self,\n        package_name: str,\n        target_package_version: str | None = None,\n        allow_prereleases: bool | None = None,\n        source: str | None = None,\n    ) -> Package | None:\n        \"\"\"\n        Given a package name and optional version,\n        returns the latest Package that matches\n        \"\"\"\n        from poetry.factory import Factory\n\n        dependency = Factory.create_dependency(\n            package_name,\n            {\n                \"version\": target_package_version or \"*\",\n                \"allow-prereleases\": allow_prereleases,\n                \"source\": source,\n            },\n        )\n        candidates = self._pool.find_packages(dependency)\n        only_prereleases = all(c.version.is_unstable() for c in candidates)\n\n        if not candidates:\n            return None\n\n        package = None\n        for candidate in candidates:\n            if (\n                candidate.is_prerelease()\n                and not dependency.allows_prereleases()\n                and not only_prereleases\n            ):\n                continue\n\n            # Select highest version of the two\n            if package is None or package.version < candidate.version:\n                package = candidate\n\n        return package\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/config/__init__.py",
    "content": ""
  },
  {
    "path": "tests/config/test_config.py",
    "content": "from __future__ import annotations\n\nimport json\nimport os\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom deepdiff.diff import DeepDiff\n\nfrom poetry.config.config import Config\nfrom poetry.config.config import boolean_normalizer\nfrom poetry.config.config import int_normalizer\nfrom poetry.utils.password_manager import PasswordManager\nfrom tests.helpers import flatten_dict\nfrom tests.helpers import isolated_environment\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n\n    from tests.conftest import DummyBackend\n\n    Normalizer = Callable[[str], Any]\n\n\ndef get_options_based_on_normalizer(normalizer: Normalizer) -> Iterator[str]:\n    flattened_config = flatten_dict(obj=Config.default_config, delimiter=\".\")\n\n    for k in flattened_config:\n        if Config._get_normalizer(k) == normalizer:\n            yield k\n\n\n@pytest.mark.parametrize(\n    (\"name\", \"value\"),\n    [\n        (\"installer.parallel\", True),\n        (\"virtualenvs.create\", True),\n        (\"requests.max-retries\", 0),\n    ],\n)\ndef test_config_get_default_value(config: Config, name: str, value: bool) -> None:\n    assert config.get(name) is value\n\n\ndef test_config_get_processes_depended_on_values(\n    config: Config, config_cache_dir: Path\n) -> None:\n    assert str(config_cache_dir / \"virtualenvs\") == config.get(\"virtualenvs.path\")\n\n\ndef generate_environment_variable_tests() -> Iterator[tuple[str, str, str, bool]]:\n    data: list[tuple[Normalizer, list[tuple[str, Any]]]] = [\n        (\n            boolean_normalizer,\n            [\n                (\"true\", True),\n                (\"false\", False),\n                (\"True\", True),\n                (\"False\", False),\n                (\"1\", True),\n                (\"0\", False),\n            ],\n        ),\n        (int_normalizer, [(\"4\", 4), (\"2\", 2)]),\n    ]\n\n    for normalizer, values in data:\n        for env_value, value in values:\n            for name in get_options_based_on_normalizer(normalizer=normalizer):\n                env_var = \"POETRY_\" + re.sub(\"[.-]+\", \"_\", name).upper()\n                yield name, env_var, env_value, value\n\n\n@pytest.mark.parametrize(\n    (\"name\", \"env_var\", \"env_value\", \"value\"),\n    list(generate_environment_variable_tests()),\n)\ndef test_config_get_from_environment_variable(\n    config: Config,\n    environ: Iterator[None],\n    name: str,\n    env_var: str,\n    env_value: str,\n    value: bool,\n) -> None:\n    os.environ[env_var] = env_value\n    assert config.get(name) is value\n\n\ndef test_config_get_from_environment_variable_nested(\n    config: Config,\n    environ: Iterator[None],\n) -> None:\n    options = config.default_config[\"virtualenvs\"][\"options\"]\n    expected = {}\n\n    for k, v in options.items():\n        if isinstance(v, bool):\n            expected[k] = not v\n            os.environ[f\"POETRY_VIRTUALENVS_OPTIONS_{k.upper().replace('-', '_')}\"] = (\n                \"true\" if expected[k] else \"false\"\n            )\n\n    assert config.get(\"virtualenvs.options\") == expected\n\n\n@pytest.mark.parametrize(\n    (\"path_config\", \"expected\"),\n    [(\"~/.venvs\", Path.home() / \".venvs\"), (\"venv\", Path(\"venv\"))],\n)\ndef test_config_expands_tilde_for_virtualenvs_path(\n    config: Config, path_config: str, expected: Path\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": path_config}})\n    assert config.virtualenvs_path == expected\n\n\ndef test_disabled_keyring_is_unavailable(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    manager = PasswordManager(config)\n    assert manager.use_keyring\n\n    config.config[\"keyring\"][\"enabled\"] = False\n    manager = PasswordManager(config)\n    assert not manager.use_keyring\n\n\n@pytest.mark.parametrize(\n    (\"value\", \"invalid\"),\n    [\n        # non-serialised json\n        (\"BAD=VALUE\", True),\n        # good value\n        (json.dumps({\"CC\": \"gcc\", \"--build-option\": [\"--one\", \"--two\"]}), False),\n        # non-string key\n        ('{0: \"hello\"}', True),\n        # non-string value in list\n        ('{\"world\": [\"hello\", 0]}', True),\n    ],\n)\ndef test_config_get_from_environment_variable_build_config_settings(\n    value: str,\n    invalid: bool,\n    config: Config,\n) -> None:\n    with isolated_environment(\n        {\n            \"POETRY_INSTALLER_BUILD_CONFIG_SETTINGS_DEMO\": value,\n        },\n        clear=True,\n    ):\n        configured_value = config.get(\"installer.build-config-settings.demo\")\n\n        if invalid:\n            assert configured_value is None\n        else:\n            assert not DeepDiff(configured_value, json.loads(value))\n"
  },
  {
    "path": "tests/config/test_config_source.py",
    "content": "from __future__ import annotations\n\nfrom typing import Any\n\nimport pytest\n\nfrom poetry.config.config_source import UNSET\nfrom poetry.config.config_source import ConfigSourceMigration\nfrom poetry.config.config_source import drop_empty_config_category\nfrom poetry.config.dict_config_source import DictConfigSource\n\n\n@pytest.mark.parametrize(\n    [\"config_data\", \"expected\"],\n    [\n        (\n            {\n                \"category_a\": {\n                    \"category_b\": {\n                        \"category_c\": {},\n                    },\n                },\n                \"system-git-client\": True,\n            },\n            {\"system-git-client\": True},\n        ),\n        (\n            {\n                \"category_a\": {\n                    \"category_b\": {\n                        \"category_c\": {},\n                        \"category_d\": {\"some_config\": True},\n                    },\n                },\n                \"system-git-client\": True,\n            },\n            {\n                \"category_a\": {\n                    \"category_b\": {\n                        \"category_d\": {\"some_config\": True},\n                    }\n                },\n                \"system-git-client\": True,\n            },\n        ),\n    ],\n)\ndef test_drop_empty_config_category(\n    config_data: dict[Any, Any], expected: dict[Any, Any]\n) -> None:\n    assert (\n        drop_empty_config_category(\n            keys=[\"category_a\", \"category_b\", \"category_c\"], config=config_data\n        )\n        == expected\n    )\n\n\ndef test_config_source_migration_rename_key() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"prefer-active-python\": True,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    migration = ConfigSourceMigration(\n        old_key=\"virtualenvs.prefer-active-python\",\n        new_key=\"virtualenvs.use-poetry-python\",\n    )\n\n    migration.apply(config_source)\n\n    config_source._config = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": True,\n        },\n        \"system-git-client\": True,\n    }\n\n\ndef test_config_source_migration_remove_key() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"prefer-active-python\": True,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    migration = ConfigSourceMigration(\n        old_key=\"virtualenvs.prefer-active-python\",\n        new_key=None,\n    )\n\n    migration.apply(config_source)\n\n    config_source._config = {\n        \"virtualenvs\": {},\n        \"system-git-client\": True,\n    }\n\n\ndef test_config_source_migration_unset_value() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"prefer-active-python\": True,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    migration = ConfigSourceMigration(\n        old_key=\"virtualenvs.prefer-active-python\",\n        new_key=\"virtualenvs.use-poetry-python\",\n        value_migration={True: UNSET, False: True},\n    )\n\n    migration.apply(config_source)\n\n    config_source._config = {\n        \"virtualenvs\": {},\n        \"system-git-client\": True,\n    }\n\n\ndef test_config_source_migration_complex_migration() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"prefer-active-python\": True,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    migration = ConfigSourceMigration(\n        old_key=\"virtualenvs.prefer-active-python\",\n        new_key=\"virtualenvs.use-poetry-python\",\n        value_migration={True: None, False: True},\n    )\n\n    migration.apply(config_source)\n\n    config_source._config = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": None,\n        },\n        \"system-git-client\": True,\n    }\n"
  },
  {
    "path": "tests/config/test_dict_config_source.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.config.config_source import PropertyNotFoundError\nfrom poetry.config.dict_config_source import DictConfigSource\n\n\ndef test_dict_config_source_add_property() -> None:\n    config_source = DictConfigSource()\n    assert config_source._config == {}\n\n    config_source.add_property(\"system-git-client\", True)\n    assert config_source._config == {\"system-git-client\": True}\n\n    config_source.add_property(\"virtualenvs.use-poetry-python\", False)\n    assert config_source._config == {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n\ndef test_dict_config_source_remove_property() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    config_source.remove_property(\"system-git-client\")\n    assert config_source._config == {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        }\n    }\n\n    config_source.remove_property(\"virtualenvs.use-poetry-python\")\n    assert config_source._config == {\"virtualenvs\": {}}\n\n\ndef test_dict_config_source_get_property() -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n    config_source = DictConfigSource()\n    config_source._config = config_data\n\n    assert config_source.get_property(\"virtualenvs.use-poetry-python\") is False\n    assert config_source.get_property(\"system-git-client\") is True\n\n\ndef test_dict_config_source_get_property_should_raise_if_not_found() -> None:\n    config_source = DictConfigSource()\n\n    with pytest.raises(\n        PropertyNotFoundError, match=r\"Key virtualenvs\\.use-poetry-python not in config\"\n    ):\n        _ = config_source.get_property(\"virtualenvs.use-poetry-python\")\n"
  },
  {
    "path": "tests/config/test_file_config_source.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\nimport tomlkit\n\nfrom poetry.config.config_source import PropertyNotFoundError\nfrom poetry.config.file_config_source import FileConfigSource\nfrom poetry.toml import TOMLFile\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\ndef test_file_config_source_add_property(tmp_path: Path) -> None:\n    config = tmp_path.joinpath(\"config.toml\")\n    config.touch()\n\n    config_source = FileConfigSource(TOMLFile(config))\n\n    assert config_source._file.read() == {}\n\n    config_source.add_property(\"system-git-client\", True)\n    assert config_source._file.read() == {\"system-git-client\": True}\n\n    config_source.add_property(\"virtualenvs.use-poetry-python\", False)\n    assert config_source._file.read() == {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n\ndef test_file_config_source_remove_property(tmp_path: Path) -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n    config = tmp_path.joinpath(\"config.toml\")\n    with config.open(mode=\"w\", encoding=\"utf-8\") as f:\n        f.write(tomlkit.dumps(config_data))\n\n    config_source = FileConfigSource(TOMLFile(config))\n\n    config_source.remove_property(\"system-git-client\")\n    assert config_source._file.read() == {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        }\n    }\n\n    config_source.remove_property(\"virtualenvs.use-poetry-python\")\n    assert config_source._file.read() == {}\n\n\ndef test_file_config_source_get_property(tmp_path: Path) -> None:\n    config_data = {\n        \"virtualenvs\": {\n            \"use-poetry-python\": False,\n        },\n        \"system-git-client\": True,\n    }\n\n    config = tmp_path.joinpath(\"config.toml\")\n    with config.open(mode=\"w\", encoding=\"utf-8\") as f:\n        f.write(tomlkit.dumps(config_data))\n\n    config_source = FileConfigSource(TOMLFile(config))\n\n    assert config_source.get_property(\"virtualenvs.use-poetry-python\") is False\n    assert config_source.get_property(\"system-git-client\") is True\n\n\ndef test_file_config_source_get_property_should_raise_if_not_found(\n    tmp_path: Path,\n) -> None:\n    config = tmp_path.joinpath(\"config.toml\")\n    config.touch()\n\n    config_source = FileConfigSource(TOMLFile(config))\n\n    with pytest.raises(\n        PropertyNotFoundError, match=r\"Key virtualenvs\\.use-poetry-python not in config\"\n    ):\n        _ = config_source.get_property(\"virtualenvs.use-poetry-python\")\n"
  },
  {
    "path": "tests/config/test_source.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom tomlkit.container import Container\nfrom tomlkit.items import Table\nfrom tomlkit.items import Trivia\n\nfrom poetry.config.source import Source\nfrom poetry.repositories.repository_pool import Priority\n\n\n@pytest.mark.parametrize(\n    \"source,table_body\",\n    [\n        (\n            Source(\"foo\", \"https://example.com\"),\n            {\n                \"name\": \"foo\",\n                \"priority\": \"primary\",\n                \"url\": \"https://example.com\",\n            },\n        ),\n        (\n            Source(\"bar\", \"https://example.com/bar\", priority=Priority.EXPLICIT),\n            {\n                \"name\": \"bar\",\n                \"priority\": \"explicit\",\n                \"url\": \"https://example.com/bar\",\n            },\n        ),\n    ],\n)\ndef test_source_to_table(source: Source, table_body: dict[str, str | bool]) -> None:\n    table = Table(Container(), Trivia(), False)\n    table._value = table_body  # type: ignore[assignment]\n\n    assert source.to_toml_table() == table\n\n\ndef test_source_default_is_primary() -> None:\n    source = Source(\"foo\", \"https://example.com\")\n    assert source.priority == Priority.PRIMARY\n\n\n@pytest.mark.parametrize(\n    (\"priority\", \"expected_priority\"),\n    [\n        (\"supplemental\", Priority.SUPPLEMENTAL),\n        (\"SUPPLEMENTAL\", Priority.SUPPLEMENTAL),\n    ],\n)\ndef test_source_priority_as_string(priority: str, expected_priority: Priority) -> None:\n    source = Source(\n        \"foo\",\n        \"https://example.com\",\n        priority=priority,  # type: ignore[arg-type]\n    )\n    assert source.priority == Priority.SUPPLEMENTAL\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport logging\nimport os\nimport platform\nimport re\nimport shutil\nimport sys\n\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport findpython\nimport keyring\nimport packaging.version\nimport pytest\nimport responses\n\nfrom installer.utils import SCHEME_NAMES\nfrom jaraco.classes import properties\nfrom keyring.backend import KeyringBackend\nfrom keyring.backends.fail import Keyring as FailKeyring\nfrom keyring.credentials import SimpleCredential\nfrom keyring.errors import KeyringError\nfrom keyring.errors import KeyringLocked\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.utils._compat import WINDOWS\nfrom poetry.core.version.markers import parse_marker\nfrom pytest import FixtureRequest\n\nfrom poetry.config.config import Config as BaseConfig\nfrom poetry.config.dict_config_source import DictConfigSource\nfrom poetry.console.commands.command import Command\nfrom poetry.factory import Factory\nfrom poetry.layouts import layout\nfrom poetry.packages.direct_origin import _get_package_from_git\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.utils.cache import ArtifactCache\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env import SystemEnv\nfrom poetry.utils.env import VirtualEnv\nfrom poetry.utils.env.python import Python\nfrom poetry.utils.password_manager import PoetryKeyring\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import TestLocker\nfrom tests.helpers import TestRepository\nfrom tests.helpers import get_package\nfrom tests.helpers import http_setup_redirect\nfrom tests.helpers import isolated_environment\nfrom tests.helpers import mock_clone\nfrom tests.helpers import set_keyring_backend\nfrom tests.helpers import switch_working_directory\nfrom tests.helpers import with_working_directory\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n    from collections.abc import Mapping\n    from typing import Any\n    from unittest.mock import MagicMock\n\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from keyring.credentials import Credential\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.package import Package\n    from pytest import Config as PyTestConfig\n    from pytest import Parser\n    from pytest import TempPathFactory\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env.base_env import PythonVersion\n    from tests.types import CommandFactory\n    from tests.types import FixtureCopier\n    from tests.types import FixtureDirGetter\n    from tests.types import MockedPoetryPythonRegister\n    from tests.types import MockedPythonRegister\n    from tests.types import PackageFactory\n    from tests.types import ProjectFactory\n    from tests.types import SetProjectContext\n\n\npytest_plugins = [\n    \"tests.repositories.fixtures\",\n]\n\n\ndef pytest_addoption(parser: Parser) -> None:\n    parser.addoption(\n        \"--integration\",\n        action=\"store_true\",\n        dest=\"integration\",\n        default=False,\n        help=\"enable integration tests\",\n    )\n\n\ndef pytest_configure(config: PyTestConfig) -> None:\n    config.addinivalue_line(\"markers\", \"integration: mark integration tests\")\n\n    if not config.option.integration:\n        if config.option.markexpr:\n            config.option.markexpr += \" and not integration\"\n        else:\n            config.option.markexpr = \"not integration\"\n\n\nclass Config(BaseConfig):\n    _config_source: DictConfigSource\n    _auth_config_source: DictConfigSource\n\n    def get(self, setting_name: str, default: Any = None) -> Any:\n        self.merge(self._config_source.config)\n        self.merge(self._auth_config_source.config)\n\n        return super().get(setting_name, default=default)\n\n    def raw(self) -> dict[str, Any]:\n        self.merge(self._config_source.config)\n        self.merge(self._auth_config_source.config)\n\n        return super().raw()\n\n    def all(self) -> dict[str, Any]:\n        self.merge(self._config_source.config)\n        self.merge(self._auth_config_source.config)\n\n        return super().all()\n\n\nclass DummyBackend(KeyringBackend):\n    def __init__(self) -> None:\n        self._passwords: dict[str, dict[str, str]] = {}\n        self._service_defaults: dict[str, Credential] = {}\n\n    @properties.classproperty\n    def priority(self) -> float:\n        return 42\n\n    def set_password(self, service: str, username: str, password: str) -> None:\n        self._passwords[service] = {username: password}\n\n    def get_password(self, service: str, username: str) -> str | None:\n        return self._passwords.get(service, {}).get(username)\n\n    def get_credential(\n        self,\n        service: str,\n        username: str | None,\n    ) -> Credential | None:\n        if username is None:\n            credential = self._service_defaults.get(service)\n            return credential\n\n        password = self.get_password(service, username)\n        if password is None:\n            return None\n\n        return SimpleCredential(username, password)\n\n    def delete_password(self, service: str, username: str) -> None:\n        if service in self._passwords and username in self._passwords[service]:\n            del self._passwords[service][username]\n\n    def set_default_service_credential(\n        self, service: str, credential: Credential\n    ) -> None:\n        self._service_defaults[service] = credential\n\n\nclass LockedBackend(KeyringBackend):\n    @properties.classproperty\n    def priority(self) -> float:\n        return 42\n\n    def set_password(self, service: str, username: str, password: str) -> None:\n        raise KeyringLocked()\n\n    def get_password(self, service: str, username: str) -> str | None:\n        raise KeyringLocked()\n\n    def get_credential(\n        self,\n        service: str,\n        username: str | None,\n    ) -> Credential | None:\n        raise KeyringLocked()\n\n    def delete_password(self, service: str, username: str) -> None:\n        raise KeyringLocked()\n\n\nclass ErroneousBackend(FailKeyring):\n    @properties.classproperty\n    def priority(self) -> float:\n        return 42\n\n    def get_credential(\n        self,\n        service: str,\n        username: str | None,\n    ) -> Credential | None:\n        raise KeyringError()\n\n\n@pytest.fixture()\ndef poetry_keyring() -> PoetryKeyring:\n    return PoetryKeyring(\"poetry-repository\")\n\n\n@pytest.fixture()\ndef dummy_keyring() -> DummyBackend:\n    return DummyBackend()\n\n\n@pytest.fixture()\ndef with_simple_keyring(dummy_keyring: DummyBackend) -> None:\n    set_keyring_backend(dummy_keyring)\n\n\n@pytest.fixture()\ndef with_fail_keyring() -> None:\n    set_keyring_backend(FailKeyring())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture()\ndef with_locked_keyring() -> None:\n    set_keyring_backend(LockedBackend())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture()\ndef with_erroneous_keyring() -> None:\n    set_keyring_backend(ErroneousBackend())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture()\ndef with_null_keyring() -> None:\n    from keyring.backends.null import Keyring\n\n    set_keyring_backend(Keyring())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture()\ndef with_chained_fail_keyring(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"keyring.backend.get_all_keyring\",\n        lambda: [FailKeyring()],  # type: ignore[no-untyped-call]\n    )\n    from keyring.backends.chainer import ChainerBackend\n\n    set_keyring_backend(ChainerBackend())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture()\ndef with_chained_null_keyring(mocker: MockerFixture) -> None:\n    from keyring.backends.null import Keyring\n\n    mocker.patch(\n        \"keyring.backend.get_all_keyring\",\n        lambda: [Keyring()],  # type: ignore[no-untyped-call]\n    )\n    from keyring.backends.chainer import ChainerBackend\n\n    set_keyring_backend(ChainerBackend())  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture\ndef config_cache_dir(tmp_path: Path) -> Path:\n    path = tmp_path / \".cache\" / \"pypoetry\"\n    path.mkdir(parents=True)\n    return path\n\n\n@pytest.fixture\ndef config_data_dir(tmp_path: Path) -> Path:\n    path = tmp_path / \".local\" / \"share\" / \"pypoetry\"\n    path.mkdir(parents=True)\n    return path\n\n\n@pytest.fixture\ndef config_virtualenvs_path(config_cache_dir: Path) -> Path:\n    return config_cache_dir / \"virtualenvs\"\n\n\n@pytest.fixture\ndef config_source(config_cache_dir: Path, config_data_dir: Path) -> DictConfigSource:\n    source = DictConfigSource()\n    source.add_property(\"cache-dir\", str(config_cache_dir))\n    source.add_property(\"data-dir\", str(config_data_dir))\n\n    return source\n\n\n@pytest.fixture\ndef auth_config_source() -> DictConfigSource:\n    source = DictConfigSource()\n\n    return source\n\n\n@pytest.fixture(autouse=True)\ndef config(\n    config_source: DictConfigSource,\n    auth_config_source: DictConfigSource,\n    mocker: MockerFixture,\n) -> Config:\n    keyring.set_keyring(FailKeyring())  # type: ignore[no-untyped-call]\n\n    c = Config()\n    c.merge(config_source.config)\n    c.set_config_source(config_source)\n    c.set_auth_config_source(auth_config_source)\n\n    mocker.patch(\"poetry.config.config.Config.create\", return_value=c)\n    mocker.patch(\"poetry.config.config.Config.set_config_source\")\n\n    return c\n\n\n@pytest.fixture\ndef artifact_cache(config: Config) -> ArtifactCache:\n    return ArtifactCache(cache_dir=config.artifacts_cache_directory)\n\n\n@pytest.fixture()\ndef config_dir(tmp_path: Path) -> Path:\n    path = tmp_path / \"config\"\n    path.mkdir()\n    return path\n\n\n@pytest.fixture(autouse=True)\ndef mock_user_config_dir(mocker: MockerFixture, config_dir: Path) -> None:\n    mocker.patch(\"poetry.locations.CONFIG_DIR\", new=config_dir)\n    mocker.patch(\"poetry.config.config.CONFIG_DIR\", new=config_dir)\n\n\n@pytest.fixture\ndef environ() -> Iterator[None]:\n    with isolated_environment():\n        yield\n\n\n@pytest.fixture(autouse=True)\ndef isolate_environ() -> Iterator[None]:\n    \"\"\"Ensure the environment is isolated from user configuration.\"\"\"\n    with isolated_environment():\n        for var in os.environ:\n            if var.startswith(\"POETRY_\") or var in {\"PYTHONPATH\", \"VIRTUAL_ENV\"}:\n                del os.environ[var]\n\n        yield\n\n\n@pytest.fixture(autouse=True)\ndef git_mock(mocker: MockerFixture, request: FixtureRequest) -> None:\n    if request.node.get_closest_marker(\"skip_git_mock\"):\n        return\n\n    # Patch git module to not actually clone projects\n    mocker.patch(\"poetry.vcs.git.Git.clone\", new=mock_clone)\n    p = mocker.patch(\"poetry.vcs.git.Git.get_revision\")\n    p.return_value = MOCK_DEFAULT_GIT_REVISION\n\n    _get_package_from_git.cache_clear()\n\n\n@pytest.fixture\ndef http() -> Iterator[responses.RequestsMock]:\n    with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:\n        yield rsps\n\n\n@pytest.fixture\ndef http_redirector(http: responses.RequestsMock) -> None:\n    http_setup_redirect(\n        http, responses.HEAD, responses.GET, responses.PUT, responses.POST\n    )\n\n\n@pytest.fixture\ndef project_root() -> Path:\n    return Path(__file__).parent.parent\n\n\n@pytest.fixture(scope=\"session\")\ndef fixture_base() -> Path:\n    return Path(__file__).parent / \"fixtures\"\n\n\n@pytest.fixture(scope=\"session\")\ndef fixture_dir(fixture_base: Path) -> FixtureDirGetter:\n    def _fixture_dir(name: str) -> Path:\n        return fixture_base / name\n\n    return _fixture_dir\n\n\n@pytest.fixture\ndef tmp_venv(tmp_path: Path) -> Iterator[VirtualEnv]:\n    venv_path = tmp_path / \"venv\"\n\n    EnvManager.build_venv(venv_path)\n\n    venv = VirtualEnv(venv_path)\n    yield venv\n\n    shutil.rmtree(venv.path)\n\n\n@pytest.fixture\ndef installed() -> InstalledRepository:\n    return InstalledRepository()\n\n\n@pytest.fixture(scope=\"session\")\ndef current_env() -> SystemEnv:\n    return SystemEnv(Path(sys.executable))\n\n\n@pytest.fixture(scope=\"session\")\ndef current_python(current_env: SystemEnv) -> PythonVersion:\n    return current_env.version_info\n\n\n@pytest.fixture(scope=\"session\")\ndef default_python(current_python: PythonVersion) -> str:\n    return \"^\" + \".\".join(str(v) for v in current_python[:2])\n\n\n@pytest.fixture\ndef repo(http: responses.RequestsMock) -> TestRepository:\n    http.get(re.compile(r\"^https?://foo\\.bar/(.+?)$\"))\n    return TestRepository(name=\"foo\")\n\n\n@pytest.fixture\ndef project_factory(\n    tmp_path: Path,\n    config: Config,\n    repo: TestRepository,\n    installed: InstalledRepository,\n    default_python: str,\n    load_required_fixtures: None,\n) -> ProjectFactory:\n    workspace = tmp_path\n\n    def _factory(\n        name: str | None = None,\n        dependencies: Mapping[str, str] | None = None,\n        dev_dependencies: Mapping[str, str] | None = None,\n        pyproject_content: str | None = None,\n        poetry_lock_content: str | None = None,\n        install_deps: bool = True,\n        source: Path | None = None,\n        locker_config: dict[str, Any] | None = None,\n        use_test_locker: bool = True,\n    ) -> Poetry:\n        project_dir = workspace / f\"poetry-fixture-{name}\"\n        dependencies = dependencies or {}\n        dev_dependencies = dev_dependencies or {}\n\n        if pyproject_content or source:\n            if source:\n                project_dir.parent.mkdir(parents=True, exist_ok=True)\n                shutil.copytree(source, project_dir)\n            else:\n                project_dir.mkdir(parents=True, exist_ok=True)\n\n            if pyproject_content:\n                with (project_dir / \"pyproject.toml\").open(\"w\", encoding=\"utf-8\") as f:\n                    f.write(pyproject_content)\n        else:\n            assert name is not None\n            layout(\"src\")(\n                name,\n                \"0.1.0\",\n                author=\"PyTest Tester <mc.testy@testface.com>\",\n                readme_format=\"md\",\n                python=default_python,\n                dependencies=dependencies,\n                dev_dependencies=dev_dependencies,\n            ).create(project_dir, with_tests=False)\n\n        if poetry_lock_content:\n            lock_file = project_dir / \"poetry.lock\"\n            lock_file.write_text(data=poetry_lock_content, encoding=\"utf-8\")\n\n        poetry = Factory().create_poetry(project_dir)\n\n        if use_test_locker:\n            locker = TestLocker(\n                poetry.locker.lock, locker_config or poetry.locker._pyproject_data\n            )\n            locker.write()\n\n            poetry.set_locker(locker)\n\n        poetry.set_config(config)\n\n        pool = RepositoryPool()\n        pool.add_repository(repo)\n\n        poetry.set_pool(pool)\n\n        if install_deps:\n            for deps in [dependencies, dev_dependencies]:\n                for name, version in deps.items():\n                    pkg = get_package(name, version)\n                    repo.add_package(pkg)\n                    installed.add_package(pkg)\n\n        return poetry\n\n    return _factory\n\n\n@pytest.fixture\ndef create_package(repo: Repository) -> PackageFactory:\n    \"\"\"\n    This function is a pytest fixture that creates a factory function to generate\n    and customize package objects. These packages are added to the default repository\n    fixture and configured with specific versions, optional extras, and self-referenced\n    extras. This helps in setting up package dependencies for testing purposes.\n\n    :return: A factory function that can be used to create and configure packages.\n    \"\"\"\n\n    def create_new_package(\n        name: str,\n        version: str | None = None,\n        dependencies: list[Dependency] | None = None,\n        extras: dict[str, list[str]] | None = None,\n        merge_extras: bool = False,\n    ) -> Package:\n        version = version or \"1.0\"\n        package = get_package(name, version)\n\n        package_extras: dict[NormalizedName, list[Dependency]] = {}\n\n        for extra, extra_dependencies in (extras or {}).items():\n            extra = canonicalize_name(extra)\n\n            if extra not in package_extras:\n                package_extras[extra] = []\n\n            for extra_dependency_spec in extra_dependencies:\n                extra_dependency = Dependency.create_from_pep_508(extra_dependency_spec)\n                extra_dependency._optional = True\n                extra_dependency.marker = extra_dependency.marker.intersect(\n                    parse_marker(f\"extra == '{extra}'\")\n                )\n\n                if extra_dependency.name != package.name:\n                    assert extra_dependency.constraint.allows(package.version)\n\n                    # if it is not a self-referencing dependency, make sure we add it to the repo\n                    try:\n                        pkg = repo.package(extra_dependency.name, package.version)\n                    except PackageNotFoundError:\n                        pkg = get_package(extra_dependency.name, str(package.version))\n                        repo.add_package(pkg)\n\n                    extra_dependency.constraint = parse_constraint(f\"^{pkg.version}\")\n\n                    if merge_extras:\n                        # if requirement already exists in the package,\n                        # update the marker\n                        for requirement in package.requires:\n                            if (\n                                requirement.name == extra_dependency.name\n                                and requirement.is_optional()\n                            ):\n                                requirement.marker = requirement.marker.union(\n                                    extra_dependency.marker\n                                )\n                                break\n                        else:\n                            package.add_dependency(extra_dependency)\n                    else:\n                        package.add_dependency(extra_dependency)\n\n                package_extras[extra].append(extra_dependency)\n\n        package.extras = package_extras\n\n        for dependency in dependencies or []:\n            package.add_dependency(dependency)\n\n        repo.add_package(package)\n\n        return package\n\n    return create_new_package\n\n\n@pytest.fixture(autouse=True)\ndef set_simple_log_formatter() -> None:\n    \"\"\"\n    This fixture removes any formatting added via IOFormatter.\n    \"\"\"\n    for name in logging.Logger.manager.loggerDict:\n        for handler in logging.getLogger(name).handlers:\n            # replace formatter with simple formatter for testing\n            handler.setFormatter(logging.Formatter(fmt=\"%(message)s\"))\n\n\n@pytest.fixture\ndef fixture_copier(fixture_base: Path, tmp_path: Path) -> FixtureCopier:\n    def _copy(relative_path: str, target: Path | None = None) -> Path:\n        path = fixture_base / relative_path\n        target = target or (tmp_path / relative_path)\n        target.parent.mkdir(parents=True, exist_ok=True)\n\n        if target.exists():\n            return target\n\n        if path.is_dir():\n            shutil.copytree(path, target)\n        else:\n            shutil.copyfile(path, target)\n\n        return target\n\n    return _copy\n\n\n@pytest.fixture\ndef required_fixtures() -> list[str]:\n    return []\n\n\n@pytest.fixture(autouse=True)\ndef load_required_fixtures(\n    required_fixtures: list[str], fixture_copier: FixtureCopier\n) -> None:\n    for fixture in required_fixtures:\n        fixture_copier(fixture)\n\n\n@pytest.fixture\ndef venv_flags_default() -> dict[str, bool]:\n    return {\n        \"always-copy\": False,\n        \"system-site-packages\": False,\n        \"no-pip\": False,\n    }\n\n\n@pytest.fixture\ndef disable_http_status_force_list(mocker: MockerFixture) -> Iterator[None]:\n    mocker.patch(\"poetry.utils.authenticator.STATUS_FORCELIST\", [])\n    yield\n\n\n@pytest.fixture(autouse=True)\ndef tmp_working_directory(tmp_path: Path) -> Iterator[Path]:\n    with switch_working_directory(tmp_path):\n        yield tmp_path\n\n\n@pytest.fixture(autouse=True, scope=\"session\")\ndef tmp_session_working_directory(tmp_path_factory: TempPathFactory) -> Iterator[Path]:\n    tmp_path = tmp_path_factory.mktemp(\"session-working-directory\")\n    with switch_working_directory(tmp_path):\n        yield tmp_path\n\n\n@pytest.fixture\ndef set_project_context(\n    tmp_working_directory: Path, tmp_path: Path, fixture_dir: FixtureDirGetter\n) -> SetProjectContext:\n    @contextlib.contextmanager\n    def project_context(project: str | Path, in_place: bool = False) -> Iterator[Path]:\n        if isinstance(project, str):\n            project = fixture_dir(project)\n\n        with with_working_directory(\n            source=project,\n            target=tmp_path.joinpath(project.name) if not in_place else None,\n        ) as path:\n            yield path\n\n    return project_context\n\n\n@pytest.fixture\ndef command_factory() -> CommandFactory:\n    \"\"\"\n    Provides a pytest fixture for creating mock commands using a factory function.\n\n    This fixture allows for customization of command attributes like name,\n    arguments, options, description, help text, and handler.\n    \"\"\"\n\n    def _command_factory(\n        command_name: str,\n        command_arguments: list[Argument] | None = None,\n        command_options: list[Option] | None = None,\n        command_description: str = \"\",\n        command_help: str = \"\",\n        command_handler: Callable[[Command], int] | str | None = None,\n    ) -> Command:\n        class MockCommand(Command):\n            name = command_name\n            arguments = command_arguments or []\n            options = command_options or []\n            description = command_description\n            help = command_help\n\n            def handle(self) -> int:\n                if command_handler is not None and not isinstance(command_handler, str):\n                    return command_handler(self)\n\n                self._io.write_line(\n                    command_handler\n                    or f\"The mock command '{command_name}' has been called\"\n                )\n\n                return 0\n\n        return MockCommand()\n\n    return _command_factory\n\n\n@pytest.fixture(autouse=True)\ndef default_keyring(with_null_keyring: None) -> None:\n    pass\n\n\n@pytest.fixture\ndef system_env(tmp_path_factory: TempPathFactory, mocker: MockerFixture) -> SystemEnv:\n    base_path = tmp_path_factory.mktemp(\"system_env\")\n    env = MockEnv(path=base_path, sys_path=[str(base_path / \"purelib\")])\n    assert env.path.is_dir()\n\n    userbase = env.path / \"userbase\"\n    userbase.mkdir(exist_ok=False)\n    env.paths[\"userbase\"] = str(userbase)\n\n    paths = {str(scheme): str(env.path / scheme) for scheme in SCHEME_NAMES}\n    env.paths.update(paths)\n\n    for path in paths.values():\n        Path(path).mkdir(exist_ok=False)\n\n    mocker.patch.object(EnvManager, \"get_system_env\", return_value=env)\n\n    env.set_paths()\n    return env\n\n\n@pytest.fixture\ndef mocked_pythons() -> list[findpython.PythonVersion]:\n    \"\"\"\n    Fixture that provides a mock representation of Python versions that are registered.\n\n    This fixture returns a list of `findpython.PythonVersion` objects. Typically,\n    it is used in test scenarios to replace actual Python version discovery with\n    mocked data. By default, this fixture returns an empty list to simulate an\n    environment without any Python installations.\n\n    :return: Mocked list of Python versions with the type of\n        `findpython.PythonVersion`.\n    \"\"\"\n    return []\n\n\n@pytest.fixture\ndef mocked_pythons_version_map() -> dict[str, findpython.PythonVersion]:\n    \"\"\"\n    Create a mocked Python version map for testing purposes. This serves as a\n    quick lookup for exact version matches.\n\n    This function provides a fixture that returns a dictionary containing a\n    mapping of specific keys to corresponding instances of the\n    `findpython.PythonVersion` class. This is primarily used for testing\n    scenarios involving multiple Python interpreters. If the key is an\n    empty string, it maps to the system Python interpreter as used by the\n    `with_mocked_findpython` fixture.\n\n    :return: A dictionary mapping string keys to `findpython.PythonVersion`\n        instances. A default key \"\" (empty string) is pre-set to match the\n        current system environment.\n    \"\"\"\n    return {\n        # add the system python if key is empty\n        \"\": Python.get_system_python()._python\n    }\n\n\n@pytest.fixture\ndef mock_findpython_find(\n    mocked_pythons: list[findpython.PythonVersion],\n    mocked_pythons_version_map: dict[str, findpython.PythonVersion],\n    mocker: MockerFixture,\n) -> MagicMock:\n    \"\"\"\n    Mock the `findpython.find` function for testing purposes, enabling controlled\n    execution and predictable results when specific python versions or executables\n    are queried. This mock is particularly useful for reproducing various scenarios\n    involving Python version detection without dependence on the actual system's\n    Python installations.\n\n    :return:\n        A `MagicMock` object representing the mocked `findpython.find` function. It\n        operates using the `_find` internal function, which resolves python versions\n        based on the provided test data (`mocked_pythons` and\n        `mocked_pythons_version_map`).\n\n    \"\"\"\n\n    def _find(\n        name: str | None = None,\n    ) -> findpython.PythonVersion | None:\n        # find exact version matches\n        # the default key is an empty string in mocked_pythons_version_map\n        if python := mocked_pythons_version_map.get(name or \"\"):\n            return python\n\n        if name is None:\n            return None\n\n        candidates: list[findpython.PythonVersion] = []\n\n        # iterate through to find executable name match\n        for python in mocked_pythons:\n            if python.executable.name == name:\n                return python\n            elif str(python.executable).endswith(name):\n                candidates.append(python)\n\n        if candidates:\n            candidates.sort(key=lambda p: p.executable.name)\n            return candidates[0]\n\n        return None\n\n    return mocker.patch(\n        \"findpython.find\",\n        side_effect=_find,\n    )\n\n\n@pytest.fixture\ndef mock_findpython_find_all(\n    mocked_pythons: list[findpython.PythonVersion],\n    mocker: MockerFixture,\n) -> MagicMock:\n    \"\"\"\n    Mocks the `find_all` function in the `findpython` module to return a predefined\n    list of `PythonVersion` objects.\n\n    This fixture is useful for testing functionality dependent on the output of the\n    `find_all` function without executing its original logic.\n\n    :return: Mocked `find_all` function patched to return the specified list of\n        `mocked_pythons`.\n    \"\"\"\n    return mocker.patch(\n        \"findpython.find_all\",\n        return_value=mocked_pythons,\n    )\n\n\n@pytest.fixture\ndef mocked_python_register(\n    with_mocked_findpython: None,\n    mocked_pythons: list[findpython.PythonVersion],\n    mocked_pythons_version_map: dict[str, findpython.PythonVersion],\n    mocker: MockerFixture,\n) -> MockedPythonRegister:\n    \"\"\"\n    Fixture to provide a mocked registration mechanism for PythonVersion objects. The\n    fixture interacts with mocked versions of Python, allowing test cases to register\n    and manage Python versions under controlled conditions. The provided register\n    function enables the dynamic registration of Python versions, executable,\n    and optional system designation.\n\n    :return: A function to register a Python version with configurable options.\n    \"\"\"\n\n    def register(\n        version: str,\n        executable_name: str | Path | None = None,\n        implementation: str | None = None,\n        free_threaded: bool = False,\n        parent: str | Path | None = None,\n        make_system: bool = False,\n    ) -> Python:\n        # we allow this to let windows specific tests setup special cases\n        parent = Path(parent or \"/usr/bin\")\n\n        if not executable_name:\n            info = version.split(\".\")\n            executable_name = f\"python{info[0]}.{info[1]}\"\n\n        class MockPythonVersion(findpython.PythonVersion):  # type: ignore[misc]\n            @property\n            def implementation(self) -> str:\n                return implementation or platform.python_implementation()\n\n            @property\n            def freethreaded(self) -> bool:\n                return free_threaded\n\n        python = MockPythonVersion(\n            executable=parent / executable_name,\n            _version=packaging.version.Version(version),\n            _interpreter=parent / executable_name,\n        )\n        mocked_pythons.append(python)\n        mocked_pythons_version_map[version] = python\n\n        if make_system:\n            mocker.patch(\n                \"poetry.utils.env.python.Python.get_system_python\",\n                return_value=Python(python=python),\n            )\n            mocked_pythons_version_map[\"\"] = python\n\n        return Python(python=python)\n\n    return register\n\n\n@pytest.fixture\ndef without_mocked_findpython(\n    mock_findpython_find: MagicMock,\n    mock_findpython_find_all: MagicMock,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    This fixture stops the mocks for the functions `mock_findpython_find_all`\n    and `mock_findpython_find`. It is intended for use within unit tests\n    to ensure that the actual behavior of the mocked functions is not\n    included unless explicitly required.\n    \"\"\"\n    mocker.stop(mock_findpython_find_all)\n    mocker.stop(mock_findpython_find)\n\n\n@pytest.fixture(autouse=True)\ndef with_mocked_findpython(\n    mock_findpython_find: MagicMock,\n    mock_findpython_find_all: MagicMock,\n) -> None:\n    \"\"\"\n    Fixture that mocks the `findpython` library functions `find` and `find_all`.\n\n    This fixture enables controlled testing of Python version discovery by providing\n    mocked data for `findpython.PythonVersion` objects and behavior. It patches\n    the `findpython.find` and `findpython.find_all` methods using the given mock\n    data to simulate real functionality.\n\n    This function mock behavior includes:\n    - Finding Python versions by an exact match of executable name or selectable from\n      candidates whose executable names end with the provided input.\n    - Returning all mocked Python versions through the `findpython.find_all`.\n\n    See also the `without_mocked_findpython`, `mocked_python_register`, `mock_findpython_find`,\n    and `mock_findpython_find_all` fixtures.\n    \"\"\"\n    return\n\n\n@pytest.fixture\ndef with_no_active_python(mocker: MockerFixture) -> MagicMock:\n    return mocker.patch(\n        \"poetry.utils.env.python.Python.get_active_python\",\n        return_value=None,\n    )\n\n\n@pytest.fixture\ndef mock_python_version(mocker: MockerFixture) -> None:\n    class MockPythonVersion(findpython.PythonVersion):  # type: ignore[misc]\n        @property\n        def implementation(self) -> str:\n            return \"PyPy\" if \"pypy\" in str(self.executable) else \"CPython\"\n\n        @property\n        def freethreaded(self) -> bool:\n            return self._install_dir.name.endswith(\"t\")\n\n        @property\n        def _install_dir(self) -> Path:\n            install_dir = self.executable.parent\n            if not WINDOWS:\n                install_dir = install_dir.parent\n            assert isinstance(install_dir, Path)\n            return install_dir\n\n        def _get_version(self) -> packaging.version.Version:\n            return packaging.version.Version(\n                self._install_dir.name.removesuffix(\"t\").split(\"@\")[1]\n            )\n\n        def _get_architecture(self) -> str:\n            return \"64bit\"\n\n        def _get_interpreter(self) -> str:\n            return str(self.executable)\n\n    mocker.patch(\n        \"poetry.utils.env.python.providers.PoetryPythonPathProvider.version_maker\",\n        MockPythonVersion,\n    )\n\n\n@pytest.fixture\ndef mocked_poetry_managed_python_register(\n    config: Config, without_mocked_findpython: None, mock_python_version: None\n) -> MockedPoetryPythonRegister:\n    config.python_installation_dir.mkdir()\n\n    def register(\n        version: str,\n        implementation: str,\n        free_threaded: bool = False,\n        with_install_dir: bool = False,\n    ) -> Path:\n        python_dir_name = f\"{implementation}@{version}\"\n        if free_threaded:\n            python_dir_name += \"t\"\n        bin_dir = config.python_installation_dir / python_dir_name\n        if with_install_dir:\n            bin_dir /= \"install\"\n        if not WINDOWS:\n            bin_dir /= \"bin\"\n        bin_dir.mkdir(parents=True)\n        (bin_dir / \"python\").touch()\n        if implementation == \"pypy\":\n            (bin_dir / \"pypy\").touch()\n        return bin_dir\n\n    return register\n"
  },
  {
    "path": "tests/console/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/cache/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/cache/conftest.py",
    "content": "from __future__ import annotations\n\nimport uuid\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.cache import FileCache\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from tests.conftest import Config\n\n\n@pytest.fixture\ndef repository_cache_dir(config: Config) -> Path:\n    return config.repository_cache_directory\n\n\n@pytest.fixture\ndef repositories() -> list[str]:\n    return [f\"01_{uuid.uuid4()}\", f\"02_{uuid.uuid4()}\"]\n\n\n@pytest.fixture\ndef repository_dirs(\n    repository_cache_dir: Path,\n    repositories: list[str],\n) -> list[Path]:\n    return [\n        repository_cache_dir / repositories[0],\n        repository_cache_dir / repositories[1],\n    ]\n\n\n@pytest.fixture\ndef caches(\n    repository_dirs: list[Path],\n) -> list[FileCache[dict[str, str]]]:\n    repository_dirs[0].mkdir(parents=True)\n    repository_dirs[1].mkdir(parents=True)\n\n    caches: list[FileCache[dict[str, str]]] = [\n        FileCache(path=repository_dirs[0]),\n        FileCache(path=repository_dirs[1]),\n    ]\n\n    caches[0].remember(\n        \"cachy:0.1\", lambda: {\"name\": \"cachy\", \"version\": \"0.1\"}, minutes=None\n    )\n    caches[0].remember(\n        \"cleo:0.2\", lambda: {\"name\": \"cleo\", \"version\": \"0.2\"}, minutes=None\n    )\n\n    caches[1].remember(\n        \"cachy:0.1\", lambda: {\"name\": \"cachy\", \"version\": \"0.1\"}, minutes=None\n    )\n    # different version of same package, other entry than in first cache\n    caches[1].remember(\n        \"cashy:0.2\", lambda: {\"name\": \"cashy\", \"version\": \"0.2\"}, minutes=None\n    )\n\n    return caches\n"
  },
  {
    "path": "tests/console/commands/cache/test_clear.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import TypeVar\n\nimport pytest\n\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import Application\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from poetry.utils.cache import FileCache\n\nT = TypeVar(\"T\")\n\n\n@pytest.fixture\ndef tester() -> ApplicationTester:\n    app = Application()\n\n    tester = ApplicationTester(app)\n    return tester\n\n\n@pytest.mark.parametrize(\"inputs\", [\"yes\", \"no\"])\ndef test_cache_clear_all(\n    tester: ApplicationTester,\n    repository_cache_dir: Path,\n    repositories: list[str],\n    repository_dirs: list[Path],\n    caches: list[FileCache[dict[str, str]]],\n    inputs: str,\n) -> None:\n    exit_code = tester.execute(\"cache clear --all\", inputs=inputs)\n\n    assert exit_code == 0\n    assert tester.io.fetch_output() == \"\"\n\n    if inputs == \"yes\":\n        assert not repository_dirs[0].exists() or not any(repository_dirs[0].iterdir())\n        assert not repository_dirs[1].exists() or not any(repository_dirs[1].iterdir())\n        assert not caches[0].has(\"cachy:0.1\")\n        assert not caches[0].has(\"cleo:0.2\")\n        assert not caches[1].has(\"cachy:0.1\")\n        assert not caches[1].has(\"cashy:0.2\")\n    else:\n        assert any((repository_cache_dir / repositories[0]).iterdir())\n        assert any((repository_cache_dir / repositories[1]).iterdir())\n        assert caches[0].has(\"cachy:0.1\")\n        assert caches[0].has(\"cleo:0.2\")\n        assert caches[1].has(\"cachy:0.1\")\n        assert caches[1].has(\"cashy:0.2\")\n\n\n@pytest.mark.parametrize(\"inputs\", [\"yes\", \"no\"])\ndef test_cache_clear_all_one_cache(\n    tester: ApplicationTester,\n    repository_cache_dir: Path,\n    repositories: list[str],\n    repository_dirs: list[Path],\n    caches: list[FileCache[dict[str, str]]],\n    inputs: str,\n) -> None:\n    exit_code = tester.execute(f\"cache clear {repositories[0]} --all\", inputs=inputs)\n\n    assert exit_code == 0\n    assert tester.io.fetch_output() == \"\"\n\n    if inputs == \"yes\":\n        assert not repository_dirs[0].exists() or not any(repository_dirs[0].iterdir())\n        assert not caches[0].has(\"cachy:0.1\")\n        assert not caches[0].has(\"cleo:0.2\")\n    else:\n        assert any((repository_cache_dir / repositories[0]).iterdir())\n        assert caches[0].has(\"cachy:0.1\")\n        assert caches[0].has(\"cleo:0.2\")\n\n    assert any((repository_cache_dir / repositories[1]).iterdir())\n    assert caches[1].has(\"cachy:0.1\")\n    assert caches[1].has(\"cashy:0.2\")\n\n\ndef test_cache_clear_all_no_entries(tester: ApplicationTester) -> None:\n    exit_code = tester.execute(\"cache clear --all\")\n\n    assert exit_code == 0\n    assert tester.io.fetch_output().strip() == \"No cache entries\"\n\n\ndef test_cache_clear_all_one_cache_no_entries(\n    tester: ApplicationTester,\n    repository_cache_dir: Path,\n    repositories: list[str],\n) -> None:\n    exit_code = tester.execute(f\"cache clear {repositories[0]} --all\")\n\n    assert exit_code == 0\n\n    assert tester.io.fetch_output().strip() == f\"No cache entries for {repositories[0]}\"\n\n\n@pytest.mark.parametrize(\"with_repo\", [False, True])\ndef test_cache_clear_missing_option(\n    tester: ApplicationTester, repositories: list[str], with_repo: bool\n) -> None:\n    command = f\"cache clear {repositories[0]}\" if with_repo else \"cache clear\"\n    exit_code = tester.execute(command)\n\n    assert exit_code == 1\n    assert (\n        \"Add the --all option if you want to clear all cache entries\"\n        in tester.io.fetch_error()\n    )\n\n\n@pytest.mark.parametrize(\"inputs\", [\"yes\", \"no\"])\n@pytest.mark.parametrize(\"package_name\", [\"cachy\", \"Cachy\"])\ndef test_cache_clear_pkg(\n    tester: ApplicationTester,\n    repositories: list[str],\n    caches: list[FileCache[dict[str, str]]],\n    package_name: str,\n    inputs: str,\n) -> None:\n    exit_code = tester.execute(\n        f\"cache clear {repositories[1]}:{package_name}:0.1\", inputs=inputs\n    )\n\n    assert exit_code == 0\n    assert tester.io.fetch_output() == \"\"\n\n    if inputs == \"yes\":\n        assert not caches[1].has(\"cachy:0.1\")\n        assert caches[1].has(\"cashy:0.2\")\n    else:\n        assert caches[1].has(\"cachy:0.1\")\n        assert caches[1].has(\"cashy:0.2\")\n\n    assert caches[0].has(\"cachy:0.1\")\n"
  },
  {
    "path": "tests/console/commands/cache/test_list.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.utils.cache import FileCache\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"cache list\")\n\n\ndef test_cache_list(\n    tester: CommandTester,\n    caches: list[FileCache[dict[str, str]]],\n    repositories: list[str],\n) -> None:\n    tester.execute()\n\n    expected = f\"\"\"\\\n{repositories[0]}\n{repositories[1]}\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_cache_list_empty(tester: CommandTester, repository_cache_dir: Path) -> None:\n    tester.execute()\n\n    expected = \"\"\"\\\nNo caches found\n\"\"\"\n\n    assert tester.io.fetch_error() == expected\n"
  },
  {
    "path": "tests/console/commands/conftest.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\n\n@pytest.fixture\ndef init_basic_inputs() -> str:\n    return \"\\n\".join(\n        [\n            \"my-package\",  # Package name\n            \"1.2.3\",  # Version\n            \"This is a description\",  # Description\n            \"n\",  # Author\n            \"MIT\",  # License\n            \">=3.6\",  # Python\n            \"n\",  # Interactive packages\n            \"n\",  # Interactive dev packages\n            \"\\n\",  # Generate\n        ]\n    )\n\n\n@pytest.fixture()\ndef init_basic_toml() -> str:\n    return \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nreadme = \"README.md\"\nrequires-python = \">=3.6\"\n\"\"\"\n\n\n@pytest.fixture\ndef init_basic_toml_no_readme(init_basic_toml: str) -> str:\n    # Remove the readme line\n    lines = init_basic_toml.splitlines()\n    lines = [line for line in lines if not line.strip().startswith(\"readme =\")]\n    init_basic_toml_no_readme = \"\\n\".join(lines)\n    return init_basic_toml_no_readme\n\n\n@pytest.fixture()\ndef new_basic_toml() -> str:\n    return \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nreadme = \"README.md\"\nrequires-python = \">=3.6\"\ndependencies = [\n]\n\n[tool.poetry]\npackages = [{include = \"my_package\", from = \"src\"}]\n\"\"\"\n"
  },
  {
    "path": "tests/console/commands/debug/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/debug/test_info.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.__version__ import __version__\nfrom poetry.utils.env import MockEnv\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"poetry.utils.env.EnvManager.get\",\n        return_value=MockEnv(\n            path=Path(\"/prefix\"), base=Path(\"/base/prefix\"), is_venv=True\n        ),\n    )\n    mocker.patch(\n        \"sys.prefix\",\n        \"/poetry/prefix\",\n    )\n    mocker.patch(\n        \"sys.executable\",\n        \"/poetry/prefix/bin/python\",\n    )\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"debug info\")\n\n\ndef test_debug_info_displays_complete_info(tester: CommandTester) -> None:\n    tester.execute()\n\n    expected = f\"\"\"\nPoetry\nVersion:    {__version__}\nPython:     {\".\".join(str(v) for v in sys.version_info[:3])}\nPath:       {Path(\"/poetry/prefix\")}\nExecutable: {Path(\"/poetry/prefix/bin/python\")}\n\nVirtualenv\nPython:         3.7.0\nImplementation: CPython\nPath:           {Path(\"/prefix\")}\nExecutable:     {Path(sys.executable)}\nValid:          True\n\nBase\nPlatform:   darwin\nOS:         posix\nPython:     {\".\".join(str(v) for v in sys.version_info[:3])}\nPath:       {Path(\"/base/prefix\")}\nExecutable: python\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/debug/test_resolve.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.factory import Factory\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"debug resolve\")\n\n\n@pytest.fixture(autouse=True)\ndef __add_packages(repo: TestRepository) -> None:\n    cachy020 = get_package(\"cachy\", \"0.2.0\")\n    cachy020.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n\n    repo.add_package(get_package(\"cachy\", \"0.1.0\"))\n    repo.add_package(cachy020)\n    repo.add_package(get_package(\"msgpack-python\", \"0.5.3\"))\n\n    repo.add_package(get_package(\"pendulum\", \"2.0.3\"))\n    repo.add_package(get_package(\"cleo\", \"0.6.5\"))\n\n\ndef test_debug_resolve_gives_resolution_results(tester: CommandTester) -> None:\n    tester.execute(\"cachy\")\n\n    expected = \"\"\"\\\nResolving dependencies...\n\nResolution results:\n\nmsgpack-python 0.5.3\ncachy          0.2.0\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_debug_resolve_tree_option_gives_the_dependency_tree(\n    tester: CommandTester,\n) -> None:\n    tester.execute(\"cachy --tree\")\n\n    expected = \"\"\"\\\nResolving dependencies...\n\nResolution results:\n\ncachy 0.2.0\n└── msgpack-python >=0.5 <0.6\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_debug_resolve_git_dependency(tester: CommandTester) -> None:\n    tester.execute(\"git+https://github.com/demo/demo.git\")\n\n    expected = \"\"\"\\\nResolving dependencies...\n\nResolution results:\n\npendulum 2.0.3\ndemo     0.1.2\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/env/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/env/conftest.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.env import EnvManager\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from pathlib import Path\n\n    from tests.helpers import PoetryTestApplication\n\n\n@pytest.fixture\ndef venv_name(app: PoetryTestApplication) -> str:\n    return EnvManager.generate_env_name(\n        app.poetry.package.name,\n        str(app.poetry.file.path.parent),\n    )\n\n\n@pytest.fixture\ndef venv_cache(tmp_path: Path) -> Path:\n    path = tmp_path / \"venv_cache\"\n    path.mkdir()\n    return path\n\n\n@pytest.fixture(scope=\"module\")\ndef python_versions() -> list[str]:\n    return [\"3.6\", \"3.7\"]\n\n\n@pytest.fixture\ndef venvs_in_cache_config(app: PoetryTestApplication, venv_cache: Path) -> None:\n    app.poetry.config.merge({\"virtualenvs\": {\"path\": str(venv_cache)}})\n\n\n@pytest.fixture\ndef venvs_in_cache_dirs(\n    app: PoetryTestApplication,\n    venvs_in_cache_config: None,\n    venv_name: str,\n    venv_cache: Path,\n    python_versions: list[str],\n) -> list[str]:\n    directories = []\n    for version in python_versions:\n        directory = venv_cache / f\"{venv_name}-py{version}\"\n        directory.mkdir(parents=True, exist_ok=True)\n        directories.append(directory.name)\n    return directories\n\n\n@pytest.fixture\ndef venvs_in_project_dir(app: PoetryTestApplication) -> Iterator[Path]:\n    os.environ.pop(\"VIRTUAL_ENV\", None)\n    venv_dir = app.poetry.file.path.parent.joinpath(\".venv\")\n    venv_dir.mkdir(exist_ok=True)\n    app.poetry.config.merge({\"virtualenvs\": {\"in-project\": True}})\n\n    try:\n        yield venv_dir\n    finally:\n        if venv_dir.exists():\n            venv_dir.rmdir()\n\n\n@pytest.fixture\ndef venvs_in_project_dir_none(app: PoetryTestApplication) -> Iterator[Path]:\n    os.environ.pop(\"VIRTUAL_ENV\", None)\n    venv_dir = app.poetry.file.path.parent.joinpath(\".venv\")\n    venv_dir.mkdir(exist_ok=True)\n    app.poetry.config.merge({\"virtualenvs\": {\"in-project\": None}})\n\n    try:\n        yield venv_dir\n    finally:\n        venv_dir.rmdir()\n\n\n@pytest.fixture\ndef venvs_in_project_dir_false(app: PoetryTestApplication) -> Iterator[Path]:\n    os.environ.pop(\"VIRTUAL_ENV\", None)\n    venv_dir = app.poetry.file.path.parent.joinpath(\".venv\")\n    venv_dir.mkdir(exist_ok=True)\n    app.poetry.config.merge({\"virtualenvs\": {\"in-project\": False}})\n\n    try:\n        yield venv_dir\n    finally:\n        venv_dir.rmdir()\n"
  },
  {
    "path": "tests/console/commands/env/helpers.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom poetry.core.constraints.version import Version\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\nVERSION_3_7_1 = Version.parse(\"3.7.1\")\n\n\ndef build_venv(path: Path | str, **_: Any) -> None:\n    Path(path).mkdir(parents=True, exist_ok=True)\n\n\ndef check_output_wrapper(\n    version: Version = VERSION_3_7_1,\n) -> Callable[[list[str], Any, Any], str]:\n    def check_output(cmd: list[str], *args: Any, **kwargs: Any) -> str:\n        # cmd is a list, like [\"python\", \"-c\", \"do stuff\"]\n        python_cmd = cmd[-1]\n        if \"print(json.dumps(env))\" in python_cmd:\n            return (\n                f'{{\"version_info\": [{version.major}, {version.minor},'\n                f\" {version.patch}]}}\"\n            )\n\n        if \"sys.version_info[:3]\" in python_cmd:\n            return version.text\n\n        if \"sys.version_info[:2]\" in python_cmd:\n            return f\"{version.major}.{version.minor}\"\n\n        if \"import sys; print(sys.executable)\" in python_cmd:\n            executable = cmd[0]\n            basename = os.path.basename(executable)\n            return f\"/usr/bin/{basename}\"\n\n        if \"print(sys.base_prefix)\" in python_cmd:\n            return \"/usr\"\n\n        assert \"import sys; print(sys.prefix)\" in python_cmd\n        return \"/prefix\"\n\n    return check_output\n"
  },
  {
    "path": "tests/console/commands/env/test_activate.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.console.commands.env.activate import ShellNotSupportedError\nfrom poetry.utils._compat import WINDOWS\n\n\nif TYPE_CHECKING:\n    from cleo.testers.application_tester import ApplicationTester\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.env import VirtualEnv\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"env activate\")\n\n\n@pytest.mark.parametrize(\n    \"shell, command, ext\",\n    (\n        (\"dash\", \".\", \"\"),\n        (\"bash\", \"source\", \"\"),\n        (\"zsh\", \"source\", \"\"),\n        (\"fish\", \"source\", \".fish\"),\n        (\"nu\", \"overlay use\", \".nu\"),\n        (\"csh\", \"source\", \".csh\"),\n        (\"tcsh\", \"source\", \".csh\"),\n    ),\n)\ndef test_env_activate_prints_correct_script(\n    tmp_venv: VirtualEnv,\n    mocker: MockerFixture,\n    tester: CommandTester,\n    shell: str,\n    command: str,\n    ext: str,\n) -> None:\n    mocker.patch(\"shellingham.detect_shell\", return_value=(shell, None))\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=tmp_venv)\n\n    if WINDOWS and shell in {\"csh\", \"tcsh\"}:\n        with pytest.raises(ShellNotSupportedError):\n            tester.execute()\n\n    else:\n        tester.execute()\n\n        line = tester.io.fetch_output().rstrip(\"\\n\")\n        assert line == f\"{command} {tmp_venv.bin_dir.as_posix()}/activate{ext}\"\n\n\n@pytest.mark.parametrize(\n    \"shell, command, ext\",\n    (\n        (\"cmd\", \"\", \".bat\"),\n        (\"pwsh\", \"&\", \".ps1\"),\n        (\"powershell\", \"&\", \".ps1\"),\n    ),\n)\n@pytest.mark.skipif(not WINDOWS, reason=\"Only Windows shells\")\ndef test_env_activate_prints_correct_script_for_windows_shells(\n    tmp_venv: VirtualEnv,\n    mocker: MockerFixture,\n    tester: CommandTester,\n    shell: str,\n    command: str,\n    ext: str,\n) -> None:\n    mocker.patch(\"shellingham.detect_shell\", return_value=(shell, None))\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=tmp_venv)\n\n    tester.execute()\n\n    line = tester.io.fetch_output().rstrip(\"\\n\")\n    activation_script = tmp_venv.bin_dir / f\"activate{ext}\"\n    assert line == f'{command} \"{activation_script}\"'.strip()\n\n\n@pytest.mark.parametrize(\"verbosity\", [\"\", \"-v\", \"-vv\", \"-vvv\"])\ndef test_no_additional_output_in_verbose_mode(\n    tmp_venv: VirtualEnv,\n    mocker: MockerFixture,\n    app_tester: ApplicationTester,\n    verbosity: str,\n) -> None:\n    mocker.patch(\"shellingham.detect_shell\", return_value=(\"pwsh\", None))\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=tmp_venv)\n\n    # use an AppTester instead of a CommandTester to catch additional output\n    app_tester.execute(f\"env activate {verbosity}\")\n\n    lines = app_tester.io.fetch_output().splitlines()\n    assert len(lines) == 1\n"
  },
  {
    "path": "tests/console/commands/env/test_info.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.env import MockEnv\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"poetry.utils.env.EnvManager.get\",\n        return_value=MockEnv(\n            path=Path(\"/prefix\"), base=Path(\"/base/prefix\"), is_venv=True\n        ),\n    )\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"env info\")\n\n\ndef test_env_info_displays_complete_info(tester: CommandTester) -> None:\n    tester.execute()\n\n    expected = f\"\"\"\nVirtualenv\nPython:         3.7.0\nImplementation: CPython\nPath:           {Path(\"/prefix\")}\nExecutable:     {sys.executable}\nValid:          True\n\nBase\nPlatform:   darwin\nOS:         posix\nPython:     {\".\".join(str(v) for v in sys.version_info[:3])}\nPath:       {Path(\"/base/prefix\")}\nExecutable: python\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_env_info_displays_path_only(tester: CommandTester) -> None:\n    tester.execute(\"--path\")\n    expected = str(Path(\"/prefix\")) + \"\\n\"\n    assert tester.io.fetch_output() == expected\n\n\ndef test_env_info_displays_executable_only(tester: CommandTester) -> None:\n    tester.execute(\"--executable\")\n    expected = str(sys.executable) + \"\\n\"\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/env/test_list.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\nimport tomlkit\n\nfrom poetry.toml.file import TOMLFile\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.env import MockEnv\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef venv_activate_37(venv_cache: Path, venv_name: str) -> None:\n    envs_file = TOMLFile(venv_cache / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    envs_file.write(doc)\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"env list\")\n\n\ndef test_none_activated(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    mocker: MockerFixture,\n    env: MockEnv,\n) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=env)\n    tester.execute()\n    expected = \"\\n\".join(venvs_in_cache_dirs)\n    assert tester.io.fetch_output().strip() == expected\n\n\ndef test_activated(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_cache: Path,\n    venv_activate_37: None,\n) -> None:\n    tester.execute()\n    expected = \"\\n\".join(venvs_in_cache_dirs).replace(\"py3.7\", \"py3.7 (Activated)\")\n    assert tester.io.fetch_output().strip() == expected\n\n\ndef test_in_project_venv(\n    tester: CommandTester, venvs_in_project_dir: list[str]\n) -> None:\n    tester.execute()\n    expected = \".venv (Activated)\\n\"\n    assert tester.io.fetch_output() == expected\n\n\ndef test_in_project_venv_no_explicit_config(\n    tester: CommandTester, venvs_in_project_dir_none: list[str]\n) -> None:\n    tester.execute()\n    expected = \".venv (Activated)\\n\"\n    assert tester.io.fetch_output() == expected\n\n\ndef test_in_project_venv_is_false(\n    tester: CommandTester, venvs_in_project_dir_false: list[str]\n) -> None:\n    tester.execute()\n    expected = \"\"\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/env/test_remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom tests.console.commands.env.helpers import check_output_wrapper\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"env remove\")\n\n\ndef test_remove_by_python_version(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_name: str,\n    venv_cache: Path,\n) -> None:\n    check_output = mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    tester.execute(\"3.6\")\n\n    assert check_output.called\n    assert not (venv_cache / f\"{venv_name}-py3.6\").exists()\n\n    expected = f\"Deleted virtualenv: {venv_cache / venv_name}-py3.6\\n\"\n    assert tester.io.fetch_output() == expected\n\n\ndef test_remove_by_name(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_name: str,\n    venv_cache: Path,\n) -> None:\n    expected = \"\"\n\n    for name in venvs_in_cache_dirs:\n        tester.execute(name)\n\n        assert not (venv_cache / name).exists()\n\n        expected += f\"Deleted virtualenv: {venv_cache / name}\\n\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    \"envs_file\", [None, \"empty\", \"self\", \"other\", \"self_and_other\"]\n)\ndef test_remove_all(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_name: str,\n    venv_cache: Path,\n    envs_file: str | None,\n) -> None:\n    envs_file_path = venv_cache / \"envs.toml\"\n    if envs_file == \"empty\":\n        envs_file_path.touch()\n    elif envs_file == \"self\":\n        envs_file_path.write_text(\n            f'[{venv_name}]\\nminor = \"3.9\"\\npatch = \"3.9.1\"\\n', encoding=\"utf-8\"\n        )\n    elif envs_file == \"other\":\n        envs_file_path.write_text(\n            '[other-abcdefgh]\\nminor = \"3.9\"\\npatch = \"3.9.1\"\\n', encoding=\"utf-8\"\n        )\n    elif envs_file == \"self_and_other\":\n        envs_file_path.write_text(\n            f'[{venv_name}]\\nminor = \"3.9\"\\npatch = \"3.9.1\"\\n'\n            '[other-abcdefgh]\\nminor = \"3.9\"\\npatch = \"3.9.1\"\\n',\n            encoding=\"utf-8\",\n        )\n    else:\n        # no envs file -> nothing to prepare\n        assert envs_file is None\n\n    expected = {\"\"}\n    tester.execute(\"--all\")\n    for name in venvs_in_cache_dirs:\n        assert not (venv_cache / name).exists()\n        expected.add(f\"Deleted virtualenv: {venv_cache / name}\")\n    assert set(tester.io.fetch_output().split(\"\\n\")) == expected\n\n    if envs_file is not None:\n        assert envs_file_path.exists()\n        envs_file_content = envs_file_path.read_text(encoding=\"utf-8\")\n        assert venv_name not in envs_file_content\n        if \"other\" in envs_file:\n            assert \"other-abcdefgh\" in envs_file_content\n        else:\n            assert envs_file_content == \"\"\n    else:\n        assert not envs_file_path.exists()\n\n\ndef test_remove_all_and_version(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_name: str,\n    venv_cache: Path,\n) -> None:\n    expected = {\"\"}\n    tester.execute(f\"--all {venvs_in_cache_dirs[0]}\")\n    for name in venvs_in_cache_dirs:\n        assert not (venv_cache / name).exists()\n        expected.add(f\"Deleted virtualenv: {venv_cache / name}\")\n    assert set(tester.io.fetch_output().split(\"\\n\")) == expected\n\n\ndef test_remove_multiple(\n    tester: CommandTester,\n    venvs_in_cache_dirs: list[str],\n    venv_name: str,\n    venv_cache: Path,\n) -> None:\n    expected = {\"\"}\n    removed_envs = venvs_in_cache_dirs[0:2]\n    remaining_envs = venvs_in_cache_dirs[2:]\n    tester.execute(\" \".join(removed_envs))\n    for name in removed_envs:\n        assert not (venv_cache / name).exists()\n        expected.add(f\"Deleted virtualenv: {venv_cache / name}\")\n    for name in remaining_envs:\n        assert (venv_cache / name).exists()\n    assert set(tester.io.fetch_output().split(\"\\n\")) == expected\n\n\ndef test_remove_in_project(tester: CommandTester, venvs_in_project_dir: Path) -> None:\n    assert venvs_in_project_dir.exists()\n\n    tester.execute()\n\n    assert not venvs_in_project_dir.exists()\n\n    expected = f\"Deleted virtualenv: {venvs_in_project_dir}\\n\"\n    assert tester.io.fetch_output() == expected\n\n\ndef test_remove_in_project_all(\n    tester: CommandTester, venvs_in_project_dir: Path\n) -> None:\n    assert venvs_in_project_dir.exists()\n\n    tester.execute(\"--all\")\n\n    assert not venvs_in_project_dir.exists()\n\n    expected = f\"Deleted virtualenv: {venvs_in_project_dir}\\n\"\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/env/test_use.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\nimport tomlkit\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.console.commands.env.use import EnvUseCommand\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env.python.exceptions import NoCompatiblePythonVersionFoundError\nfrom tests.console.commands.env.helpers import build_venv\nfrom tests.console.commands.env.helpers import check_output_wrapper\n\n\nif TYPE_CHECKING:\n    from unittest.mock import MagicMock\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.env.base_env import PythonVersion\n    from tests.types import CommandTesterFactory\n    from tests.types import MockedPythonRegister\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n\n@pytest.fixture(autouse=True)\ndef mock_subprocess_calls(\n    setup: None, current_python: PythonVersion, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.from_parts(*current_python[:3])),\n    )\n    mocker.patch(\n        \"subprocess.Popen.communicate\",\n        side_effect=[(\"/prefix\", None), (\"/prefix\", None), (\"/prefix\", None)],\n    )\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"env use\")\n\n\ndef test_activate_activates_non_existing_virtualenv_no_envs_file(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    venv_cache: Path,\n    venv_name: str,\n    venvs_in_cache_config: None,\n    mocked_python_register: MockedPythonRegister,\n    with_no_active_python: MagicMock,\n) -> None:\n    mocked_python_register(\"3.7.1\")\n    mock_build_env = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv\n    )\n    envs_file = TOMLFile(venv_cache / \"envs.toml\")\n\n    assert not envs_file.exists()\n    assert not list(venv_cache.iterdir())\n\n    tester.execute(\"3.7\")\n\n    venv_py37 = venv_cache / f\"{venv_name}-py3.7\"\n    mock_build_env.assert_called_with(\n        venv_py37,\n        executable=Path(\"/usr/bin/python3.7\"),\n        flags={\n            \"always-copy\": False,\n            \"system-site-packages\": False,\n            \"no-pip\": False,\n        },\n        prompt=\"simple-project-py3.7\",\n    )\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.7\"\n    assert envs[venv_name][\"patch\"] == \"3.7.1\"\n\n    assert (\n        tester.io.fetch_error()\n        == f\"Creating virtualenv {venv_py37.name} in {venv_py37.parent}\\n\"\n    )\n    assert tester.io.fetch_output() == f\"Using virtualenv: {venv_py37}\\n\"\n\n\n@pytest.mark.parametrize(\"use_poetry_python\", [True, False])\ndef test_activate_does_not_activate_non_existing_virtualenv_with_unsupported_version(\n    tester: CommandTester,\n    venv_cache: Path,\n    venv_name: str,\n    venvs_in_cache_config: None,\n    mocked_python_register: MockedPythonRegister,\n    with_no_active_python: MagicMock,\n    use_poetry_python: bool,\n) -> None:\n    mocked_python_register(\"3.7.1\")\n    mocked_python_register(\"3.8.2\")\n    command = tester.command\n    assert isinstance(command, EnvUseCommand)\n    command.poetry.package.python_versions = \"~3.8\"\n    command.poetry.config.merge(\n        {\"virtualenvs\": {\"use-poetry-python\": use_poetry_python}}\n    )\n\n    assert not list(venv_cache.iterdir())\n\n    with pytest.raises(NoCompatiblePythonVersionFoundError):\n        tester.execute(\"3.7\")\n\n    assert not list(venv_cache.iterdir())\n\n\ndef test_get_prefers_explicitly_activated_virtualenvs_over_env_var(\n    tester: CommandTester,\n    current_python: PythonVersion,\n    venv_cache: Path,\n    venv_name: str,\n    venvs_in_cache_config: None,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    os.environ[\"VIRTUAL_ENV\"] = \"/environment/prefix\"\n\n    python_minor = \".\".join(str(v) for v in current_python[:2])\n    python_patch = \".\".join(str(v) for v in current_python[:3])\n    venv_dir = venv_cache / f\"{venv_name}-py{python_minor}\"\n    venv_dir.mkdir(parents=True, exist_ok=True)\n\n    envs_file = TOMLFile(venv_cache / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": python_minor, \"patch\": python_patch}\n    envs_file.write(doc)\n\n    mocked_python_register(python_patch)\n\n    tester.execute(python_minor)\n\n    expected = f\"\"\"\\\nUsing virtualenv: {venv_dir}\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    current_python: PythonVersion,\n    venv_cache: Path,\n    venv_name: str,\n    venvs_in_cache_config: None,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    os.environ[\"VIRTUAL_ENV\"] = \"/environment/prefix\"\n\n    python_minor = \".\".join(str(v) for v in current_python[:2])\n    venv_dir = venv_cache / f\"{venv_name}-py{python_minor}\"\n\n    mocked_python_register(python_minor)\n\n    mocker.patch(\n        \"poetry.utils.env.EnvManager._env\",\n        new_callable=mocker.PropertyMock,\n        return_value=MockEnv(\n            path=Path(\"/environment/prefix\"),\n            base=Path(\"/base/prefix\"),\n            version_info=current_python,\n            is_venv=True,\n        ),\n    )\n    mocker.patch(\"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv)\n\n    tester.execute(python_minor)\n\n    assert (\n        tester.io.fetch_error()\n        == f\"Creating virtualenv {venv_dir.name} in {venv_dir.parent}\\n\"\n    )\n    assert tester.io.fetch_output() == f\"Using virtualenv: {venv_dir}\\n\"\n"
  },
  {
    "path": "tests/console/commands/python/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/python/test_python_install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version.version import Version\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.env.python.installer import PythonDownloadNotFoundError\nfrom poetry.utils.env.python.installer import PythonInstallationError\n\n\nif TYPE_CHECKING:\n    from unittest.mock import MagicMock\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture(autouse=True)\ndef mock_installer(mocker: MockerFixture) -> MagicMock:\n    return mocker.patch(\"poetry.console.commands.python.install.PythonInstaller\")\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"python install\")\n\n\ndef test_install_invalid_version(tester: CommandTester) -> None:\n    tester.execute(\"foo\")\n\n    assert tester.status_code == 1\n    assert tester.io.fetch_error() == \"Invalid Python version requested foo\\n\"\n\n\ndef test_install_free_threaded_not_supported(tester: CommandTester) -> None:\n    tester.execute(\"-t 3.12\")\n\n    assert tester.status_code == 1\n    assert (\n        \"Free threading is not supported for Python versions prior to 3.13.0.\\n\"\n        in tester.io.fetch_error()\n    )\n\n\ndef test_install_exists(tester: CommandTester, mock_installer: MagicMock) -> None:\n    mock_installer.return_value.exists.return_value = True\n\n    tester.execute(\"3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_not_called()\n\n    assert tester.status_code == 1\n    assert \"Python version already installed at\" in tester.io.fetch_error()\n\n\ndef test_install_no_download(tester: CommandTester, mock_installer: MagicMock) -> None:\n    mock_installer.return_value.exists.side_effect = PythonDownloadNotFoundError\n\n    tester.execute(\"3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_not_called()\n\n    assert tester.status_code == 1\n    assert (\n        \"No suitable standalone build found for the requested Python version.\\n\"\n        in tester.io.fetch_error()\n    )\n\n\ndef test_install_failure(tester: CommandTester, mock_installer: MagicMock) -> None:\n    mock_installer.return_value.exists.return_value = False\n    mock_installer.return_value.install.side_effect = PythonInstallationError(\"foo\")\n\n    tester.execute(\"3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_called_once()\n\n    assert tester.status_code == 1\n    assert (\n        tester.io.fetch_output()\n        == \"Downloading and installing 3.11 (cpython) ... Failed\\n\"\n    )\n    assert \"foo\\n\" in tester.io.fetch_error()\n\n\n@pytest.mark.parametrize(\"clean\", [False, True])\ndef test_install_corrupt(\n    tester: CommandTester, mock_installer: MagicMock, config: Config, clean: bool\n) -> None:\n    def create_install_dir() -> None:\n        (config.python_installation_dir / \"cpython@3.11.9\").mkdir(parents=True)\n\n    mock_installer.return_value.exists.side_effect = [False, PoetryRuntimeError(\"foo\")]\n    mock_installer.return_value.install.side_effect = create_install_dir\n    mock_installer.return_value.version = Version.parse(\"3.11.9\")\n\n    with pytest.raises(PoetryRuntimeError):\n        clean_opt = \"-c \" if clean else \"\"\n        tester.execute(f\"{clean_opt}3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_called_once()\n\n    expected = (\n        \"Downloading and installing 3.11 (cpython) ... Done\\n\"\n        \"Testing 3.11 (cpython) ... Failed\\n\"\n    )\n    if clean:\n        expected += \"Removing installation 3.11.9 (cpython) ... Done\\n\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_install_success(tester: CommandTester, mock_installer: MagicMock) -> None:\n    mock_installer.return_value.exists.return_value = False\n\n    tester.execute(\"3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_called_once()\n\n    assert tester.status_code == 0\n    assert tester.io.fetch_output() == (\n        \"Downloading and installing 3.11 (cpython) ... Done\\n\"\n        \"Testing 3.11 (cpython) ... Done\\n\"\n    )\n\n\ndef test_install_reinstall(tester: CommandTester, mock_installer: MagicMock) -> None:\n    mock_installer.return_value.exists.return_value = True\n\n    tester.execute(\"-r 3.11\")\n\n    mock_installer.assert_called_once_with(\"3.11\", \"cpython\", False)\n    mock_installer.return_value.install.assert_called_once()\n\n    assert tester.status_code == 0\n    assert tester.io.fetch_output() == (\n        \"Downloading and installing 3.11 (cpython) ... Done\\n\"\n        \"Testing 3.11 (cpython) ... Done\\n\"\n    )\n\n\n@pytest.mark.parametrize(\"free_threaded\", [False, True])\n@pytest.mark.parametrize(\"implementation\", [\"cpython\", \"pypy\"])\ndef test_install_passes_options_to_installer(\n    tester: CommandTester,\n    mock_installer: MagicMock,\n    free_threaded: bool,\n    implementation: str,\n) -> None:\n    mock_installer.return_value.exists.return_value = False\n\n    free_threaded_opt = \"-t \" if free_threaded else \"\"\n    impl_opt = f\"-i {implementation} \"\n    tester.execute(f\"{free_threaded_opt}{impl_opt}3.13\")\n\n    mock_installer.assert_called_once_with(\"3.13\", implementation, free_threaded)\n    mock_installer.return_value.install.assert_called_once()\n\n    assert tester.status_code == 0\n    details = f\"{implementation}, free-threaded\" if free_threaded else implementation\n    assert tester.io.fetch_output() == (\n        f\"Downloading and installing 3.13 ({details}) ... Done\\n\"\n        f\"Testing 3.13 ({details}) ... Done\\n\"\n    )\n\n\ndef test_install_free_threaded_via_trailing_t(\n    tester: CommandTester, mock_installer: MagicMock\n) -> None:\n    mock_installer.return_value.exists.return_value = False\n\n    tester.execute(\"3.13t\")\n\n    mock_installer.assert_called_once_with(\"3.13\", \"cpython\", True)\n    mock_installer.return_value.install.assert_called_once()\n\n    assert tester.status_code == 0\n    assert tester.io.fetch_output() == (\n        \"Downloading and installing 3.13 (cpython, free-threaded) ... Done\\n\"\n        \"Testing 3.13 (cpython, free-threaded) ... Done\\n\"\n    )\n"
  },
  {
    "path": "tests/console/commands/python/test_python_list.py",
    "content": "from __future__ import annotations\n\nimport platform\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils._compat import WINDOWS\nfrom tests.helpers import pbs_installer_supported_arch\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.config.config import Config\n    from tests.types import CommandTesterFactory\n    from tests.types import MockedPoetryPythonRegister\n    from tests.types import MockedPythonRegister\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"python list\")\n\n\ndef test_list_no_versions(tester: CommandTester) -> None:\n    tester.execute()\n\n    assert tester.io.fetch_output() == \"No Python installations found.\\n\"\n\n\ndef test_list_all(tester: CommandTester) -> None:\n    tester.execute(\"--all\")\n\n    if platform.system() == \"FreeBSD\" or not pbs_installer_supported_arch(\n        platform.machine()\n    ):\n        assert tester.io.fetch_output() == \"No Python installations found.\\n\"\n    else:\n        assert \"Available for download\" in tester.io.fetch_output()\n\n\ndef test_list_invalid_version(tester: CommandTester) -> None:\n    tester.execute(\"foo\")\n\n    assert tester.status_code == 1\n    assert tester.io.fetch_error() == \"Invalid Python version requested foo\\n\"\n\n\ndef test_list(\n    tester: CommandTester, mocked_python_register: MockedPythonRegister\n) -> None:\n    mocked_python_register(\"3.9.1\", parent=\"a\")\n    mocked_python_register(\"3.9.3\", parent=\"b\")\n    mocked_python_register(\"3.10.4\", parent=\"c\")\n\n    tester.execute()\n\n    expected = \"\"\"\\\n Version Implementation Manager Path         \\\n\n 3.10.4  CPython        System  c/python3.10 \\\n\n 3.9.3   CPython        System  b/python3.9  \\\n\n 3.9.1   CPython        System  a/python3.9  \\\n\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\"only_poetry_managed\", [False, True])\ndef test_list_poetry_managed(\n    tester: CommandTester,\n    config: Config,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n    only_poetry_managed: bool,\n) -> None:\n    mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    mocked_poetry_managed_python_register(\"3.10.8\", \"pypy\")\n    mocked_poetry_managed_python_register(\"3.14.0\", \"cpython\", free_threaded=True)\n\n    tester.execute(\"-m\" if only_poetry_managed else \"\")\n\n    lines = tester.io.fetch_output().splitlines()\n    system_lines = [line.strip() for line in lines if \"System\" in line]\n    poetry_lines = [line.strip() for line in lines if \"Poetry\" in line]\n\n    install_dir = config.python_installation_dir.as_posix()\n    bin_dir = \"\" if WINDOWS else \"bin/\"\n    expected = {\n        f\"3.10.8  PyPy           Poetry  {install_dir}/pypy@3.10.8/{bin_dir}pypy\",\n        f\"3.10.8  PyPy           Poetry  {install_dir}/pypy@3.10.8/{bin_dir}python\",\n        f\"3.9.1   CPython        Poetry  {install_dir}/cpython@3.9.1/{bin_dir}python\",\n        f\"3.14.0t CPython        Poetry  {install_dir}/cpython@3.14.0t/{bin_dir}python\",\n    }\n\n    assert set(poetry_lines) == expected\n    if only_poetry_managed:\n        assert not system_lines\n    else:\n        assert system_lines\n\n\n@pytest.mark.parametrize(\n    (\"version\", \"expected\"),\n    [(\"3\", 3), (\"3.9\", 2), (\"3.9.2\", 0), (\"3.9.3\", 1)],\n)\ndef test_list_version(\n    tester: CommandTester,\n    mocked_python_register: MockedPythonRegister,\n    version: str,\n    expected: int,\n) -> None:\n    mocked_python_register(\"2.7.13\", parent=\"_\")\n    mocked_python_register(\"3.9.1\", parent=\"a\")\n    mocked_python_register(\"3.9.3\", parent=\"b\")\n    mocked_python_register(\"3.10.4\", parent=\"c\")\n\n    tester.execute(version)\n\n    assert len(tester.io.fetch_output().splitlines()) - 1 == expected\n\n\n@pytest.mark.parametrize(\n    (\"implementation\", \"expected\"), [(\"PyPy\", 1), (\"pypy\", 1), (\"CPython\", 2)]\n)\ndef test_list_implementation(\n    tester: CommandTester,\n    mocked_python_register: MockedPythonRegister,\n    implementation: str,\n    expected: int,\n) -> None:\n    mocked_python_register(\"3.9.1\", implementation=\"PyPy\", parent=\"a\")\n    mocked_python_register(\"3.9.3\", implementation=\"CPython\", parent=\"b\")\n    mocked_python_register(\"3.10.4\", implementation=\"CPython\", parent=\"c\")\n\n    tester.execute(f\"-i {implementation}\")\n\n    assert len(tester.io.fetch_output().splitlines()) - 1 == expected\n\n\n@pytest.mark.parametrize((\"free_threaded\", \"expected\"), [(\"-t\", 1), (\"\", 3)])\ndef test_list_free_threaded(\n    tester: CommandTester,\n    mocked_python_register: MockedPythonRegister,\n    free_threaded: str,\n    expected: int,\n) -> None:\n    mocked_python_register(\"3.13.0\", free_threaded=False, parent=\"a\")\n    mocked_python_register(\"3.14.0\", free_threaded=False, parent=\"b\")\n    mocked_python_register(\"3.14.0\", free_threaded=True, parent=\"c\")\n\n    tester.execute(free_threaded)\n\n    assert len(tester.io.fetch_output().splitlines()) - 1 == expected\n"
  },
  {
    "path": "tests/console/commands/python/test_python_remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.config.config import Config\n    from tests.types import CommandTesterFactory\n    from tests.types import MockedPoetryPythonRegister\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"python remove\")\n\n\ndef test_remove_invalid_version(tester: CommandTester) -> None:\n    tester.execute(\"foo\")\n\n    assert tester.status_code == 1\n    assert tester.io.fetch_error() == \"Invalid Python version requested foo\\n\"\n\n\ndef test_remove_version_not_precise_enough(tester: CommandTester) -> None:\n    tester.execute(\"3.9\")\n\n    assert tester.status_code == 1\n    assert (\n        tester.io.fetch_error()\n        == \"\"\"\\\nInvalid Python version requested 3.9\n\nYou need to provide an exact Python version in the format X.Y.Z to be removed.\n\nYou can use poetry python list -m to list installed Poetry managed Python versions.\n\"\"\"\n    )\n\n\ndef test_remove_version_no_installation(tester: CommandTester, config: Config) -> None:\n    tester.execute(\"3.9.1\")\n\n    location = config.python_installation_dir / \"cpython@3.9.1\"\n    assert tester.io.fetch_output() == f\"No installation was found at {location}.\\n\"\n\n\ndef test_remove_version(\n    tester: CommandTester,\n    config: Config,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n) -> None:\n    cpython_path = mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    other_cpython_path = mocked_poetry_managed_python_register(\"3.9.2\", \"cpython\")\n    pypy_path = mocked_poetry_managed_python_register(\"3.9.1\", \"pypy\")\n\n    assert tester.execute(\"3.9.1\") == 0, tester.io.fetch_error()\n\n    assert (\n        tester.io.fetch_output() == \"Removing installation 3.9.1 (cpython) ... Done\\n\"\n    )\n    assert not cpython_path.exists()\n    assert pypy_path.exists()\n    assert other_cpython_path.exists()\n\n\n@pytest.mark.parametrize(\"implementation\", [\"cpython\", \"pypy\"])\ndef test_remove_version_implementation(\n    tester: CommandTester,\n    config: Config,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n    implementation: str,\n) -> None:\n    cpython_path = mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    pypy_path = mocked_poetry_managed_python_register(\"3.9.1\", \"pypy\")\n\n    assert tester.execute(f\"3.9.1 -i {implementation}\") == 0, tester.io.fetch_error()\n\n    assert (\n        tester.io.fetch_output()\n        == f\"Removing installation 3.9.1 ({implementation}) ... Done\\n\"\n    )\n    if implementation == \"cpython\":\n        assert not cpython_path.exists()\n        assert pypy_path.exists()\n    else:\n        assert cpython_path.exists()\n        assert not pypy_path.exists()\n\n\n@pytest.mark.parametrize(\"free_threaded\", [True, False])\n@pytest.mark.parametrize(\"option\", [True, False])\ndef test_remove_version_free_threaded(\n    tester: CommandTester,\n    config: Config,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n    free_threaded: bool,\n    option: bool,\n) -> None:\n    standard_path = mocked_poetry_managed_python_register(\"3.14.0\", \"cpython\")\n    free_threaded_path = mocked_poetry_managed_python_register(\n        \"3.14.0\", \"cpython\", free_threaded=True\n    )\n\n    args = \"3.14.0\"\n    if free_threaded:\n        args += \" --free-threaded\" if option else \"t\"\n\n    assert tester.execute(args) == 0, tester.io.fetch_error()\n\n    details = \"cpython\"\n    if free_threaded:\n        details += \", free-threaded\"\n    assert (\n        tester.io.fetch_output()\n        == f\"Removing installation 3.14.0 ({details}) ... Done\\n\"\n    )\n    if free_threaded:\n        assert not free_threaded_path.exists()\n        assert standard_path.exists()\n    else:\n        assert not standard_path.exists()\n        assert free_threaded_path.exists()\n\n\ndef test_remove_multiple_versions(\n    tester: CommandTester,\n    config: Config,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n) -> None:\n    cpython_path_1 = mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    cpython_path_2 = mocked_poetry_managed_python_register(\"3.9.2\", \"cpython\")\n    cpython_path_3 = mocked_poetry_managed_python_register(\"3.9.3\", \"cpython\")\n\n    tester.execute(\"3.9.1 3.9.3\")\n\n    assert tester.io.fetch_output() == (\n        \"Removing installation 3.9.1 (cpython) ... Done\\n\"\n        \"Removing installation 3.9.3 (cpython) ... Done\\n\"\n    )\n    assert not cpython_path_1.exists()\n    assert cpython_path_2.exists()\n    assert not cpython_path_3.exists()\n"
  },
  {
    "path": "tests/console/commands/self/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/self/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.__version__ import __version__\nfrom poetry.factory import Factory\nfrom poetry.repositories import RepositoryPool\nfrom poetry.utils.env import EnvManager\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterable\n\n    import responses\n\n    from cleo.io.io import IO\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from poetry.repositories.repository import Repository\n    from poetry.utils.env import VirtualEnv\n    from tests.helpers import TestRepository\n\n\n@pytest.fixture\ndef poetry_package() -> Package:\n    return Package(\"poetry\", __version__)\n\n\n@pytest.fixture(autouse=True)\ndef _patch_repos(\n    repo: TestRepository, installed: Repository, poetry_package: Package\n) -> None:\n    repo.add_package(poetry_package)\n    installed.add_package(poetry_package)\n\n\n@pytest.fixture()\ndef pool(repo: TestRepository) -> RepositoryPool:\n    return RepositoryPool([repo])\n\n\ndef create_pool_factory(\n    repo: Repository,\n) -> Callable[[Config, Iterable[dict[str, Any]], IO, bool], RepositoryPool]:\n    def _create_pool(\n        config: Config,\n        sources: Iterable[dict[str, Any]] = (),\n        io: IO | None = None,\n        disable_cache: bool = False,\n    ) -> RepositoryPool:\n        pool = RepositoryPool()\n        pool.add_repository(repo)\n\n        return pool\n\n    return _create_pool\n\n\n@pytest.fixture(autouse=True)\ndef setup_mocks(\n    mocker: MockerFixture,\n    tmp_venv: VirtualEnv,\n    installed: Repository,\n    pool: RepositoryPool,\n    http: responses.RequestsMock,\n    repo: Repository,\n) -> None:\n    mocker.patch.object(EnvManager, \"get_system_env\", return_value=tmp_venv)\n    mocker.patch(\n        \"poetry.repositories.repository_pool.RepositoryPool.find_packages\",\n        pool.find_packages,\n    )\n    mocker.patch(\n        \"poetry.repositories.repository_pool.RepositoryPool.package\", pool.package\n    )\n    mocker.patch(\n        \"poetry.installation.installer.Installer._get_installed\",\n        return_value=installed,\n    )\n    mocker.patch.object(Factory, \"create_pool\", side_effect=create_pool_factory(repo))\n"
  },
  {
    "path": "tests/console/commands/self/fixtures/poetry-1.0.5-darwin.sha256sum",
    "content": "be3d3b916cb47038899d6ff37e875fd08ba3fed22bcdbf5a92f3f48fd2f15da8\n"
  },
  {
    "path": "tests/console/commands/self/test_add_plugins.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.console.commands.add import AddCommand\nfrom poetry.console.commands.self.self_command import SelfCommand\nfrom poetry.factory import Factory\nfrom tests.console.commands.self.utils import get_self_command_dependencies\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"self add\")\n\n\ndef assert_plugin_add_result(\n    tester: CommandTester,\n    expected: str,\n    constraint: str,\n) -> None:\n    assert tester.io.fetch_output() == expected\n    dependencies: list[str] | None = get_self_command_dependencies()\n\n    assert dependencies\n    assert \"poetry-plugin\" in dependencies[0]\n    assert constraint in dependencies[0]\n\n\ndef test_add_no_constraint(\n    tester: CommandTester,\n    repo: TestRepository,\n) -> None:\n    repo.add_package(Package(\"poetry-plugin\", \"0.1.0\"))\n\n    tester.execute(\"poetry-plugin\")\n\n    expected = \"\"\"\\\nUsing version ^0.1.0 for poetry-plugin\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing poetry-plugin (0.1.0)\n\nWriting lock file\n\"\"\"\n    assert_plugin_add_result(tester, expected, \"(>=0.1.0,<0.2.0)\")\n\n\ndef test_add_with_constraint(\n    tester: CommandTester,\n    repo: TestRepository,\n) -> None:\n    repo.add_package(Package(\"poetry-plugin\", \"0.1.0\"))\n    repo.add_package(Package(\"poetry-plugin\", \"0.2.0\"))\n\n    tester.execute(\"poetry-plugin@^0.2.0\")\n\n    expected = \"\"\"\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing poetry-plugin (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(tester, expected, \"(>=0.2.0,<0.3.0)\")\n\n\ndef test_add_with_git_constraint(\n    tester: CommandTester,\n    repo: TestRepository,\n) -> None:\n    repo.add_package(Package(\"pendulum\", \"2.0.5\"))\n\n    tester.execute(\"git+https://github.com/demo/poetry-plugin.git\")\n\n    expected = \"\"\"\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (2.0.5)\n  - Installing poetry-plugin (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(\n        tester, expected, \"https://github.com/demo/poetry-plugin.git\"\n    )\n\n\ndef test_add_with_git_constraint_with_extras(\n    tester: CommandTester,\n    repo: TestRepository,\n) -> None:\n    repo.add_package(Package(\"pendulum\", \"2.0.5\"))\n    repo.add_package(Package(\"tomlkit\", \"0.7.0\"))\n\n    tester.execute(\"git+https://github.com/demo/poetry-plugin.git[foo]\")\n\n    expected = \"\"\"\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 3 installs, 0 updates, 0 removals\n\n  - Installing pendulum (2.0.5)\n  - Installing tomlkit (0.7.0)\n  - Installing poetry-plugin (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(\n        tester,\n        expected,\n        \"poetry-plugin[foo] @ git+https://github.com/demo/poetry-plugin.git\",\n    )\n\n\n@pytest.mark.parametrize(\n    \"url, rev\",\n    [\n        (\"git+https://github.com/demo/poetry-plugin2.git#subdirectory=subdir\", None),\n        (\n            \"git+https://github.com/demo/poetry-plugin2.git@master#subdirectory=subdir\",\n            \"master\",\n        ),\n    ],\n)\ndef test_add_with_git_constraint_with_subdirectory(\n    url: str,\n    rev: str | None,\n    tester: CommandTester,\n    repo: TestRepository,\n) -> None:\n    repo.add_package(Package(\"pendulum\", \"2.0.5\"))\n\n    tester.execute(url)\n\n    expected = \"\"\"\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (2.0.5)\n  - Installing poetry-plugin (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(tester, expected, url)\n\n\ndef test_add_existing_plugin_warns_about_no_operation(\n    tester: CommandTester,\n    repo: TestRepository,\n    installed: TestRepository,\n) -> None:\n    pyproject = SelfCommand.get_default_system_pyproject_file()\n    with open(pyproject, \"w\", encoding=\"utf-8\", newline=\"\") as f:\n        f.write(\n            f\"\"\"\\\n[tool.poetry]\nname = \"poetry-instance\"\nversion = \"1.2.0\"\ndescription = \"Python dependency management and packaging made easy.\"\nauthors = []\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.{SelfCommand.ADDITIONAL_PACKAGE_GROUP}.dependencies]\npoetry-plugin = \"^1.2.3\"\n\"\"\"\n        )\n\n    installed.add_package(Package(\"poetry-plugin\", \"1.2.3\"))\n\n    repo.add_package(Package(\"poetry-plugin\", \"1.2.3\"))\n\n    tester.execute(\"poetry-plugin\")\n\n    assert isinstance(tester.command, AddCommand)\n    expected = f\"\"\"\\\nThe following packages are already present in the pyproject.toml and will be\\\n skipped:\n\n  - poetry-plugin\n{tester.command._hint_update_packages}\nNothing to add.\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_add_existing_plugin_updates_if_requested(\n    tester: CommandTester,\n    repo: TestRepository,\n    installed: TestRepository,\n) -> None:\n    pyproject = SelfCommand.get_default_system_pyproject_file()\n    with open(pyproject, \"w\", encoding=\"utf-8\", newline=\"\") as f:\n        f.write(\n            f\"\"\"\\\n[tool.poetry]\nname = \"poetry-instance\"\nversion = \"1.2.0\"\ndescription = \"Python dependency management and packaging made easy.\"\nauthors = []\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[dependency-groups]\n{SelfCommand.ADDITIONAL_PACKAGE_GROUP} = [\n    \"poetry-plugin (>=1.2.3,<2.0.0)\"\n]\n\"\"\"\n        )\n\n    installed.add_package(Package(\"poetry-plugin\", \"1.2.3\"))\n\n    repo.add_package(Package(\"poetry-plugin\", \"1.2.3\"))\n    repo.add_package(Package(\"poetry-plugin\", \"2.3.4\"))\n\n    tester.execute(\"poetry-plugin@latest\")\n\n    expected = \"\"\"\\\nUsing version ^2.3.4 for poetry-plugin\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 0 installs, 1 update, 0 removals\n\n  - Updating poetry-plugin (1.2.3 -> 2.3.4)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(tester, expected, \"(>=2.3.4,<3.0.0)\")\n\n\ndef test_adding_a_plugin_can_update_poetry_dependencies_if_needed(\n    tester: CommandTester,\n    repo: TestRepository,\n    installed: TestRepository,\n    poetry_package: Package,\n) -> None:\n    poetry_package.add_dependency(Factory.create_dependency(\"tomlkit\", \"^0.7.0\"))\n\n    plugin_package = Package(\"poetry-plugin\", \"1.2.3\")\n    plugin_package.add_dependency(Factory.create_dependency(\"tomlkit\", \"^0.7.2\"))\n\n    installed.add_package(poetry_package)\n    installed.add_package(Package(\"tomlkit\", \"0.7.1\"))\n\n    repo.add_package(plugin_package)\n    repo.add_package(Package(\"tomlkit\", \"0.7.1\"))\n    repo.add_package(Package(\"tomlkit\", \"0.7.2\"))\n\n    tester.execute(\"poetry-plugin\")\n\n    expected = \"\"\"\\\nUsing version ^1.2.3 for poetry-plugin\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 1 update, 0 removals\n\n  - Updating tomlkit (0.7.1 -> 0.7.2)\n  - Installing poetry-plugin (1.2.3)\n\nWriting lock file\n\"\"\"\n\n    assert_plugin_add_result(tester, expected, \"(>=1.2.3,<2.0.0)\")\n"
  },
  {
    "path": "tests/console/commands/self/test_install.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.console.commands.self.install import SelfInstallCommand\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef command() -> str:\n    return \"self install\"\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory, command: str) -> CommandTester:\n    return command_tester_factory(command)\n\n\n@pytest.mark.parametrize(\n    \"pyproject_content\",\n    (\n        None,\n        \"\"\"\\\n[tool.poetry]\nname = \"poetry-instance\"\nversion = \"1.2\"\ndescription = \"\"\nauthors = []\nlicense = \"\"\n# no package-mode -> defaults to true\n\n[tool.poetry.dependencies]\npython = \"3.9\"\npoetry = \"1.2\"\n\"\"\",\n    ),\n)\ndef test_self_install(\n    tester: CommandTester,\n    pyproject_content: str | None,\n) -> None:\n    command = tester.command\n    assert isinstance(command, SelfInstallCommand)\n    pyproject_path = command.system_pyproject\n    if pyproject_content:\n        pyproject_path.write_text(pyproject_content, encoding=\"utf-8\")\n    else:\n        assert not pyproject_path.exists()\n\n    tester.execute()\n\n    output = tester.io.fetch_output()\n    assert output.startswith(\"Updating dependencies\")\n    assert output.endswith(\"Writing lock file\\n\")\n    assert tester.io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"sync\", [True, False])\ndef test_sync_deprecation(tester: CommandTester, sync: bool) -> None:\n    tester.execute(\"--sync\" if sync else \"\")\n\n    error = tester.io.fetch_error()\n    if sync:\n        assert \"deprecated\" in error\n        assert \"poetry self sync\" in error\n    else:\n        assert error == \"\"\n"
  },
  {
    "path": "tests/console/commands/self/test_remove_plugins.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\nimport tomlkit\n\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.commands.self.self_command import SelfCommand\nfrom poetry.factory import Factory\nfrom tests.console.commands.self.utils import get_self_command_dependencies\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.repositories import Repository\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"self remove\")\n\n\n@pytest.fixture(autouse=True)\ndef install_plugin(installed: Repository) -> None:\n    package = ProjectPackage(\"poetry-instance\", __version__)\n    plugin = Package(\"poetry-plugin\", \"1.2.3\")\n\n    content = Factory.create_legacy_pyproject_from_package(package)\n    content[\"dependency-groups\"] = tomlkit.table()\n    content[\"dependency-groups\"][SelfCommand.ADDITIONAL_PACKAGE_GROUP] = tomlkit.array(  # type: ignore[index]\n        \"[\\n]\"\n    )\n    content[\"dependency-groups\"][SelfCommand.ADDITIONAL_PACKAGE_GROUP].append(  # type: ignore[index, union-attr, call-arg]\n        Dependency(plugin.name, \"^1.2.3\").to_pep_508()\n    )\n\n    system_pyproject_file = SelfCommand.get_default_system_pyproject_file()\n    with open(system_pyproject_file, \"w\", encoding=\"utf-8\", newline=\"\") as f:\n        f.write(content.as_string())\n\n    lock_content = {\n        \"package\": [\n            {\n                \"name\": \"poetry-plugin\",\n                \"version\": \"1.2.3\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"files\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": \"2.0\",\n            \"python-versions\": \"^3.6\",\n            \"content-hash\": \"123456789\",\n        },\n    }\n    system_pyproject_file.parent.joinpath(\"poetry.lock\").write_text(\n        tomlkit.dumps(lock_content), encoding=\"utf-8\"\n    )\n\n    installed.add_package(plugin)\n\n\ndef test_remove_installed_package(tester: CommandTester) -> None:\n    tester.execute(\"poetry-plugin\")\n\n    expected = \"\"\"\\\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 0 installs, 0 updates, 1 removal\n\n  - Removing poetry-plugin (1.2.3)\n\nWriting lock file\n\"\"\"\n    assert tester.io.fetch_output() == expected\n\n    dependencies = get_self_command_dependencies()\n\n    assert not dependencies\n\n\ndef test_remove_installed_package_dry_run(tester: CommandTester) -> None:\n    tester.execute(\"poetry-plugin --dry-run\")\n\n    expected = f\"\"\"\\\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 0 installs, 0 updates, 1 removal, 1 skipped\n\n  - Removing poetry-plugin (1.2.3)\n  - Installing poetry ({__version__}): Skipped for the following reason: Already \\\ninstalled\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n    dependencies = get_self_command_dependencies()\n\n    assert dependencies\n    assert len(dependencies) == 1\n    assert \"poetry-plugin\" in dependencies[0]\n"
  },
  {
    "path": "tests/console/commands/self/test_self_command.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.commands.self.self_command import SelfCommand\nfrom poetry.factory import Factory\n\n\n@pytest.fixture\ndef example_system_pyproject() -> str:\n    package = ProjectPackage(\"poetry-instance\", __version__)\n    plugin = Package(\"poetry-plugin\", \"1.2.3\")\n\n    package.add_dependency(\n        Dependency(plugin.name, \"^1.2.3\", groups=[SelfCommand.ADDITIONAL_PACKAGE_GROUP])\n    )\n    content = Factory.create_legacy_pyproject_from_package(package)\n    return content.as_string().rstrip(\"\\n\")\n\n\n@pytest.mark.parametrize(\"existing_newlines\", [0, 2])\ndef test_generate_system_pyproject_trailing_newline(\n    existing_newlines: int,\n    example_system_pyproject: str,\n) -> None:\n    cmd = SelfCommand()\n    cmd.system_pyproject.write_text(\n        example_system_pyproject + \"\\n\" * existing_newlines, encoding=\"utf-8\"\n    )\n    cmd.generate_system_pyproject()\n    generated = cmd.system_pyproject.read_text(encoding=\"utf-8\")\n\n    assert len(generated) - len(generated.rstrip(\"\\n\")) == existing_newlines\n\n\ndef test_generate_system_pyproject_carriage_returns(\n    example_system_pyproject: str,\n) -> None:\n    cmd = SelfCommand()\n    cmd.system_pyproject.write_text(example_system_pyproject + \"\\n\", encoding=\"utf-8\")\n    cmd.generate_system_pyproject()\n\n    with open(\n        cmd.system_pyproject, newline=\"\", encoding=\"utf-8\"\n    ) as f:  # do not translate newlines\n        generated = f.read()\n\n    assert \"\\r\\r\" not in generated\n"
  },
  {
    "path": "tests/console/commands/self/test_show.py",
    "content": "from __future__ import annotations\n\nimport json\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\nimport tomlkit\n\nfrom poetry.__version__ import __version__\nfrom poetry.console.commands.self.self_command import SelfCommand\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"self show\")\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--format json\", \"--format text\"])\ndef test_show_format(tester: CommandTester, options: str) -> None:\n    pyproject_content = {\n        \"tool\": {\n            \"poetry\": {\n                \"name\": \"poetry-instance\",\n                \"version\": __version__,\n                \"dependencies\": {\"python\": \"^3.9\", \"poetry\": __version__},\n            }\n        }\n    }\n    lock_content = {\n        \"package\": [\n            {\n                \"name\": \"poetry\",\n                \"version\": __version__,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"files\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": \"2.0\",\n            \"python-versions\": \"^3.9\",\n            \"content-hash\": \"123456789\",\n        },\n    }\n    if \"json\" in options:\n        expected = json.dumps(\n            [\n                {\n                    \"name\": \"poetry\",\n                    \"installed_status\": \"installed\",\n                    \"version\": __version__,\n                    \"description\": \"\",\n                }\n            ]\n        )\n    else:\n        expected = f\"poetry {__version__}\"\n    system_pyproject_file = SelfCommand.get_default_system_pyproject_file()\n    system_pyproject_file.write_text(tomlkit.dumps(pyproject_content), encoding=\"utf-8\")\n    system_pyproject_file.parent.joinpath(\"poetry.lock\").write_text(\n        tomlkit.dumps(lock_content), encoding=\"utf-8\"\n    )\n    assert tester.execute(options) == 0\n    assert tester.io.fetch_output().strip() == expected\n\n\ndef test_self_show_errors_without_lock_file(tester: CommandTester) -> None:\n    system_pyproject_file = SelfCommand.get_default_system_pyproject_file()\n    system_pyproject_file.write_text(\n        tomlkit.dumps(\n            {\n                \"tool\": {\n                    \"poetry\": {\n                        \"name\": \"poetry-instance\",\n                        \"version\": __version__,\n                        \"dependencies\": {\"python\": \"^3.9\", \"poetry\": __version__},\n                    }\n                }\n            }\n        ),\n        encoding=\"utf-8\",\n    )\n    system_pyproject_file.parent.joinpath(\"poetry.lock\").unlink(missing_ok=True)\n\n    assert tester.execute() == 1\n    assert (\n        tester.io.fetch_error()\n        == \"Error: poetry.lock not found. Run `poetry self lock` to create it.\\n\"\n    )\n"
  },
  {
    "path": "tests/console/commands/self/test_show_plugins.py",
    "content": "from __future__ import annotations\n\nfrom importlib import metadata\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\n\nfrom poetry.plugins.application_plugin import ApplicationPlugin\nfrom poetry.plugins.plugin import Plugin\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from os import PathLike\n    from pathlib import Path\n\n    from cleo.io.io import IO\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.plugins.base_plugin import BasePlugin\n    from poetry.poetry import Poetry\n    from poetry.repositories import Repository\n    from poetry.utils.env import Env\n    from tests.helpers import PoetryTestApplication\n    from tests.types import CommandTesterFactory\n\n\nclass DoNothingPlugin(Plugin):\n    def activate(self, poetry: Poetry, io: IO) -> None:\n        pass\n\n\nclass EntryPoint(metadata.EntryPoint):\n    def load(self) -> type[BasePlugin]:\n        if self.group == ApplicationPlugin.group:\n            return ApplicationPlugin\n\n        return DoNothingPlugin\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"self show plugins\")\n\n\n@pytest.fixture()\ndef plugin_package_requires_dist() -> list[str]:\n    return []\n\n\n@pytest.fixture()\ndef plugin_package(plugin_package_requires_dist: list[str]) -> Package:\n    package = Package(\"poetry-plugin\", \"1.2.3\")\n\n    for requirement in plugin_package_requires_dist:\n        package.add_dependency(Dependency.create_from_pep_508(requirement))\n\n    return package\n\n\n@pytest.fixture()\ndef plugin_distro(plugin_package: Package, tmp_path: Path) -> metadata.Distribution:\n    class MockDistribution(metadata.Distribution):\n        def read_text(self, filename: str) -> str | None:\n            if filename == \"METADATA\":\n                return \"\\n\".join(\n                    [\n                        f\"Name: {plugin_package.name}\",\n                        f\"Version: {plugin_package.version}\",\n                        *[\n                            f\"Requires-Dist: {dep.to_pep_508()}\"\n                            for dep in plugin_package.requires\n                        ],\n                    ]\n                )\n            return None\n\n        def locate_file(self, path: str | PathLike[str]) -> Path:\n            return tmp_path / path\n\n    return MockDistribution()  # type: ignore[no-untyped-call]\n\n\n@pytest.fixture\ndef entry_point_name() -> str:\n    return \"poetry-plugin\"\n\n\n@pytest.fixture\ndef entry_point_values_by_group() -> dict[str, list[str]]:\n    return {}\n\n\n@pytest.fixture\ndef entry_points(\n    entry_point_name: str,\n    entry_point_values_by_group: dict[str, list[str]],\n    plugin_distro: metadata.Distribution,\n) -> Callable[..., list[metadata.EntryPoint]]:\n    by_group = {\n        key: [\n            EntryPoint(  # type: ignore[no-untyped-call]\n                name=entry_point_name,\n                group=key,\n                value=value,\n            )._for(  # type: ignore[attr-defined]\n                plugin_distro\n            )\n            for value in values\n        ]\n        for key, values in entry_point_values_by_group.items()\n    }\n\n    def _entry_points(**params: Any) -> list[metadata.EntryPoint]:\n        group = params.get(\"group\")\n\n        if group not in by_group:\n            return []\n\n        eps: list[metadata.EntryPoint] = by_group[group]\n\n        return eps\n\n    return _entry_points\n\n\n@pytest.fixture(autouse=True)\ndef mock_metadata_entry_points(\n    plugin_package: Package,\n    plugin_distro: metadata.Distribution,\n    installed: Repository,\n    mocker: MockerFixture,\n    tmp_venv: Env,\n    entry_points: Callable[..., metadata.EntryPoint],\n) -> None:\n    installed.add_package(plugin_package)\n\n    mocker.patch.object(\n        tmp_venv.site_packages, \"find_distribution\", return_value=plugin_distro\n    )\n    mocker.patch.object(metadata, \"entry_points\", entry_points)\n\n\n@pytest.mark.parametrize(\"entry_point_name\", [\"poetry-plugin\", \"not-package-name\"])\n@pytest.mark.parametrize(\n    \"entry_point_values_by_group\",\n    [\n        {\n            ApplicationPlugin.group: [\"FirstApplicationPlugin\"],\n            Plugin.group: [\"FirstPlugin\"],\n        }\n    ],\n)\ndef test_show_displays_installed_plugins(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    tester.execute(\"\")\n\n    expected = \"\"\"\n  - poetry-plugin (1.2.3)\n      1 plugin and 1 application plugin\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    \"entry_point_values_by_group\",\n    [\n        {\n            ApplicationPlugin.group: [\n                \"FirstApplicationPlugin\",\n                \"SecondApplicationPlugin\",\n            ],\n            Plugin.group: [\"FirstPlugin\", \"SecondPlugin\"],\n        }\n    ],\n)\ndef test_show_displays_installed_plugins_with_multiple_plugins(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    tester.execute(\"\")\n\n    expected = \"\"\"\n  - poetry-plugin (1.2.3)\n      2 plugins and 2 application plugins\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    \"plugin_package_requires_dist\", [[\"foo (>=1.2.3)\", \"bar (<4.5.6)\"]]\n)\n@pytest.mark.parametrize(\n    \"entry_point_values_by_group\",\n    [\n        {\n            ApplicationPlugin.group: [\"FirstApplicationPlugin\"],\n            Plugin.group: [\"FirstPlugin\"],\n        }\n    ],\n)\ndef test_show_displays_installed_plugins_with_dependencies(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    tester.execute(\"\")\n\n    expected = \"\"\"\n  - poetry-plugin (1.2.3)\n      1 plugin and 1 application plugin\n\n      Dependencies\n        - foo (>=1.2.3)\n        - bar (<4.5.6)\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/self/test_sync.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.exceptions import CleoNoSuchOptionError\n\nfrom poetry.console.commands.self.sync import SelfSyncCommand\n\n# import all tests from the self install command\n# and run them for sync by overriding the command fixture\nfrom tests.console.commands.self.test_install import *  # noqa: F403\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n\n@pytest.fixture  # type: ignore[no-redef]\ndef command() -> str:\n    return \"self sync\"\n\n\n@pytest.mark.skip(\"Only relevant for `poetry self install`\")  # type: ignore[no-redef]\ndef test_sync_deprecation() -> None:\n    \"\"\"The only test from the self install command that does not work for self sync.\"\"\"\n\n\ndef test_sync_option_not_available(tester: CommandTester) -> None:\n    with pytest.raises(CleoNoSuchOptionError):\n        tester.execute(\"--sync\")\n\n\ndef test_synced_installer(tester: CommandTester, mocker: MockerFixture) -> None:\n    assert isinstance(tester.command, SelfSyncCommand)\n    mock = mocker.patch(\n        \"poetry.console.commands.install.InstallCommand.installer\",\n        new_callable=mocker.PropertyMock,\n    )\n\n    tester.execute()\n\n    mock.return_value.requires_synchronization.assert_called_with(True)\n"
  },
  {
    "path": "tests/console/commands/self/test_update.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.package import Package\n\nfrom poetry.__version__ import __version__\nfrom poetry.factory import Factory\nfrom poetry.installation.executor import Executor\nfrom poetry.installation.wheel_installer import WheelInstaller\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture\ndef setup(mocker: MockerFixture, fixture_dir: FixtureDirGetter) -> None:\n    mocker.patch.object(\n        Executor,\n        \"_download\",\n        return_value=fixture_dir(\"distributions\").joinpath(\n            \"demo-0.1.2-py2.py3-none-any.whl\"\n        ),\n    )\n\n    mocker.patch.object(WheelInstaller, \"install\")\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"self update\")\n\n\ndef test_self_update_can_update_from_recommended_installation(\n    tester: CommandTester,\n    repo: TestRepository,\n    installed: TestRepository,\n) -> None:\n    new_version = Version.parse(__version__).next_minor().text\n\n    old_poetry = Package(\"poetry\", __version__)\n    old_poetry.add_dependency(Factory.create_dependency(\"cleo\", \"^0.8.2\"))\n\n    new_poetry = Package(\"poetry\", new_version)\n    new_poetry.add_dependency(Factory.create_dependency(\"cleo\", \"^1.0.0\"))\n\n    installed.add_package(old_poetry)\n    installed.add_package(Package(\"cleo\", \"0.8.2\"))\n\n    repo.add_package(new_poetry)\n    repo.add_package(Package(\"cleo\", \"1.0.0\"))\n\n    tester.execute()\n\n    expected_output = f\"\"\"\\\nUpdating Poetry version ...\n\nUsing version ^{new_version} for poetry\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 0 installs, 2 updates, 0 removals\n\n  - Updating cleo (0.8.2 -> 1.0.0)\n  - Updating poetry ({__version__} -> {new_version})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected_output\n"
  },
  {
    "path": "tests/console/commands/self/utils.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import Any\n\nfrom tomlkit.items import Array\n\nfrom poetry.factory import Factory\n\n\ndef get_self_command_dependencies(locked: bool = True) -> Array | None:\n    from poetry.console.commands.self.self_command import SelfCommand\n    from poetry.locations import CONFIG_DIR\n\n    system_pyproject_file = SelfCommand.get_default_system_pyproject_file()\n\n    assert system_pyproject_file.exists()\n    assert system_pyproject_file.parent == Path(CONFIG_DIR)\n\n    if locked:\n        assert system_pyproject_file.parent.joinpath(\"poetry.lock\").exists()\n\n    poetry = Factory().create_poetry(system_pyproject_file.parent, disable_plugins=True)\n\n    pyproject: dict[str, Any] = poetry.file.read()\n    content = pyproject.get(\"dependency-groups\", {})\n\n    if SelfCommand.ADDITIONAL_PACKAGE_GROUP not in content:\n        return None\n\n    dependencies = content[SelfCommand.ADDITIONAL_PACKAGE_GROUP]\n    assert isinstance(dependencies, Array)\n    return dependencies\n"
  },
  {
    "path": "tests/console/commands/source/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/commands/source/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.config.source import Source\nfrom poetry.repositories.repository_pool import Priority\n\n\nif TYPE_CHECKING:\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef source_one() -> Source:\n    return Source(name=\"one\", url=\"https://one.com\")\n\n\n@pytest.fixture\ndef source_two() -> Source:\n    return Source(name=\"two\", url=\"https://two.com\")\n\n\n@pytest.fixture\ndef source_primary() -> Source:\n    return Source(name=\"primary\", url=\"https://primary.com\", priority=Priority.PRIMARY)\n\n\n@pytest.fixture\ndef source_supplemental() -> Source:\n    return Source(\n        name=\"supplemental\",\n        url=\"https://supplemental.com\",\n        priority=Priority.SUPPLEMENTAL,\n    )\n\n\n@pytest.fixture\ndef source_explicit() -> Source:\n    return Source(\n        name=\"explicit\", url=\"https://explicit.com\", priority=Priority.EXPLICIT\n    )\n\n\n@pytest.fixture\ndef source_pypi() -> Source:\n    return Source(name=\"PyPI\")\n\n\n@pytest.fixture\ndef source_pypi_explicit() -> Source:\n    return Source(name=\"PyPI\", priority=Priority.EXPLICIT)\n\n\n_existing_source = Source(name=\"existing\", url=\"https://existing.com\")\n\n\n@pytest.fixture\ndef source_existing() -> Source:\n    return _existing_source\n\n\nPYPROJECT_WITHOUT_POETRY_SECTION = \"\"\"\n[project]\nname = \"source-command-test\"\nversion = \"0.1.0\"\n\"\"\"\n\n\nPYPROJECT_WITHOUT_SOURCES = \"\"\"\n[tool.poetry]\nname = \"source-command-test\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Poetry Tester <tester@poetry.org>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.9\"\n\n[tool.poetry.dev-dependencies]\n\"\"\"\n\n\nPYPROJECT_WITH_SOURCES = f\"\"\"{PYPROJECT_WITHOUT_SOURCES}\n\n[[tool.poetry.source]]\nname = \"{_existing_source.name}\"\nurl = \"{_existing_source.url}\"\n\"\"\"\n\n\nPYPROJECT_WITH_PYPI = f\"\"\"{PYPROJECT_WITHOUT_SOURCES}\n\n[[tool.poetry.source]]\nname = \"PyPI\"\n\"\"\"\n\n\nPYPROJECT_WITH_PYPI_AND_OTHER = f\"\"\"{PYPROJECT_WITH_SOURCES}\n\n[[tool.poetry.source]]\nname = \"PyPI\"\n\"\"\"\n\n\n@pytest.fixture\ndef poetry_without_poetry_section(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(pyproject_content=PYPROJECT_WITHOUT_POETRY_SECTION)\n\n\n@pytest.fixture\ndef poetry_without_source(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(pyproject_content=PYPROJECT_WITHOUT_SOURCES)\n\n\n@pytest.fixture\ndef poetry_with_source(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(pyproject_content=PYPROJECT_WITH_SOURCES)\n\n\n@pytest.fixture\ndef poetry_with_pypi(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(pyproject_content=PYPROJECT_WITH_PYPI)\n\n\n@pytest.fixture\ndef poetry_with_pypi_and_other(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(pyproject_content=PYPROJECT_WITH_PYPI_AND_OTHER)\n\n\n@pytest.fixture\ndef add_multiple_sources(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_source: Poetry,\n    source_one: Source,\n    source_two: Source,\n) -> None:\n    add = command_tester_factory(\"source add\", poetry=poetry_with_source)\n    for source in [source_one, source_two]:\n        add.execute(f\"{source.name} {source.url}\")\n\n\n@pytest.fixture\ndef add_all_source_types(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_source: Poetry,\n    source_primary: Source,\n    source_supplemental: Source,\n    source_explicit: Source,\n) -> None:\n    add = command_tester_factory(\"source add\", poetry=poetry_with_source)\n    for source in [\n        source_primary,\n        source_supplemental,\n        source_explicit,\n    ]:\n        add.execute(f\"{source.name} {source.url} --priority={source.name}\")\n"
  },
  {
    "path": "tests/console/commands/source/test_add.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.config.source import Source\nfrom poetry.repositories.repository_pool import Priority\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(\n    command_tester_factory: CommandTesterFactory, poetry_with_source: Poetry\n) -> CommandTester:\n    return command_tester_factory(\"source add\", poetry=poetry_with_source)\n\n\ndef assert_source_added(\n    tester: CommandTester,\n    poetry: Poetry,\n    added_source: Source,\n    existing_sources: Iterable[Source] = (),\n) -> None:\n    expected_error = \"\"\n    if tester.io.input.option(\"priority\") is None:\n        expected_error = f\"The default priority will change to supplemental in a future release.\\n{expected_error}\"\n    assert tester.io.fetch_error().strip() == expected_error.strip()\n\n    expected_output = f\"Adding source with name {added_source.name}.\"\n    assert tester.io.fetch_output().strip() == expected_output\n\n    poetry.pyproject.reload()\n    sources = poetry.get_sources()\n    assert sources == [*existing_sources, added_source]\n    assert tester.status_code == 0\n\n\ndef test_source_add_simple(\n    tester: CommandTester,\n    source_existing: Source,\n    source_one: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    tester.execute(f\"{source_one.name} {source_one.url}\")\n    assert_source_added(tester, poetry_with_source, source_one, [source_existing])\n\n\ndef test_source_add_simple_without_existing_sources(\n    tester: CommandTester,\n    source_one: Source,\n    poetry_without_source: Poetry,\n) -> None:\n    tester.execute(f\"{source_one.name} {source_one.url}\")\n    assert_source_added(tester, poetry_without_source, source_one)\n\n\ndef test_source_add_simple_without_existing_poetry_section(\n    tester: CommandTester,\n    source_one: Source,\n    poetry_without_poetry_section: Poetry,\n) -> None:\n    tester.execute(f\"{source_one.name} {source_one.url}\")\n    assert_source_added(tester, poetry_without_poetry_section, source_one)\n\n\ndef test_source_add_supplemental(\n    tester: CommandTester,\n    source_existing: Source,\n    source_supplemental: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    tester.execute(\n        f\"--priority=supplemental {source_supplemental.name} {source_supplemental.url}\"\n    )\n    assert_source_added(\n        tester, poetry_with_source, source_supplemental, [source_existing]\n    )\n\n\ndef test_source_add_explicit(\n    tester: CommandTester,\n    source_existing: Source,\n    source_explicit: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    tester.execute(f\"--priority=explicit {source_explicit.name} {source_explicit.url}\")\n    assert_source_added(tester, poetry_with_source, source_explicit, [source_existing])\n\n\ndef test_source_add_error_no_url(tester: CommandTester) -> None:\n    tester.execute(\"foo\")\n    assert (\n        tester.io.fetch_error().strip()\n        == \"A custom source cannot be added without a URL.\"\n    )\n    assert tester.status_code == 1\n\n\ndef test_source_add_error_pypi(tester: CommandTester) -> None:\n    tester.execute(\"pypi https://test.pypi.org/simple/\")\n    assert (\n        tester.io.fetch_error().strip() == \"The URL of PyPI is fixed and cannot be set.\"\n    )\n    assert tester.status_code == 1\n\n\n@pytest.mark.parametrize(\"name\", [\"pypi\", \"PyPI\"])\ndef test_source_add_pypi(\n    name: str,\n    tester: CommandTester,\n    source_existing: Source,\n    source_pypi: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    tester.execute(name)\n    assert_source_added(tester, poetry_with_source, source_pypi, [source_existing])\n\n\ndef test_source_add_pypi_explicit(\n    tester: CommandTester,\n    source_existing: Source,\n    source_pypi_explicit: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    tester.execute(\"--priority=explicit PyPI\")\n    assert_source_added(\n        tester, poetry_with_source, source_pypi_explicit, [source_existing]\n    )\n\n\n@pytest.mark.parametrize(\"modifier\", [\"lower\", \"upper\"])\ndef test_source_add_existing_no_change_except_case_of_name(\n    modifier: str,\n    tester: CommandTester,\n    source_existing: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    name = getattr(source_existing.name, modifier)()\n    tester.execute(f\"--priority=primary {name} {source_existing.url}\")\n    assert (\n        tester.io.fetch_output().strip()\n        == f\"Source with name {name} already exists. Updating.\"\n    )\n\n    poetry_with_source.pyproject.reload()\n    sources = poetry_with_source.get_sources()\n\n    assert len(sources) == 1\n    assert sources[0].name == getattr(source_existing.name, modifier)()\n    assert sources[0].url == source_existing.url\n    assert sources[0].priority == source_existing.priority\n\n\n@pytest.mark.parametrize(\"modifier\", [\"lower\", \"upper\"])\ndef test_source_add_existing_updating(\n    modifier: str,\n    tester: CommandTester,\n    source_existing: Source,\n    poetry_with_source: Poetry,\n) -> None:\n    name = getattr(source_existing.name, modifier)()\n    tester.execute(f\"--priority=supplemental {name} {source_existing.url}\")\n    assert (\n        tester.io.fetch_output().strip()\n        == f\"Source with name {name} already exists. Updating.\"\n    )\n\n    poetry_with_source.pyproject.reload()\n    sources = poetry_with_source.get_sources()\n\n    assert len(sources) == 1\n    assert sources[0] != source_existing\n    expected_source = Source(\n        name=name, url=source_existing.url, priority=Priority.SUPPLEMENTAL\n    )\n    assert sources[0] == expected_source\n"
  },
  {
    "path": "tests/console/commands/source/test_remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.config.source import Source\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_source: Poetry,\n    add_multiple_sources: None,\n) -> CommandTester:\n    return command_tester_factory(\"source remove\", poetry=poetry_with_source)\n\n\n@pytest.fixture\ndef tester_pypi(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_pypi: Poetry,\n) -> CommandTester:\n    return command_tester_factory(\"source remove\", poetry=poetry_with_pypi)\n\n\n@pytest.fixture\ndef tester_pypi_and_other(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_pypi_and_other: Poetry,\n) -> CommandTester:\n    return command_tester_factory(\"source remove\", poetry=poetry_with_pypi_and_other)\n\n\n@pytest.mark.parametrize(\"modifier\", [\"lower\", \"upper\"])\ndef test_source_remove_simple(\n    tester: CommandTester,\n    poetry_with_source: Poetry,\n    source_existing: Source,\n    source_one: Source,\n    source_two: Source,\n    modifier: str,\n) -> None:\n    tester.execute(getattr(f\"{source_existing.name}\", modifier)())\n    assert (\n        tester.io.fetch_output().strip()\n        == f\"Removing source with name {source_existing.name}.\"\n    )\n\n    poetry_with_source.pyproject.reload()\n    sources = poetry_with_source.get_sources()\n    assert sources == [source_one, source_two]\n\n    assert tester.status_code == 0\n\n\n@pytest.mark.parametrize(\"name\", [\"pypi\", \"PyPI\"])\ndef test_source_remove_pypi(\n    name: str, tester_pypi: CommandTester, poetry_with_pypi: Poetry\n) -> None:\n    tester_pypi.execute(name)\n    assert tester_pypi.io.fetch_output().strip() == \"Removing source with name PyPI.\"\n\n    poetry_with_pypi.pyproject.reload()\n    sources = poetry_with_pypi.get_sources()\n    assert sources == []\n\n    assert tester_pypi.status_code == 0\n\n\n@pytest.mark.parametrize(\"name\", [\"pypi\", \"PyPI\"])\ndef test_source_remove_pypi_and_other(\n    name: str,\n    tester_pypi_and_other: CommandTester,\n    poetry_with_pypi_and_other: Poetry,\n    source_existing: Source,\n) -> None:\n    tester_pypi_and_other.execute(name)\n    assert (\n        tester_pypi_and_other.io.fetch_output().strip()\n        == \"Removing source with name PyPI.\"\n    )\n\n    poetry_with_pypi_and_other.pyproject.reload()\n    sources = poetry_with_pypi_and_other.get_sources()\n    assert sources == [source_existing]\n\n    assert tester_pypi_and_other.status_code == 0\n\n\n@pytest.mark.parametrize(\"name\", [\"foo\", \"pypi\", \"PyPI\"])\ndef test_source_remove_error(name: str, tester: CommandTester) -> None:\n    tester.execute(name)\n    assert tester.io.fetch_error().strip() == f\"Source with name {name} was not found.\"\n    assert tester.status_code == 1\n"
  },
  {
    "path": "tests/console/commands/source/test_show.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.repositories.pypi_repository import PyPiRepository\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.config.source import Source\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_source: Poetry,\n    add_multiple_sources: None,\n) -> CommandTester:\n    return command_tester_factory(\"source show\", poetry=poetry_with_source)\n\n\n@pytest.fixture\ndef tester_no_sources(\n    command_tester_factory: CommandTesterFactory,\n    poetry_without_source: Poetry,\n) -> CommandTester:\n    return command_tester_factory(\"source show\", poetry=poetry_without_source)\n\n\n@pytest.fixture\ndef tester_pypi(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_pypi: Poetry,\n) -> CommandTester:\n    return command_tester_factory(\"source show\", poetry=poetry_with_pypi)\n\n\n@pytest.fixture\ndef tester_pypi_and_other(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_pypi_and_other: Poetry,\n) -> CommandTester:\n    return command_tester_factory(\"source show\", poetry=poetry_with_pypi_and_other)\n\n\n@pytest.fixture\ndef tester_all_types(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_source: Poetry,\n    add_all_source_types: None,\n) -> CommandTester:\n    return command_tester_factory(\"source show\", poetry=poetry_with_source)\n\n\ndef test_source_show_simple(tester: CommandTester) -> None:\n    tester.execute(\"\")\n\n    expected = \"\"\"\\\nname      : existing\nurl       : https://existing.com\npriority  : primary\n\nname      : one\nurl       : https://one.com\npriority  : primary\n\nname      : two\nurl       : https://two.com\npriority  : primary\n\"\"\".splitlines()\n    assert [\n        line.strip() for line in tester.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester.status_code == 0\n\n\n@pytest.mark.parametrize(\"modifier\", [\"lower\", \"upper\"])\ndef test_source_show_one(\n    tester: CommandTester, source_one: Source, modifier: str\n) -> None:\n    tester.execute(getattr(f\"{source_one.name}\", modifier)())\n\n    expected = \"\"\"\\\nname      : one\nurl       : https://one.com\npriority  : primary\n\"\"\".splitlines()\n    assert [\n        line.strip() for line in tester.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester.status_code == 0\n\n\n@pytest.mark.parametrize(\"modifier\", [\"lower\", \"upper\"])\ndef test_source_show_two(\n    tester: CommandTester, source_one: Source, source_two: Source, modifier: str\n) -> None:\n    tester.execute(getattr(f\"{source_one.name} {source_two.name}\", modifier)())\n\n    expected = \"\"\"\\\nname      : one\nurl       : https://one.com\npriority  : primary\n\nname      : two\nurl       : https://two.com\npriority  : primary\n\"\"\".splitlines()\n    assert [\n        line.strip() for line in tester.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester.status_code == 0\n\n\n@pytest.mark.parametrize(\n    \"source_str\",\n    (\n        \"source_primary\",\n        \"source_supplemental\",\n        \"source_explicit\",\n    ),\n)\ndef test_source_show_given_priority(\n    tester_all_types: CommandTester, source_str: str, request: pytest.FixtureRequest\n) -> None:\n    source = request.getfixturevalue(source_str)\n    tester_all_types.execute(f\"{source.name}\")\n\n    expected = f\"\"\"\\\nname      : {source.name}\nurl       : {source.url}\npriority  : {source.name}\n\"\"\".splitlines()\n    assert [\n        line.strip() for line in tester_all_types.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester_all_types.status_code == 0\n\n\ndef test_source_show_pypi(tester_pypi: CommandTester) -> None:\n    tester_pypi.execute(\"\")\n    expected = \"\"\"\\\nname      : PyPI\npriority  : primary\n\"\"\".splitlines()\n    assert [\n        line.strip() for line in tester_pypi.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester_pypi.status_code == 0\n\n\ndef test_source_show_pypi_and_other(tester_pypi_and_other: CommandTester) -> None:\n    tester_pypi_and_other.execute(\"\")\n    expected = \"\"\"\\\nname      : existing\nurl       : https://existing.com\npriority  : primary\n\nname      : PyPI\npriority  : primary\n\"\"\".splitlines()\n    assert [\n        line.strip()\n        for line in tester_pypi_and_other.io.fetch_output().strip().splitlines()\n    ] == expected\n    assert tester_pypi_and_other.status_code == 0\n\n\ndef test_source_show_no_sources(tester_no_sources: CommandTester) -> None:\n    tester_no_sources.execute(\"error\")\n    assert (\n        tester_no_sources.io.fetch_output().strip()\n        == \"No sources configured for this project.\"\n    )\n    assert tester_no_sources.status_code == 0\n\n\ndef test_source_show_no_sources_implicit_pypi(\n    tester_no_sources: CommandTester, poetry_without_source: Poetry\n) -> None:\n    poetry_without_source.pool.add_repository(PyPiRepository())\n    tester_no_sources.execute(\"\")\n\n    output = tester_no_sources.io.fetch_output().strip()\n\n    assert \"No sources configured for this project.\" in output\n    assert \"PyPI is implicitly enabled as a primary source.\" in output\n\n    assert tester_no_sources.status_code == 0\n\n\ndef test_source_show_error(tester: CommandTester) -> None:\n    tester.execute(\"error\")\n    assert tester.io.fetch_error().strip() == \"No source found with name(s): error\"\n    assert tester.status_code == 1\n"
  },
  {
    "path": "tests/console/commands/test_about.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"about\")\n\n\ndef test_about(tester: CommandTester) -> None:\n    from importlib import metadata\n\n    tester.execute()\n\n    expected = f\"\"\"\\\nPoetry - Package Management for Python\n\nVersion: {metadata.version(\"poetry\")}\nPoetry-Core Version: {metadata.version(\"poetry-core\")}\n\nPoetry is a dependency manager tracking local dependencies of your projects and\\\n libraries.\nSee https://github.com/python-poetry/poetry for more information.\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n"
  },
  {
    "path": "tests/console/commands/test_add.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import cast\n\nimport pytest\nimport tomlkit\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.package import Package\n\nfrom poetry.console.commands.installer_command import InstallerCommand\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.utils.dependency_specification import RequirementsParser\nfrom tests.helpers import TestLocker\nfrom tests.helpers import get_dependency\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from typing import Any\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n    from tomlkit import TOMLDocument\n\n    from poetry.config.config import Config\n    from poetry.poetry import Poetry\n    from poetry.utils.env import MockEnv\n    from poetry.utils.env import VirtualEnv\n    from tests.helpers import PoetryTestApplication\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture(autouse=True)\ndef config(config: Config) -> Config:\n    # Disable parallel installs to get reproducible output.\n    config.merge({\"installer\": {\"parallel\": False}})\n    return config\n\n\n@pytest.fixture\ndef poetry_with_up_to_date_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    source = fixture_dir(\"up_to_date_lock\")\n\n    return project_factory(\n        name=\"foobar\",\n        pyproject_content=(source / \"pyproject.toml\").read_text(encoding=\"utf-8\"),\n        poetry_lock_content=(source / \"poetry.lock\").read_text(encoding=\"utf-8\"),\n    )\n\n\n@pytest.fixture\ndef poetry_with_path_dependency(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    source = fixture_dir(\"with_path_dependency\")\n\n    poetry = project_factory(\n        name=\"foobar\",\n        source=source,\n        use_test_locker=False,\n    )\n    return poetry\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"add\")\n\n\n@pytest.fixture(autouse=True)\ndef repo_add_default_packages(repo: TestRepository) -> None:\n    msgpack_optional_dep = get_dependency(\"msgpack-python\", \">=0.5 <0.6\", optional=True)\n    msgpack_dep = get_dependency(\"msgpack-python\", \">=0.5 <0.6\")\n    msgpack = get_package(\"msgpack-python\", \"0.5.6\")\n    repo.add_package(msgpack)\n\n    redis_dep = get_dependency(\"redis\", \">=3.3.6 <4.0.0\", optional=True)\n    redis = get_package(\"redis\", \"3.4.0\")\n    repo.add_package(redis)\n\n    cachy010 = get_package(\"cachy\", \"0.1.0\")\n    cachy010.extras = {canonicalize_name(\"msgpack\"): [msgpack_dep]}\n    cachy010.add_dependency(msgpack_optional_dep)\n    repo.add_package(cachy010)\n\n    cachy020 = get_package(\"cachy\", \"0.2.0\")\n    cachy020.extras = {canonicalize_name(\"msgpack\"): [get_dependency(\"msgpack-python\")]}\n    cachy020.add_dependency(msgpack_dep)\n    repo.add_package(cachy020)\n\n    cleo065 = get_package(\"cleo\", \"0.6.5\")\n    cleo065.add_dependency(msgpack_optional_dep)\n    cleo065.add_dependency(redis_dep)\n    cleo065.extras = {\n        canonicalize_name(\"redis\"): [redis_dep],\n        canonicalize_name(\"msgpack\"): [msgpack_dep],\n    }\n    repo.add_package(cleo065)\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"tomlkit\", \"0.5.5\"))\n    repo.add_package(get_package(\"pyyaml\", \"3.13\"))\n    repo.add_package(get_package(\"pyyaml\", \"4.2b2\"))\n    repo.add_package(get_package(\"torch\", \"2.4.0+cpu\"))\n\n\ndef test_add_no_constraint(app: PoetryTestApplication, tester: CommandTester) -> None:\n    tester.execute(\"cachy\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == \"^0.2.0\"\n\n\ndef test_add_local_version(app: PoetryTestApplication, tester: CommandTester) -> None:\n    tester.execute(\"torch\")\n\n    expected = \"\"\"\\\nUsing version ^2.4.0 for torch\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing torch (2.4.0+cpu)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"torch\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"torch\"] == \"^2.4.0\"\n\n\ndef test_add_non_package_mode_no_name(\n    project_factory: ProjectFactory,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    poetry = project_factory(\n        name=\"foobar\", pyproject_content=\"[tool.poetry]\\npackage-mode = false\\n\"\n    )\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    tester.execute(\"cachy\")\n\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == \"^0.2.0\"\n\n\ndef test_add_replace_by_constraint(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    tester.execute(\"cachy\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == \"^0.2.0\"\n\n    tester.execute(\"cachy@0.1.0\")\n    expected = \"\"\"\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing cachy (0.1.0)\n\nWriting lock file\n\"\"\"\n    assert tester.io.fetch_output() == expected\n\n    pyproject2: dict[str, Any] = app.poetry.file.read()\n    content = pyproject2[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == \"0.1.0\"\n\n\ndef test_add_no_constraint_editable_error(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    tester.execute(\"-e cachy\")\n\n    expected = \"\"\"\nFailed to add packages. Only vcs/path dependencies support editable installs.\\\n cachy is neither.\n\nNo changes were applied.\n\"\"\"\n    assert tester.status_code == 1\n    assert tester.io.fetch_error() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 0\n\n    pyproject2: dict[str, Any] = app.poetry.file.read()\n    assert content == pyproject2[\"tool\"][\"poetry\"]\n\n\ndef test_add_equal_constraint(tester: CommandTester) -> None:\n    tester.execute(\"cachy==0.1.0\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing cachy (0.1.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n\ndef test_add_greater_constraint(tester: CommandTester) -> None:\n    tester.execute(\"cachy>=0.1.0\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n\n@pytest.mark.parametrize(\"extra_name\", [\"msgpack\", \"MsgPack\"])\ndef test_add_constraint_with_extras(\n    tester: CommandTester,\n    extra_name: str,\n) -> None:\n    tester.execute(f\"cachy[{extra_name}]>=0.1.0,<0.2.0\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.1.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n\ndef test_add_constraint_dependencies(tester: CommandTester) -> None:\n    tester.execute(\"cachy=0.2.0\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n\ndef test_add_with_markers(app: PoetryTestApplication, tester: CommandTester) -> None:\n    marker = \"python_version <= '3.4' or sys_platform == 'win32'\"\n    tester.execute(f\"\"\"cachy --markers \"{marker}\" \"\"\")\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"][\"version\"] == \"^0.2.0\"\n    assert content[\"dependencies\"][\"cachy\"][\"markers\"] == marker\n\n\ndef test_add_git_constraint(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    tmp_venv: VirtualEnv,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    tester.command.set_env(tmp_venv)\n\n    tester.execute(\"git+https://github.com/demo/demo.git\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\n        \"git\": \"https://github.com/demo/demo.git\"\n    }\n\n\ndef test_add_git_constraint_with_poetry(\n    tester: CommandTester,\n    tmp_venv: VirtualEnv,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    tester.command.set_env(tmp_venv)\n\n    tester.execute(\"git+https://github.com/demo/pyproject-demo.git\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert tester.command.installer.executor.installations_count == 2\n\n\n@pytest.mark.parametrize(\"extra_name\", [\"foo\", \"FOO\"])\ndef test_add_git_constraint_with_extras(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    tmp_venv: VirtualEnv,\n    extra_name: str,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    tester.command.set_env(tmp_venv)\n\n    tester.execute(f\"git+https://github.com/demo/demo.git[{extra_name},bar]\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 4 installs, 0 updates, 0 removals\n\n  - Installing cleo (0.6.5)\n  - Installing pendulum (1.4.4)\n  - Installing tomlkit (0.5.5)\n  - Installing demo (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output().strip() == expected.strip()\n    assert tester.command.installer.executor.installations_count == 4\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\n        \"git\": \"https://github.com/demo/demo.git\",\n        \"extras\": [extra_name, \"bar\"],\n    }\n\n\n@pytest.mark.parametrize(\n    \"url, rev\",\n    [\n        (\"git+https://github.com/demo/subdirectories.git#subdirectory=two\", None),\n        (\n            \"git+https://github.com/demo/subdirectories.git@master#subdirectory=two\",\n            \"master\",\n        ),\n    ],\n)\ndef test_add_git_constraint_with_subdirectory(\n    url: str,\n    rev: str | None,\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    tester.execute(url)\n\n    expected = \"\"\"\\\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing two (2.0.0 9cf87a2)\n\nWriting lock file\n\"\"\"\n    assert tester.io.fetch_output().strip() == expected.strip()\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    constraint = {\n        \"git\": \"https://github.com/demo/subdirectories.git\",\n        \"subdirectory\": \"two\",\n    }\n\n    if rev:\n        constraint[\"rev\"] = rev\n\n    assert \"two\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"two\"] == constraint\n\n\n@pytest.mark.parametrize(\"editable\", [False, True])\ndef test_add_git_ssh_constraint(\n    editable: bool,\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    tmp_venv: VirtualEnv,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    tester.command.set_env(tmp_venv)\n\n    url = \"git+ssh://git@github.com/demo/demo.git@develop\"\n    tester.execute(f\"{url}\" if not editable else f\"-e {url}\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 9cf87a2)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n\n    expected_content: dict[str, Any] = {\n        \"git\": \"ssh://git@github.com/demo/demo.git\",\n        \"rev\": \"develop\",\n    }\n    if editable:\n        expected_content[\"develop\"] = True\n\n    assert content[\"dependencies\"][\"demo\"] == expected_content\n\n\n@pytest.mark.parametrize(\n    \"required_fixtures\",\n    [[\"git/github.com/demo/demo\"]],\n)\n@pytest.mark.parametrize(\"editable\", [False, True])\ndef test_add_directory_constraint(\n    editable: bool,\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    path = \"../git/github.com/demo/demo\"\n    tester.execute(f\"{path}\" if not editable else f\"-e {path}\")\n\n    demo_path = app.poetry.file.path.parent.joinpath(path).resolve().as_posix()\n    expected = f\"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 {demo_path})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n\n    expected_content: dict[str, Any] = {\"path\": path}\n    if editable:\n        expected_content[\"develop\"] = True\n\n    assert content[\"dependencies\"][\"demo\"] == expected_content\n\n\ndef test_add_to_new_group_keeps_existing_group(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    groups_content: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[dependency-groups]\nexample = [\n    \"cachy (>=0.2.0,<0.3.0)\",\n]\n\"\"\"\n    )\n    pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    tester.execute(\"-G foo pendulum\")\n\n    expected = \"\"\"\\\nUsing version ^1.4.4 for pendulum\n\"\"\"\n\n    assert tester.io.fetch_output().startswith(expected)\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    assert \"example\" in pyproject[\"dependency-groups\"]\n    assert pyproject[\"dependency-groups\"][\"example\"] == [\n        \"cachy (>=0.2.0,<0.3.0)\",\n    ]\n    assert pyproject[\"dependency-groups\"][\"foo\"] == [\n        \"pendulum (>=1.4.4,<2.0.0)\",\n    ]\n\n\n@pytest.mark.parametrize(\"additional_poetry_group\", [False, True])\ndef test_add_to_existing_group(\n    app: PoetryTestApplication, tester: CommandTester, additional_poetry_group: bool\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    groups_content: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[dependency-groups]\nexample = [\n    \"cachy (>=0.2.0,<0.3.0)\",\n    {include-group = \"included\"},\n]\nincluded = []\n\"\"\"\n    )\n    pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    if additional_poetry_group:\n        poetry_groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.example.dependencies]\ncachy = { allow-prereleases = true }\n\"\"\"\n        )\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = poetry_groups_content[\"tool\"][\"poetry\"][\n            \"group\"\n        ]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    tester.execute(\"-G example pendulum\")\n\n    expected = \"\"\"\\\nUsing version ^1.4.4 for pendulum\n\"\"\"\n\n    assert tester.io.fetch_output().startswith(expected)\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    assert \"example\" in pyproject[\"dependency-groups\"]\n    assert pyproject[\"dependency-groups\"][\"example\"] == [\n        \"cachy (>=0.2.0,<0.3.0)\",\n        {\"include-group\": \"included\"},\n        \"pendulum (>=1.4.4,<2.0.0)\",\n    ]\n    if additional_poetry_group:\n        assert \"example\" in pyproject[\"tool\"][\"poetry\"][\"group\"]\n        assert pyproject[\"tool\"][\"poetry\"][\"group\"][\"example\"][\"dependencies\"] == {\n            \"cachy\": {\"allow-prereleases\": True},\n        }\n    else:\n        assert \"group\" not in pyproject[\"tool\"][\"poetry\"]\n\n\ndef test_add_to_group_with_latest_overwrite_existing(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    groups_content: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[dependency-groups]\nexample = [\n    {include-group = \"included\"},\n    \"cachy (>=0.1.0,<0.2.0)\",\n    \"pendulum (>=1.4.4,<2.0.0)\",\n]\nincluded = []\n\"\"\"\n    )\n    pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    tester.execute(\"-G example cachy@latest\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\"\"\"\n\n    assert tester.io.fetch_output().startswith(expected)\n    assert isinstance(tester.command, InstallerCommand)\n    # `cachy` has `msgpack-python` as dependency. So installation count increases by 1.\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    assert \"example\" in pyproject[\"dependency-groups\"]\n    assert pyproject[\"dependency-groups\"][\"example\"] == [\n        {\"include-group\": \"included\"},\n        \"cachy (>=0.2.0,<0.3.0)\",\n        \"pendulum (>=1.4.4,<2.0.0)\",\n    ]\n\n\ndef test_add_multiple_dependencies_to_dependency_group(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    tester.execute(\"-G example cachy pendulum\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\nUsing version ^1.4.4 for pendulum\n\"\"\"\n\n    assert tester.io.fetch_output().startswith(expected)\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    assert isinstance(tester.command, InstallerCommand)\n    # `cachy` has `msgpack-python` as dependency. So installation count increases by 1.\n    assert tester.command.installer.executor.installations_count == 3\n\n    assert \"example\" in pyproject[\"dependency-groups\"]\n    assert pyproject[\"dependency-groups\"][\"example\"] == [\n        \"cachy (>=0.2.0,<0.3.0)\",\n        \"pendulum (>=1.4.4,<2.0.0)\",\n    ]\n\n\ndef test_add_to_group_uses_existing_legacy_group(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    groups_content: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[tool.poetry.group.example.dependencies]\npendulum = \"^1.4.4\"\n\"\"\"\n    )\n    pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    tester.execute(\"-G example cachy\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\"\"\"\n\n    assert tester.io.fetch_output().startswith(expected)\n    assert isinstance(tester.command, InstallerCommand)\n    # `cachy` has `msgpack-python` as dependency. So installation count increases by 1.\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    assert \"dependency-groups\" not in pyproject\n    assert \"example\" in pyproject[\"tool\"][\"poetry\"][\"group\"]\n    assert \"pendulum\" in pyproject[\"tool\"][\"poetry\"][\"group\"][\"example\"][\"dependencies\"]\n    assert \"cachy\" in pyproject[\"tool\"][\"poetry\"][\"group\"][\"example\"][\"dependencies\"]\n\n\n@pytest.mark.parametrize(\n    \"required_fixtures\",\n    [[\"git/github.com/demo/demo\"]],\n)\ndef test_add_group_directory_constraint_mix_pep735(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    path = \"../git/github.com/demo/demo\"\n    tester.execute(f\"-G example -e {path}\")\n\n    demo_path = app.poetry.file.path.parent.joinpath(path).resolve().as_posix()\n    expected = f\"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 {demo_path})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n\n    assert \"demo @ file://\" in pyproject[\"dependency-groups\"][\"example\"][0]\n    assert demo_path in pyproject[\"dependency-groups\"][\"example\"][0]\n    assert \"demo\" in pyproject[\"tool\"][\"poetry\"][\"group\"][\"example\"][\"dependencies\"]\n    assert pyproject[\"tool\"][\"poetry\"][\"group\"][\"example\"][\"dependencies\"][\"demo\"] == {\n        \"develop\": True\n    }\n\n\n@pytest.mark.parametrize(\n    \"required_fixtures\",\n    [[\"git/github.com/demo/pyproject-demo\"]],\n)\ndef test_add_directory_with_poetry(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    path = \"../git/github.com/demo/pyproject-demo\"\n    tester.execute(f\"{path}\")\n\n    demo_path = app.poetry.file.path.parent.joinpath(path).resolve().as_posix()\n    expected = f\"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.2 {demo_path})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n\n@pytest.mark.parametrize(\n    \"required_fixtures\",\n    [[\"distributions/demo-0.1.0-py2.py3-none-any.whl\"]],\n)\ndef test_add_file_constraint_wheel(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    path = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    tester.execute(f\"{path}\")\n\n    demo_path = app.poetry.file.path.parent.joinpath(path).resolve().as_posix()\n    expected = f\"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.0 {demo_path})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\"path\": path}\n\n\n@pytest.mark.parametrize(\n    \"required_fixtures\",\n    [[\"distributions/demo-0.1.0.tar.gz\"]],\n)\ndef test_add_file_constraint_sdist(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    path = \"../distributions/demo-0.1.0.tar.gz\"\n    tester.execute(f\"{path}\")\n\n    demo_path = app.poetry.file.path.parent.joinpath(path).resolve().as_posix()\n    expected = f\"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo (0.1.0 {demo_path})\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\"path\": path}\n\n\n@pytest.mark.parametrize(\"extra_name\", [\"msgpack\", \"MsgPack\"])\ndef test_add_constraint_with_extras_option(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    extra_name: str,\n) -> None:\n    tester.execute(f\"cachy=0.2.0 --extras {extra_name}\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == {\n        \"version\": \"0.2.0\",\n        \"extras\": [extra_name],\n    }\n\n\ndef test_add_url_constraint_wheel(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    p = mocker.patch(\"pathlib.Path.cwd\")\n    p.return_value = Path(__file__) / \"..\"\n\n    tester.execute(\n        \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    )\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing pendulum (1.4.4)\n  - Installing demo\\\n (0.1.0 https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\n        \"url\": \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    }\n\n\n@pytest.mark.parametrize(\"extra_name\", [\"foo\", \"FOO\"])\ndef test_add_url_constraint_wheel_with_extras(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    extra_name: str,\n) -> None:\n    tester.execute(\n        \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n        f\"[{extra_name},bar]\"\n    )\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 4 installs, 0 updates, 0 removals\n\n  - Installing cleo (0.6.5)\n  - Installing pendulum (1.4.4)\n  - Installing tomlkit (0.5.5)\n  - Installing demo\\\n (0.1.0 https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl)\n\nWriting lock file\n\"\"\"\n    # Order might be different, split into lines and compare the overall output.\n    expected_lines = set(expected.splitlines())\n    output = set(tester.io.fetch_output().splitlines())\n    assert output == expected_lines\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 4\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"demo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"demo\"] == {\n        \"url\": (\n            \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n        ),\n        \"extras\": [extra_name, \"bar\"],\n    }\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\n@pytest.mark.parametrize(\n    (\"existing_extras\", \"expected_extras\"),\n    [\n        (None, {\"my-extra\": [\"cachy (==0.2.0)\"]}),\n        (\n            {\"other\": [\"tomlkit (<2)\"]},\n            {\"other\": [\"tomlkit (<2)\"], \"my-extra\": [\"cachy (==0.2.0)\"]},\n        ),\n        (\n            {\"my-extra\": [\"tomlkit (<2)\"]},\n            {\"my-extra\": [\"tomlkit (<2)\", \"cachy (==0.2.0)\"]},\n        ),\n        (\n            {\"my-extra\": [\"tomlkit (<2)\", \"cachy (==0.1.0)\", \"pendulum (>1)\"]},\n            {\"my-extra\": [\"tomlkit (<2)\", \"cachy (==0.2.0)\", \"pendulum (>1)\"]},\n        ),\n    ],\n)\ndef test_add_constraint_with_optional(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    project_dependencies: bool,\n    existing_extras: dict[str, list[str]] | None,\n    expected_extras: dict[str, list[str]],\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    if project_dependencies:\n        pyproject[\"project\"][\"dependencies\"] = [\"tomlkit (<1)\"]\n        if existing_extras:\n            pyproject[\"project\"][\"optional-dependencies\"] = existing_extras\n    else:\n        pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"tomlkit\"] = \"<1\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n    app.reset_poetry()\n\n    tester.execute(\"cachy=0.2.0 --optional my-extra\")\n\n    assert tester.io.fetch_output().endswith(\"Writing lock file\\n\")\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count > 0\n\n    # check pyproject content\n    pyproject2: dict[str, Any] = app.poetry.file.read()\n    project_content = pyproject2[\"project\"]\n    poetry_content = pyproject2[\"tool\"][\"poetry\"]\n\n    if project_dependencies:\n        assert \"cachy\" not in poetry_content[\"dependencies\"]\n        assert \"cachy\" not in project_content[\"dependencies\"]\n        assert \"my-extra\" in project_content[\"optional-dependencies\"]\n        assert project_content[\"optional-dependencies\"] == expected_extras\n        assert not tester.io.fetch_error()\n    else:\n        assert \"dependencies\" not in project_content\n        assert \"optional-dependencies\" not in project_content\n        assert \"cachy\" in poetry_content[\"dependencies\"]\n        assert poetry_content[\"dependencies\"][\"cachy\"] == {\n            \"version\": \"0.2.0\",\n            \"optional\": True,\n        }\n        assert (\n            \"Optional dependencies will not be added to extras in legacy mode.\"\n            in tester.io.fetch_error()\n        )\n\n    # check lock content\n    if project_dependencies:\n        lock_data = app.poetry.locker.lock_data\n\n        extras = lock_data[\"extras\"]\n        assert list(extras) == sorted(expected_extras)\n        assert extras[\"my-extra\"] == sorted(\n            e.split(\" \")[0] for e in expected_extras[\"my-extra\"]\n        )\n\n        added_package: dict[str, Any] | None = None\n        for package in lock_data[\"package\"]:\n            if package[\"name\"] == \"cachy\":\n                added_package = package\n                break\n        assert added_package is not None\n        assert added_package.get(\"markers\") == 'extra == \"my-extra\"'\n\n\ndef test_add_constraint_with_optional_not_main_group(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"cachy=0.2.0 --group dev --optional my-extra\")\n\n    assert str(e.value) == \"You can only add optional dependencies to the main group\"\n\n\ndef test_add_constraint_with_python(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    tester.execute(\"cachy=0.2.0 --python >=2.7\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == {\"version\": \"0.2.0\", \"python\": \">=2.7\"}\n\n\ndef test_add_constraint_with_platform(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    env: MockEnv,\n) -> None:\n    platform = sys.platform\n    env._platform = platform\n\n    tester.execute(f\"cachy=0.2.0 --platform {platform} -vvv\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == {\n        \"version\": \"0.2.0\",\n        \"platform\": platform,\n    }\n\n\ndef test_add_constraint_with_source(\n    app: PoetryTestApplication,\n    poetry: Poetry,\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    repo = LegacyRepository(name=\"my-index\", url=\"https://my-index.fake\")\n    package = Package(\n        \"cachy\",\n        Version.parse(\"0.2.0\"),\n        source_type=\"legacy\",\n        source_reference=repo.name,\n        source_url=repo._url,\n        yanked=False,\n    )\n    mocker.patch.object(repo, \"package\", return_value=package)\n    mocker.patch.object(repo, \"_find_packages\", wraps=lambda _, name: [package])\n\n    poetry.pool.add_repository(repo)\n\n    tester.execute(\"cachy=0.2.0 --source my-index\")\n\n    expected = \"\"\"\\\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cachy\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cachy\"] == {\n        \"version\": \"0.2.0\",\n        \"source\": \"my-index\",\n    }\n\n\ndef test_add_constraint_with_source_that_does_not_exist(tester: CommandTester) -> None:\n    with pytest.raises(IndexError) as e:\n        tester.execute(\"foo --source i-dont-exist\")\n\n    assert str(e.value) == 'Repository \"i-dont-exist\" does not exist.'\n\n\ndef test_add_constraint_not_found_with_source(\n    poetry: Poetry,\n    mocker: MockerFixture,\n    tester: CommandTester,\n) -> None:\n    repo = LegacyRepository(name=\"my-index\", url=\"https://my-index.fake\")\n    mocker.patch.object(repo, \"find_packages\", return_value=[])\n\n    poetry.pool.add_repository(repo)\n\n    pypi = poetry.pool.repositories[0]\n    pypi.add_package(get_package(\"cachy\", \"0.2.0\"))\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"cachy --source my-index\")\n\n    assert str(e.value) == \"Could not find a matching version of package cachy\"\n\n\n@pytest.mark.parametrize(\"group_name\", [\"dev\", \"foo.BAR\"])\ndef test_add_to_section_that_does_not_exist_yet(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n    group_name: str,\n) -> None:\n    tester.execute(f\"cachy --group {group_name}\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"dependency-groups\"]\n\n    assert content[group_name][0] == \"cachy (>=0.2.0,<0.3.0)\"\n\n    escaped_group_name = f'\"{group_name}\"' if \".\" in group_name else group_name\n    expected = f\"\"\"\\\n{escaped_group_name} = [\n    \"cachy (>=0.2.0,<0.3.0)\"\n]\n\"\"\"\n    string_content = content.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected = expected.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected in string_content\n\n\ndef test_add_creating_poetry_section_does_not_remove_existing_tools(\n    repo: TestRepository,\n    project_factory: ProjectFactory,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    repo.add_package(get_package(\"cachy\", \"0.2.0\"))\n\n    poetry = project_factory(\n        name=\"foobar\",\n        pyproject_content=(\n            '[project]\\nname = \"foobar\"\\nversion=\"0\"\\n[tool.foo]\\nkey = \"value\"\\n'\n        ),\n    )\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    tester.execute(\"--group dev cachy\")\n\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = poetry.file.read()\n    content = pyproject[\"dependency-groups\"]\n\n    assert content[\"dev\"][0] == \"cachy (>=0.2.0,<0.3.0)\"\n    assert \"foo\" in pyproject[\"tool\"]\n    assert pyproject[\"tool\"][\"foo\"][\"key\"] == \"value\"\n\n\ndef test_add_to_dev_group(app: PoetryTestApplication, tester: CommandTester) -> None:\n    tester.execute(\"cachy --dev\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing cachy (0.2.0)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_error() == \"\"\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 2\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"dependency-groups\"]\n\n    assert content[\"dev\"][0] == \"cachy (>=0.2.0,<0.3.0)\"\n\n\ndef test_add_should_not_select_prereleases(\n    app: PoetryTestApplication, tester: CommandTester\n) -> None:\n    tester.execute(\"pyyaml\")\n\n    expected = \"\"\"\\\nUsing version ^3.13 for pyyaml\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing pyyaml (3.13)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert isinstance(tester.command, InstallerCommand)\n    assert tester.command.installer.executor.installations_count == 1\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"pyyaml\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"pyyaml\"] == \"^3.13\"\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\ndef test_add_should_skip_when_adding_existing_package_with_no_constraint(\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    tester: CommandTester,\n    project_dependencies: bool,\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    if project_dependencies:\n        pyproject[\"project\"][\"dependencies\"] = [\"foo>1\"]\n    else:\n        pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^1.0\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    repo.add_package(get_package(\"foo\", \"1.1.2\"))\n    tester.execute(\"foo\")\n\n    expected = \"\"\"\\\nThe following packages are already present in the pyproject.toml and will be skipped:\n\n  - foo\n\nIf you want to update it to the latest compatible version,\\\n you can use `poetry update package`.\nIf you prefer to upgrade it to the latest available version,\\\n you can use `poetry add package@latest`.\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\ndef test_add_should_skip_when_adding_canonicalized_existing_package_with_no_constraint(\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    tester: CommandTester,\n    project_dependencies: bool,\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    if project_dependencies:\n        pyproject[\"project\"][\"dependencies\"] = [\"foo-bar>1\"]\n    else:\n        pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo-bar\"] = \"^1.0\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    repo.add_package(get_package(\"foo-bar\", \"1.1.2\"))\n    tester.execute(\"Foo_Bar\")\n\n    expected = \"\"\"\\\nThe following packages are already present in the pyproject.toml and will be skipped:\n\n  - Foo_Bar\n\nIf you want to update it to the latest compatible version,\\\n you can use `poetry update package`.\nIf you prefer to upgrade it to the latest available version,\\\n you can use `poetry add package@latest`.\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_add_should_fail_circular_dependency(\n    repo: TestRepository, tester: CommandTester\n) -> None:\n    repo.add_package(get_package(\"simple-project\", \"1.1.2\"))\n    result = tester.execute(\"simple-project\")\n\n    assert result == 1\n\n    expected = \"Cannot add dependency on simple-project to project with the same name.\"\n    assert expected in tester.io.fetch_error()\n\n\ndef test_add_latest_should_strip_out_invalid_pep508_path(\n    tester: CommandTester, repo: TestRepository, mocker: MockerFixture\n) -> None:\n    spy = mocker.spy(RequirementsParser, \"parse\")\n    repo.add_package(get_package(\"foo\", \"1.1.1\"))\n    repo.add_package(get_package(\"foo\", \"1.1.2\"))\n    tester.execute(\"foo@latest\")\n\n    assert tester.status_code == 0\n    assert \"Using version ^1.1.2 for foo\" in tester.io.fetch_output()\n\n    assert spy.call_count == 1\n    assert spy.call_args_list[0].args[1] == \"foo\"\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\ndef test_add_latest_should_not_create_duplicate_keys(\n    project_factory: ProjectFactory,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    project_dependencies: bool,\n) -> None:\n    if project_dependencies:\n        pyproject_content = \"\"\"\\\n        [project]\n        name = \"simple-project\"\n        version = \"1.2.3\"\n        dependencies = [\n            \"Foo >= 0.6,<0.7\",\n        ]\n        \"\"\"\n    else:\n        pyproject_content = \"\"\"\\\n        [tool.poetry]\n        name = \"simple-project\"\n        version = \"1.2.3\"\n\n        [tool.poetry.dependencies]\n        python = \"^3.6\"\n        Foo = \"^0.6\"\n        \"\"\"\n\n    poetry = project_factory(name=\"simple-project\", pyproject_content=pyproject_content)\n    pyproject: dict[str, Any] = poetry.file.read()\n\n    if project_dependencies:\n        assert \"tool\" not in pyproject\n        assert pyproject[\"project\"][\"dependencies\"] == [\"Foo >= 0.6,<0.7\"]\n    else:\n        assert \"project\" not in pyproject\n        assert \"Foo\" in pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n        assert pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"Foo\"] == \"^0.6\"\n        assert \"foo\" not in pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    repo.add_package(get_package(\"foo\", \"1.1.2\"))\n    tester.execute(\"foo@latest\")\n\n    updated_pyproject: dict[str, Any] = poetry.file.read()\n    if project_dependencies:\n        assert \"tool\" not in updated_pyproject\n        assert updated_pyproject[\"project\"][\"dependencies\"] == [\"foo (>=1.1.2,<2.0.0)\"]\n    else:\n        assert \"project\" not in updated_pyproject\n        assert \"Foo\" in updated_pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n        assert updated_pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"Foo\"] == \"^1.1.2\"\n        assert \"foo\" not in updated_pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\ndef test_add_should_work_when_adding_existing_package_with_latest_constraint(\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    tester: CommandTester,\n    project_dependencies: bool,\n) -> None:\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    if project_dependencies:\n        pyproject[\"project\"][\"dependencies\"] = [\"foo>1\"]\n    else:\n        pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^1.0\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    repo.add_package(get_package(\"foo\", \"1.1.2\"))\n\n    tester.execute(\"foo@latest\")\n\n    expected = \"\"\"\\\nUsing version ^1.1.2 for foo\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing foo (1.1.2)\n\nWriting lock file\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n    pyproject2: dict[str, Any] = app.poetry.file.read()\n    project_content = pyproject2[\"project\"]\n    poetry_content = pyproject2[\"tool\"][\"poetry\"]\n\n    if project_dependencies:\n        assert \"foo\" not in poetry_content[\"dependencies\"]\n        assert project_content[\"dependencies\"] == [\"foo (>=1.1.2,<2.0.0)\"]\n    else:\n        assert \"dependencies\" not in project_content\n        assert \"foo\" in poetry_content[\"dependencies\"]\n        assert poetry_content[\"dependencies\"][\"foo\"] == \"^1.1.2\"\n\n\ndef test_add_chooses_prerelease_if_only_prereleases_are_available(\n    repo: TestRepository, tester: CommandTester\n) -> None:\n    repo.add_package(get_package(\"foo\", \"1.2.3b0\"))\n    repo.add_package(get_package(\"foo\", \"1.2.3b1\"))\n\n    tester.execute(\"foo\")\n\n    expected = \"\"\"\\\nUsing version ^1.2.3b1 for foo\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing foo (1.2.3b1)\n\nWriting lock file\n\"\"\"\n    assert expected in tester.io.fetch_output()\n\n\ndef test_add_prefers_stable_releases(\n    repo: TestRepository, tester: CommandTester\n) -> None:\n    repo.add_package(get_package(\"foo\", \"1.2.3\"))\n    repo.add_package(get_package(\"foo\", \"1.2.4b1\"))\n\n    tester.execute(\"foo\")\n\n    expected = \"\"\"\\\nUsing version ^1.2.3 for foo\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing foo (1.2.3)\n\nWriting lock file\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_add_with_lock(app: PoetryTestApplication, tester: CommandTester) -> None:\n    content_hash = app.poetry.locker._get_content_hash()\n\n    tester.execute(\"cachy --lock\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n    assert content_hash != app.poetry.locker.lock_data[\"metadata\"][\"content-hash\"]\n\n\ndef test_add_keyboard_interrupt_restore_content(\n    poetry_with_up_to_date_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    tester = command_tester_factory(\"add\", poetry=poetry_with_up_to_date_lockfile)\n\n    mocker.patch(\n        \"poetry.installation.installer.Installer._execute\",\n        side_effect=KeyboardInterrupt(),\n    )\n    original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()\n    original_lockfile_content = poetry_with_up_to_date_lockfile._locker.lock_data\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    tester.execute(\"cachy\")\n\n    assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content\n    assert (\n        poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content\n    )\n\n\n@pytest.mark.parametrize(\n    \"command\",\n    [\n        \"cachy --dry-run\",\n        \"cachy --lock --dry-run\",\n    ],\n)\ndef test_add_with_dry_run_keep_files_intact(\n    command: str,\n    poetry_with_up_to_date_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    tester = command_tester_factory(\"add\", poetry=poetry_with_up_to_date_lockfile)\n\n    original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()\n    original_lockfile_content = poetry_with_up_to_date_lockfile._locker.lock_data\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    tester.execute(command)\n\n    assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content\n    assert (\n        poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content\n    )\n\n\ndef test_add_should_not_change_lock_file_when_dependency_installation_fail(\n    poetry_with_up_to_date_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    tester = command_tester_factory(\"add\", poetry=poetry_with_up_to_date_lockfile)\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()\n    original_lockfile_content = poetry_with_up_to_date_lockfile.locker.lock_data\n\n    def error(_: Any) -> int:\n        tester.io.write(\"\\n  BuildError\\n\\n\")\n        return 1\n\n    mocker.patch(\"poetry.installation.installer.Installer._execute\", side_effect=error)\n    tester.execute(\"cachy\")\n\n    expected = \"\"\"\\\nUsing version ^0.2.0 for cachy\n\nUpdating dependencies\nResolving dependencies...\n\n  BuildError\n\n\"\"\"\n\n    assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content\n    assert poetry_with_up_to_date_lockfile.locker.lock_data == original_lockfile_content\n    assert tester.io.fetch_output() == expected\n\n\ndef test_add_with_path_dependency_no_loopiness(\n    poetry_with_path_dependency: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    \"\"\"https://github.com/python-poetry/poetry/issues/7398\"\"\"\n    tester = command_tester_factory(\"add\", poetry=poetry_with_path_dependency)\n\n    requests_old = get_package(\"requests\", \"2.25.1\")\n    requests_new = get_package(\"requests\", \"2.28.2\")\n\n    repo.add_package(requests_old)\n    repo.add_package(requests_new)\n\n    with pytest.raises(SolverProblemError):\n        tester.execute(\"requests\")\n\n\ndef test_add_extras_are_parsed_and_included(\n    app: PoetryTestApplication,\n    tester: CommandTester,\n) -> None:\n    tester.execute('cleo --extras \"redis msgpack\"')\n\n    expected = \"\"\"\\\nUsing version ^0.6.5 for cleo\n\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 3 installs, 0 updates, 0 removals\n\n  - Installing msgpack-python (0.5.6)\n  - Installing redis (3.4.0)\n  - Installing cleo (0.6.5)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"cleo\" in content[\"dependencies\"]\n    assert content[\"dependencies\"][\"cleo\"] == {\n        \"version\": \"^0.6.5\",\n        \"extras\": [\"redis\", \"msgpack\"],\n    }\n\n\n@pytest.mark.parametrize(\n    \"command\",\n    [\n        \"requests --extras security socks\",\n    ],\n)\ndef test_add_extras_only_accepts_one_package(\n    command: str, tester: CommandTester, repo: TestRepository\n) -> None:\n    \"\"\"\n    You cannot pass in multiple package values to a single --extras flag.\\\n    e.g. --extras security socks is not allowed.\n    \"\"\"\n    repo.add_package(get_package(\"requests\", \"2.30.0\"))\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(command)\n        assert (\n            str(e.value)\n            == \"You can only specify one package when using the --extras option\"\n        )\n\n\n@pytest.mark.parametrize(\"command\", [\"foo\", \"foo --lock\"])\n@pytest.mark.parametrize(\n    (\"locked\", \"expected_docker\"), [(True, \"4.3.1\"), (False, \"4.3.2\")]\n)\ndef test_add_does_not_update_locked_dependencies(\n    repo: TestRepository,\n    poetry_with_up_to_date_lockfile: Poetry,\n    tester: CommandTester,\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    locked: bool,\n    expected_docker: str,\n) -> None:\n    assert isinstance(poetry_with_up_to_date_lockfile.locker, TestLocker)\n    poetry_with_up_to_date_lockfile.locker.locked(locked)\n    tester = command_tester_factory(\"add\", poetry=poetry_with_up_to_date_lockfile)\n    docker_locked = get_package(\"docker\", \"4.3.1\")\n    docker_new = get_package(\"docker\", \"4.3.2\")\n    docker_dep = get_dependency(\"docker\", \">=4.0.0\")\n    foo = get_package(\"foo\", \"0.1.0\")\n    foo.add_dependency(docker_dep)\n    for package in docker_locked, docker_new, foo:\n        repo.add_package(package)\n\n    # set correct files to avoid cache refresh\n    if locked:\n        docker_locked.files = (\n            poetry_with_up_to_date_lockfile.locker.locked_repository()\n            .package(\"docker\", Version.parse(\"4.3.1\"))\n            .files\n        )\n\n    tester.execute(command)\n\n    lock_data = poetry_with_up_to_date_lockfile.locker.lock_data\n    docker_locked_after_command = next(\n        p for p in lock_data[\"package\"] if p[\"name\"] == \"docker\"\n    )\n    assert docker_locked_after_command[\"version\"] == expected_docker\n\n\ndef test_add_creates_dependencies_array_if_necessary(\n    project_factory: ProjectFactory,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    pyproject_content = \"\"\"\\\n    [project]\n    name = \"simple-project\"\n    version = \"1.2.3\"\n\n    [project.optional-dependencies]\n    test = [\"foo\"]\n    \"\"\"\n\n    poetry = project_factory(name=\"simple-project\", pyproject_content=pyproject_content)\n\n    repo.add_package(get_package(\"foo\", \"2.0\"))\n    repo.add_package(get_package(\"bar\", \"2.0\"))\n\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    tester.execute(\"bar>=1.0\")\n\n    updated_pyproject: dict[str, Any] = poetry.file.read()\n    assert updated_pyproject[\"project\"][\"dependencies\"] == [\"bar (>=1.0)\"]\n\n\n@pytest.mark.parametrize(\"has_poetry_section\", [True, False])\ndef test_add_does_not_add_poetry_dependencies_if_not_necessary(\n    project_factory: ProjectFactory,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    has_poetry_section: bool,\n) -> None:\n    pyproject_content = \"\"\"\\\n    [project]\n    name = \"simple-project\"\n    version = \"1.2.3\"\n    dependencies = [\n        \"foo >= 1.0\",\n    ]\n    \"\"\"\n    if has_poetry_section:\n        pyproject_content += \"\"\"\\\n    [tool.poetry]\n    packages = [ { include = \"simple\" } ]\n    \"\"\"\n\n    poetry = project_factory(name=\"simple-project\", pyproject_content=pyproject_content)\n    pyproject: dict[str, Any] = poetry.file.read()\n\n    if has_poetry_section:\n        assert \"dependencies\" not in pyproject[\"tool\"][\"poetry\"]\n    else:\n        assert \"tool\" not in pyproject\n\n    repo.add_package(get_package(\"foo\", \"2.0\"))\n    repo.add_package(get_package(\"bar\", \"2.0\"))\n\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    tester.execute(\"bar>=1.0 --platform linux\")\n\n    updated_pyproject: dict[str, Any] = poetry.file.read()\n    if has_poetry_section:\n        assert \"dependencies\" not in pyproject[\"tool\"][\"poetry\"]\n    else:\n        assert \"tool\" not in pyproject\n    assert updated_pyproject[\"project\"][\"dependencies\"] == [\n        \"foo >= 1.0\",\n        'bar (>=1.0) ; sys_platform == \"linux\"',\n    ]\n\n\n@pytest.mark.parametrize(\"has_poetry_section\", [True, False])\ndef test_add_poetry_dependencies_if_necessary(\n    project_factory: ProjectFactory,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n    has_poetry_section: bool,\n) -> None:\n    pyproject_content = \"\"\"\\\n    [project]\n    name = \"simple-project\"\n    version = \"1.2.3\"\n    dependencies = [\n        \"foo >= 1.0\",\n    ]\n    \"\"\"\n    if has_poetry_section:\n        pyproject_content += \"\"\"\\\n    [tool.poetry]\n    packages = [ { include = \"simple\" } ]\n    \"\"\"\n\n    poetry = project_factory(name=\"simple-project\", pyproject_content=pyproject_content)\n    pyproject: dict[str, Any] = poetry.file.read()\n\n    if has_poetry_section:\n        assert \"dependencies\" not in pyproject[\"tool\"][\"poetry\"]\n    else:\n        assert \"tool\" not in pyproject\n\n    repo.add_package(get_package(\"foo\", \"2.0\"))\n    other_repo = LegacyRepository(name=\"my-index\", url=\"https://my-index.fake\")\n    poetry.pool.add_repository(other_repo)\n    package = Package(\n        \"bar\",\n        \"2.0\",\n        source_type=\"legacy\",\n        source_url=other_repo.url,\n        source_reference=other_repo.name,\n    )\n    mocker.patch.object(other_repo, \"package\", return_value=package)\n    mocker.patch.object(other_repo, \"_find_packages\", wraps=lambda _, name: [package])\n    repo.add_package(package)\n\n    tester = command_tester_factory(\"add\", poetry=poetry)\n    tester.execute(\"bar>=1.0 --platform linux --allow-prereleases --source my-index\")\n\n    updated_pyproject: dict[str, Any] = poetry.file.read()\n    if has_poetry_section:\n        assert \"dependencies\" not in pyproject[\"tool\"][\"poetry\"]\n    else:\n        assert \"tool\" not in pyproject\n    assert updated_pyproject[\"project\"][\"dependencies\"] == [\n        \"foo >= 1.0\",\n        'bar (>=1.0) ; sys_platform == \"linux\"',\n    ]\n    assert updated_pyproject[\"tool\"][\"poetry\"][\"dependencies\"] == {\n        \"bar\": {\n            \"platform\": \"linux\",\n            \"source\": \"my-index\",\n            \"allow-prereleases\": True,\n        }\n    }\n"
  },
  {
    "path": "tests/console/commands/test_build.py",
    "content": "from __future__ import annotations\n\nimport shutil\nimport tarfile\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import Application\nfrom poetry.console.commands.build import BuildCommand\nfrom poetry.console.commands.build import BuildHandler\nfrom poetry.console.commands.build import BuildOptions\nfrom poetry.factory import Factory\nfrom poetry.utils.helpers import remove_directory\nfrom tests.helpers import with_working_directory\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env import VirtualEnv\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture\ndef tmp_project_path(tmp_path: Path) -> Path:\n    return tmp_path / \"project\"\n\n\n@pytest.fixture\ndef tmp_poetry(tmp_project_path: Path, fixture_dir: FixtureDirGetter) -> Poetry:\n    # copy project so that we start with a clean directory\n    shutil.copytree(fixture_dir(\"simple_project\"), tmp_project_path)\n    poetry = Factory().create_poetry(tmp_project_path)\n    return poetry\n\n\n@pytest.fixture\ndef tmp_tester(\n    tmp_poetry: Poetry, command_tester_factory: CommandTesterFactory\n) -> CommandTester:\n    return command_tester_factory(\"build\", tmp_poetry)\n\n\ndef get_package_glob(poetry: Poetry, local_version: str | None = None) -> str:\n    version = poetry.package.version\n\n    if local_version:\n        version = version.replace(local=local_version)\n\n    return f\"{poetry.package.name.replace('-', '_')}-{version}*\"\n\n\ndef test_build_format_is_not_valid(tmp_tester: CommandTester) -> None:\n    with pytest.raises(ValueError, match=r\"Invalid format.*\"):\n        tmp_tester.execute(\"--format not_valid\")\n\n\n@pytest.mark.parametrize(\"format\", [\"sdist\", \"wheel\", \"all\"])\ndef test_build_creates_packages_in_dist_directory_if_no_output_is_specified(\n    tmp_tester: CommandTester, tmp_project_path: Path, tmp_poetry: Poetry, format: str\n) -> None:\n    shutil.rmtree(tmp_project_path / \"dist\")\n    tmp_tester.execute(f\"--format {format}\")\n    build_artifacts = tuple(\n        (tmp_project_path / \"dist\").glob(get_package_glob(tmp_poetry))\n    )\n    assert len(build_artifacts) > 0\n    assert all(archive.exists() for archive in build_artifacts)\n\n\ndef test_build_with_local_version_label(\n    tmp_tester: CommandTester, tmp_project_path: Path, tmp_poetry: Poetry\n) -> None:\n    shutil.rmtree(tmp_project_path / \"dist\")\n    local_version_label = \"local-version\"\n    tmp_tester.execute(f\"--local-version {local_version_label}\")\n    build_artifacts = tuple(\n        (tmp_project_path / \"dist\").glob(\n            get_package_glob(tmp_poetry, local_version=local_version_label)\n        )\n    )\n\n    assert len(build_artifacts) > 0\n    assert all(archive.exists() for archive in build_artifacts)\n\n\n@pytest.mark.parametrize(\"clean\", [True, False])\ndef test_build_with_clean(\n    tmp_tester: CommandTester, tmp_project_path: Path, tmp_poetry: Poetry, clean: bool\n) -> None:\n    dist_dir = tmp_project_path.joinpath(\"dist\")\n    dist_dir.joinpath(\"hello\").touch(exist_ok=True)\n\n    tmp_tester.execute(\"--clean\" if clean else \"\")\n    build_artifacts = tuple(dist_dir.glob(\"*\"))\n\n    assert len(build_artifacts) == 2 if clean else 3\n    assert all(archive.exists() for archive in build_artifacts)\n\n\ndef test_build_with_clean_non_existing_output(\n    tmp_tester: CommandTester, tmp_project_path: Path, tmp_poetry: Poetry\n) -> None:\n    dist_dir = tmp_project_path.joinpath(\"dist\")\n\n    remove_directory(dist_dir, force=True)\n    assert not dist_dir.exists()\n\n    tmp_tester.execute(\"--clean\")\n    build_artifacts = tuple(dist_dir.glob(\"*\"))\n\n    assert len(build_artifacts) == 2\n    assert all(archive.exists() for archive in build_artifacts)\n\n\ndef test_build_not_possible_in_non_package_mode(\n    fixture_dir: FixtureDirGetter,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    source_dir = fixture_dir(\"non_package_mode\")\n\n    poetry = Factory().create_poetry(source_dir)\n    tester = command_tester_factory(\"build\", poetry)\n\n    assert tester.execute() == 1\n    assert (\n        tester.io.fetch_error()\n        == \"Building a package is not possible in non-package mode.\\n\"\n    )\n\n\ndef test_build_with_multiple_readme_files(\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    tmp_venv: VirtualEnv,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    source_dir = fixture_dir(\"with_multiple_readme_files\")\n    target_dir = tmp_path / \"project\"\n    shutil.copytree(str(source_dir), str(target_dir))\n\n    poetry = Factory().create_poetry(target_dir)\n    tester = command_tester_factory(\"build\", poetry, environment=tmp_venv)\n    tester.execute()\n\n    build_dir = target_dir / \"dist\"\n    assert build_dir.exists()\n\n    sdist_file = build_dir / \"my_package-0.1.tar.gz\"\n    assert sdist_file.exists()\n    assert sdist_file.stat().st_size > 0\n\n    (wheel_file,) = build_dir.glob(\"my_package-0.1-*.whl\")\n    assert wheel_file.exists()\n    assert wheel_file.stat().st_size > 0\n\n    with tarfile.open(sdist_file) as tf:\n        sdist_content = tf.getnames()\n\n    assert \"my_package-0.1/README-1.rst\" in sdist_content\n    assert \"my_package-0.1/README-2.rst\" in sdist_content\n\n\n@pytest.mark.parametrize(\n    \"output_dir\", [None, \"dist\", \"test/dir\", \"../dist\", \"absolute\"]\n)\ndef test_build_output_option(\n    tmp_tester: CommandTester,\n    tmp_project_path: Path,\n    tmp_poetry: Poetry,\n    output_dir: str,\n) -> None:\n    shutil.rmtree(tmp_project_path / \"dist\")\n    if output_dir is None:\n        tmp_tester.execute()\n        build_dir = tmp_project_path / \"dist\"\n    elif output_dir == \"absolute\":\n        tmp_tester.execute(f\"--output {tmp_project_path / 'tmp/dist'}\")\n        build_dir = tmp_project_path / \"tmp/dist\"\n    else:\n        tmp_tester.execute(f\"--output {output_dir}\")\n        build_dir = tmp_project_path / output_dir\n\n    build_artifacts = tuple(build_dir.glob(get_package_glob(tmp_poetry)))\n    assert len(build_artifacts) > 0\n    assert all(archive.exists() for archive in build_artifacts)\n\n\ndef test_build_relative_directory_src_layout(\n    tmp_path: Path, fixture_dir: FixtureDirGetter\n) -> None:\n    tmp_project_path = tmp_path / \"project\"\n    with with_working_directory(fixture_dir(\"simple_project\"), tmp_project_path):\n        shutil.rmtree(tmp_project_path / \"dist\")\n        (tmp_project_path / \"src\").mkdir()\n        (tmp_project_path / \"simple_project\").rename(\n            tmp_project_path / \"src\" / \"simple_project\"\n        )\n\n        # We have to use ApplicationTester because CommandTester\n        # initializes Poetry before passing the directory.\n        app = Application()\n        tester = ApplicationTester(app)\n        tester.execute(\"build --project .\")\n\n        build_dir = tmp_project_path / \"dist\"\n\n        assert len(list(build_dir.iterdir())) == 2\n\n\ndef test_build_options_validate_formats() -> None:\n    with pytest.raises(ValueError, match=\"Invalid format: UNKNOWN\"):\n        _ = BuildOptions(clean=True, formats=[\"sdist\", \"UNKNOWN\"], output=\"dist\")  # type: ignore[list-item]\n\n\ndef test_prepare_config_settings() -> None:\n    config_settings = BuildCommand._prepare_config_settings(\n        local_version=\"42\",\n        config_settings=[\"setting_1=value_1\", \"setting_2=value_2\"],\n        io=NullIO(),\n    )\n\n    assert config_settings == {\n        \"local-version\": \"42\",\n        \"setting_1\": \"value_1\",\n        \"setting_2\": \"value_2\",\n    }\n\n\ndef test_prepare_config_settings_raise_on_invalid_setting() -> None:\n    with pytest.raises(ValueError, match=\"Invalid config setting format: value_2\"):\n        _ = BuildCommand._prepare_config_settings(\n            local_version=\"42\",\n            config_settings=[\"setting_1=value_1\", \"value_2\"],\n            io=NullIO(),\n        )\n\n\n@pytest.mark.parametrize(\n    [\"fmt\", \"expected_formats\"],\n    [\n        (\"all\", [\"sdist\", \"wheel\"]),\n        (None, [\"sdist\", \"wheel\"]),\n        (\"sdist\", [\"sdist\"]),\n        (\"wheel\", [\"wheel\"]),\n    ],\n)\ndef test_prepare_formats(fmt: str | None, expected_formats: list[str]) -> None:\n    formats = BuildCommand._prepare_formats(fmt)\n    assert formats == expected_formats\n\n\n@pytest.mark.parametrize(\n    [\"project\", \"isolated_build\"],\n    [\n        (\"core_in_range\", False),\n        (\"core_not_in_range\", True),\n        (\"has_build_script\", True),\n        (\"multiple_build_deps\", True),\n        (\"no_core\", True),\n        (\"core_from_git\", True),\n        (\"no_build_system\", False),\n        (\"no_build_backend\", False),\n    ],\n)\ndef test_requires_isolated_build(\n    project: str,\n    isolated_build: bool,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(f\"build_systems/{project}\"))\n    handler = BuildHandler(poetry=poetry, env=mocker.Mock(), io=NullIO())\n\n    assert handler._requires_isolated_build() is isolated_build\n\n\ndef test_build_handler_build_isolated(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture\n) -> None:\n    from build import ProjectBuilder\n\n    poetry = Factory().create_poetry(fixture_dir(\"build_systems/has_build_script\"))\n\n    mock_builder = mocker.MagicMock(spec=ProjectBuilder)\n    mock_isolated_builder = mocker.patch(\n        \"poetry.console.commands.build.isolated_builder\"\n    )\n    mock_isolated_builder.return_value.__enter__.return_value = mock_builder\n\n    handler = BuildHandler(poetry=poetry, env=mocker.Mock(), io=NullIO())\n    handler.build(BuildOptions(clean=True, formats=[\"wheel\"], output=\"dist\"))\n\n    assert mock_builder.build.call_count == 1\n"
  },
  {
    "path": "tests/console/commands/test_check.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.factory import Factory\nfrom poetry.packages import Locker\nfrom poetry.toml import TOMLFile\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from pathlib import Path\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import SetProjectContext\n\n\n@pytest.fixture\ndef poetry_simple_project(set_project_context: SetProjectContext) -> Iterator[Poetry]:\n    with set_project_context(\"simple_project\", in_place=False) as cwd:\n        yield Factory().create_poetry(cwd)\n\n\n@pytest.fixture\ndef poetry_with_outdated_lockfile(\n    set_project_context: SetProjectContext,\n) -> Iterator[Poetry]:\n    with set_project_context(\"outdated_lock\", in_place=False) as cwd:\n        yield Factory().create_poetry(cwd)\n\n\n@pytest.fixture\ndef poetry_with_up_to_date_lockfile(\n    set_project_context: SetProjectContext,\n) -> Iterator[Poetry]:\n    with set_project_context(\"up_to_date_lock\", in_place=False) as cwd:\n        yield Factory().create_poetry(cwd)\n\n\n@pytest.fixture\ndef poetry_with_pypi_reference(\n    set_project_context: SetProjectContext,\n) -> Iterator[Poetry]:\n    with set_project_context(\"pypi_reference\", in_place=False) as cwd:\n        yield Factory().create_poetry(cwd)\n\n\n@pytest.fixture\ndef poetry_with_invalid_pyproject(\n    set_project_context: SetProjectContext,\n) -> Iterator[Poetry]:\n    with set_project_context(\"invalid_pyproject\", in_place=False) as cwd:\n        yield Factory().create_poetry(cwd)\n\n\n@pytest.fixture()\ndef tester(\n    command_tester_factory: CommandTesterFactory, poetry_simple_project: Poetry\n) -> CommandTester:\n    return command_tester_factory(\"check\", poetry=poetry_simple_project)\n\n\ndef test_check_valid(tester: CommandTester) -> None:\n    tester.execute()\n\n    expected = \"\"\"\\\nAll set!\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    [\"args\", \"expected_status\"],\n    [\n        ([], 0),\n        ([\"--strict\"], 1),\n    ],\n)\ndef test_check_valid_legacy(\n    args: list[str],\n    expected_status: int,\n    mocker: MockerFixture,\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(fixture_dir(\"simple_project_legacy\") / \"pyproject.toml\"),\n        new_callable=mocker.PropertyMock,\n    )\n    tester.execute(\" \".join(args))\n\n    expected = (\n        \"Warning: [tool.poetry.name] is deprecated. Use [project.name] instead.\\n\"\n        \"Warning: [tool.poetry.version] is set but 'version' is not in \"\n        \"[project.dynamic]. If it is static use [project.version]. If it is dynamic, \"\n        \"add 'version' to [project.dynamic].\\n\"\n        \"If you want to set the version dynamically via `poetry build \"\n        \"--local-version` or you are using a plugin, which sets the version \"\n        \"dynamically, you should define the version in [tool.poetry] and add \"\n        \"'version' to [project.dynamic].\\n\"\n        \"Warning: [tool.poetry.description] is deprecated. Use [project.description] \"\n        \"instead.\\n\"\n        \"Warning: [tool.poetry.readme] is set but 'readme' is not in \"\n        \"[project.dynamic]. If it is static use [project.readme]. If it is dynamic, \"\n        \"add 'readme' to [project.dynamic].\\n\"\n        \"If you want to define multiple readmes, you should define them in \"\n        \"[tool.poetry] and add 'readme' to [project.dynamic].\\n\"\n        \"Warning: [tool.poetry.license] is deprecated. Use [project.license] instead.\\n\"\n        \"Warning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\\n\"\n        \"Warning: [tool.poetry.keywords] is deprecated. Use [project.keywords] \"\n        \"instead.\\n\"\n        \"Warning: [tool.poetry.classifiers] is set but 'classifiers' is not in \"\n        \"[project.dynamic]. If it is static use [project.classifiers]. If it is \"\n        \"dynamic, add 'classifiers' to [project.dynamic].\\n\"\n        \"ATTENTION: Per default Poetry determines classifiers for supported Python \"\n        \"versions and license automatically. If you define classifiers in [project], \"\n        \"you disable the automatic enrichment. In other words, you have to define all \"\n        \"classifiers manually. If you want to use Poetry's automatic enrichment of \"\n        \"classifiers, you should define them in [tool.poetry] and add 'classifiers' \"\n        \"to [project.dynamic].\\n\"\n        \"Warning: [tool.poetry.homepage] is deprecated. Use [project.urls] instead.\\n\"\n        \"Warning: [tool.poetry.repository] is deprecated. Use [project.urls] instead.\\n\"\n        \"Warning: [tool.poetry.documentation] is deprecated. Use [project.urls] \"\n        \"instead.\\n\"\n        \"Warning: Defining console scripts in [tool.poetry.scripts] is deprecated. \"\n        \"Use [project.scripts] instead. ([tool.poetry.scripts] should only be used \"\n        \"for scripts of type 'file').\\n\"\n    )\n\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == expected_status\n\n\ndef test_check_invalid_dep_name_same_as_project_name(\n    mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter\n) -> None:\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(\n            fixture_dir(\"invalid_pyproject_dep_name\") / \"pyproject.toml\"\n        ),\n        new_callable=mocker.PropertyMock,\n    )\n    tester.execute(\"\")\n\n    expected = \"\"\"\\\nError: Project name (invalid) is same as one of its dependencies\n\"\"\"\n\n    assert tester.io.fetch_error() == expected\n\n\ndef test_check_invalid(\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_invalid_pyproject: Poetry,\n) -> None:\n    tester = command_tester_factory(\"check\", poetry=poetry_with_invalid_pyproject)\n    tester.execute(\"--lock\")\n\n    expected = \"\"\"\\\nError: Unrecognized classifiers: ['Intended Audience :: Clowns'].\nError: Declared README file does not exist: never/exists.md\nError: Invalid source \"not-exists\" referenced in dependencies.\nError: Invalid source \"not-exists2\" referenced in dependencies.\nError: poetry.lock was not found.\nWarning: [project.license] is not a valid SPDX expression.\\\n This is deprecated and will raise an error in the future.\nWarning: A wildcard Python dependency is ambiguous.\\\n Consider specifying a more explicit one.\nWarning: The \"pendulum\" dependency specifies the \"allows-prereleases\" property,\\\n which is deprecated. Use \"allow-prereleases\" instead.\nWarning: Deprecated classifier 'Natural Language :: Ukranian'.\\\n Must be replaced by ['Natural Language :: Ukrainian'].\nWarning: Deprecated classifier\\\n 'Topic :: Communications :: Chat :: AOL Instant Messenger'.\\\n Must be removed.\n\"\"\"\n\n    assert tester.io.fetch_error() == expected\n\n\ndef test_check_private(\n    mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter\n) -> None:\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(fixture_dir(\"private_pyproject\") / \"pyproject.toml\"),\n        new_callable=mocker.PropertyMock,\n    )\n\n    tester.execute()\n\n    expected = \"\"\"\\\nAll set!\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_check_non_package_mode(\n    mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter\n) -> None:\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(fixture_dir(\"non_package_mode\") / \"pyproject.toml\"),\n        new_callable=mocker.PropertyMock,\n    )\n\n    tester.execute()\n\n    expected = \"\"\"\\\nAll set!\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    (\"options\", \"expected\", \"expected_status\"),\n    [\n        (\"\", \"All set!\\n\", 0),\n        (\"--lock\", \"Error: poetry.lock was not found.\\n\", 1),\n    ],\n)\ndef test_check_lock_missing(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n    options: str,\n    expected: str,\n    expected_status: int,\n) -> None:\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(fixture_dir(\"private_pyproject\") / \"pyproject.toml\"),\n        new_callable=mocker.PropertyMock,\n    )\n\n    status_code = tester.execute(options)\n\n    assert status_code == expected_status\n\n    if status_code == 0:\n        assert tester.io.fetch_output() == expected\n    else:\n        assert tester.io.fetch_error() == expected\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--lock\"])\ndef test_check_lock_outdated(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_outdated_lockfile: Poetry,\n    options: str,\n) -> None:\n    locker = Locker(\n        lock=poetry_with_outdated_lockfile.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry_with_outdated_lockfile.locker._pyproject_data,\n    )\n    poetry_with_outdated_lockfile.set_locker(locker)\n\n    tester = command_tester_factory(\"check\", poetry=poetry_with_outdated_lockfile)\n    status_code = tester.execute(options)\n    expected = (\n        \"Error: pyproject.toml changed significantly since poetry.lock was last generated. \"\n        \"Run `poetry lock` to fix the lock file.\\n\"\n    )\n\n    assert tester.io.fetch_error() == expected\n\n    # exit with an error\n    assert status_code == 1\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--lock\"])\ndef test_check_lock_up_to_date(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_up_to_date_lockfile: Poetry,\n    options: str,\n) -> None:\n    locker = Locker(\n        lock=poetry_with_up_to_date_lockfile.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry_with_up_to_date_lockfile.locker._pyproject_data,\n    )\n    poetry_with_up_to_date_lockfile.set_locker(locker)\n\n    tester = command_tester_factory(\"check\", poetry=poetry_with_up_to_date_lockfile)\n    status_code = tester.execute(options)\n    expected = \"All set!\\n\"\n    assert tester.io.fetch_output() == expected\n\n    # exit with an error\n    assert status_code == 0\n\n\ndef test_check_does_not_error_on_pypi_reference(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_pypi_reference: Poetry,\n) -> None:\n    tester = command_tester_factory(\"check\", poetry=poetry_with_pypi_reference)\n    status_code = tester.execute(\"\")\n\n    assert tester.io.fetch_output() == \"All set!\\n\"\n    assert status_code == 0\n\n\n@pytest.fixture(params=[\"project_str\", \"project_dict\", \"poetry_str\", \"poetry_array\"])\ndef pyproject_with_readme_file(tmp_path: Path, request: pytest.FixtureRequest) -> Path:\n    pyproject_content = \"\"\"\\\n[project]\nname = \"test\"\nversion = \"1.0.0\"\n\"\"\"\n    if request.param == \"project_str\":\n        pyproject_content += 'readme = \"README.md\"\\n'\n\n    elif request.param == \"project_dict\":\n        pyproject_content += (\n            'readme = { file = \"README.md\", content-type = \"text/markdown\" }\\n'\n        )\n    elif request.param == \"poetry_str\":\n        pyproject_content += \"\"\"\n[tool.poetry]\nreadme = \"README.md\"\n\"\"\"\n    elif request.param == \"poetry_array\":\n        pyproject_content += \"\"\"\n[tool.poetry]\nreadme = [\"README.md\"]\n\"\"\"\n    else:\n        raise ValueError(f\"Unknown readme type: {request.param}\")\n    pyproject_path = tmp_path / \"pyproject.toml\"\n    pyproject_path.write_text(pyproject_content, encoding=\"utf-8\")\n    return pyproject_path\n\n\n@pytest.mark.parametrize(\"readme_exists\", [True, False])\ndef test_check_readme_file_exists(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    pyproject_with_readme_file: Path,\n    readme_exists: bool,\n) -> None:\n    if readme_exists:\n        readme_path = tmp_path / \"README.md\"\n        readme_path.write_text(\"README\", encoding=\"utf-8\")\n\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(pyproject_with_readme_file),\n        new_callable=mocker.PropertyMock,\n    )\n    result = tester.execute()\n\n    if readme_exists:\n        assert result == 0\n        assert \"Declared README file does not exist\" not in tester.io.fetch_error()\n    else:\n        assert result == 1\n        assert (\n            \"Declared README file does not exist: README.md\" in tester.io.fetch_error()\n        )\n\n\n@pytest.fixture(params=[\"project_str\", \"project_dict\", \"poetry_str\", \"poetry_array\"])\ndef pyproject_with_empty_readme_file(\n    tmp_path: Path, request: pytest.FixtureRequest\n) -> Path:\n    pyproject_content = \"\"\"\\\n[project]\nname = \"test\"\nversion = \"1.0.0\"\n\"\"\"\n    if request.param == \"project_str\":\n        pyproject_content += 'readme = \"\"\\n'\n\n    elif request.param == \"project_dict\":\n        pyproject_content += 'readme = { file = \"\", content-type = \"text/markdown\" }\\n'\n    elif request.param == \"poetry_str\":\n        pyproject_content += \"\"\"\n[tool.poetry]\nreadme = \"\"\n\"\"\"\n    elif request.param == \"poetry_array\":\n        pyproject_content += \"\"\"\n[tool.poetry]\nreadme = [\"\"]\n\"\"\"\n    else:\n        raise ValueError(f\"Unknown readme type: {request.param}\")\n    pyproject_path = tmp_path / \"pyproject.toml\"\n    pyproject_path.write_text(pyproject_content, encoding=\"utf-8\")\n    return pyproject_path\n\n\ndef test_check_project_readme_as_text(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pyproject_content = \"\"\"[project]\nname = \"test\"\nversion = \"1.0.0\"\nreadme = { content-type = \"text/markdown\", text = \"README\" }\n\"\"\"\n    pyproject_path = tmp_path / \"pyproject.toml\"\n    pyproject_path.write_text(pyproject_content, encoding=\"utf-8\")\n\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(pyproject_path),\n        new_callable=mocker.PropertyMock,\n    )\n    result = tester.execute()\n\n    assert result == 0\n    assert \"Declared README file does not exist\" not in tester.io.fetch_error()\n\n\ndef test_check_poetry_readme_multiple(\n    mocker: MockerFixture,\n    tester: CommandTester,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pyproject_content = \"\"\"[project]\nname = \"test\"\nversion = \"1.0.0\"\ndynamic = [\"readme\"]\n\n[tool.poetry]\nreadme = [\"README1.md\", \"README2.md\", \"README3.md\", \"README4.md\"]\n\"\"\"\n    pyproject_path = tmp_path / \"pyproject.toml\"\n    pyproject_path.write_text(pyproject_content, encoding=\"utf-8\")\n    (tmp_path / \"README2.md\").write_text(\"README 2\", encoding=\"utf-8\")\n    (tmp_path / \"README3.md\").write_text(\"README 3\", encoding=\"utf-8\")\n\n    mocker.patch(\n        \"poetry.poetry.Poetry.file\",\n        return_value=TOMLFile(pyproject_path),\n        new_callable=mocker.PropertyMock,\n    )\n    result = tester.execute()\n\n    assert result == 1\n    assert tester.io.fetch_error() == (\n        \"Error: Declared README file does not exist: README1.md\\n\"\n        \"Error: Declared README file does not exist: README4.md\\n\"\n    )\n"
  },
  {
    "path": "tests/console/commands/test_config.py",
    "content": "from __future__ import annotations\n\nimport json\nimport os\nimport textwrap\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom deepdiff.diff import DeepDiff\nfrom poetry.core.pyproject.exceptions import PyProjectError\n\nfrom poetry.config.config_source import ConfigSource\nfrom poetry.config.config_source import PropertyNotFoundError\nfrom poetry.console.commands.config import ConfigCommand\nfrom poetry.console.commands.install import InstallCommand\nfrom poetry.factory import Factory\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom tests.conftest import Config\nfrom tests.helpers import flatten_dict\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.config.dict_config_source import DictConfigSource\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"config\")\n\n\ndef test_config_command_in_sync_with_config_class() -> None:\n    assert set(ConfigCommand().unique_config_values) == set(\n        flatten_dict(Config.default_config)\n    )\n\n\ndef test_show_config_with_local_config_file_empty(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"poetry.factory.Factory.create_poetry\",\n        side_effect=PyProjectError(\"[tool.poetry] section not found\"),\n    )\n    tester.execute()\n\n    assert tester.io.fetch_output() == \"\"\n\n\ndef test_list_displays_default_value_if_not_set(\n    tester: CommandTester, config_cache_dir: Path, config_data_dir: Path\n) -> None:\n    tester.execute(\"--list\")\n\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = true\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_list_displays_set_get_setting(\n    tester: CommandTester, config: Config, config_cache_dir: Path, config_data_dir: Path\n) -> None:\n    tester.execute(\"virtualenvs.create false\")\n\n    tester.execute(\"--list\")\n\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = false\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n\n    assert config.set_config_source.call_count == 0  # type: ignore[attr-defined]\n    assert tester.io.fetch_output() == expected\n\n\ndef test_cannot_set_with_multiple_values(tester: CommandTester) -> None:\n    with pytest.raises(RuntimeError) as e:\n        tester.execute(\"virtualenvs.create false true\")\n\n    assert str(e.value) == \"You can only pass one value.\"\n\n\ndef test_cannot_set_invalid_value(tester: CommandTester) -> None:\n    with pytest.raises(RuntimeError) as e:\n        tester.execute(\"virtualenvs.create foo\")\n\n    assert str(e.value) == '\"foo\" is an invalid value for virtualenvs.create'\n\n\ndef test_cannot_unset_with_value(tester: CommandTester) -> None:\n    with pytest.raises(RuntimeError) as e:\n        tester.execute(\"virtualenvs.create false --unset\")\n\n    assert str(e.value) == \"You can not combine a setting value with --unset\"\n\n\ndef test_unset_setting(\n    tester: CommandTester, config: Config, config_cache_dir: Path, config_data_dir: Path\n) -> None:\n    tester.execute(\"virtualenvs.path /some/path\")\n    tester.execute(\"virtualenvs.path --unset\")\n    tester.execute(\"--list\")\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = true\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n    assert config.set_config_source.call_count == 0  # type: ignore[attr-defined]\n    assert tester.io.fetch_output() == expected\n\n\ndef test_unset_repo_setting(\n    tester: CommandTester, config: Config, config_cache_dir: Path, config_data_dir: Path\n) -> None:\n    tester.execute(\"repositories.foo.url https://bar.com/simple/\")\n    tester.execute(\"repositories.foo.url --unset \")\n    tester.execute(\"--list\")\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = true\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n    assert config.set_config_source.call_count == 0  # type: ignore[attr-defined]\n    assert tester.io.fetch_output() == expected\n\n\ndef test_unset_value_not_exists(tester: CommandTester) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"foobar --unset\")\n\n    assert str(e.value) == \"Setting foobar does not exist\"\n\n\n@pytest.mark.parametrize(\n    (\"value\", \"expected\"),\n    [\n        (\"virtualenvs.create\", \"true\\n\"),\n        (\"repositories.foo.url\", \"{'url': 'https://bar.com/simple/'}\\n\"),\n    ],\n)\ndef test_display_single_setting(\n    tester: CommandTester, value: str, expected: str | bool\n) -> None:\n    tester.execute(\"repositories.foo.url https://bar.com/simple/\")\n    tester.execute(value)\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_display_single_local_setting(\n    command_tester_factory: CommandTesterFactory, fixture_dir: FixtureDirGetter\n) -> None:\n    tester = command_tester_factory(\n        \"config\", poetry=Factory().create_poetry(fixture_dir(\"with_local_config\"))\n    )\n    tester.execute(\"virtualenvs.create\")\n\n    expected = \"\"\"false\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_display_empty_repositories_setting(\n    command_tester_factory: CommandTesterFactory, fixture_dir: FixtureDirGetter\n) -> None:\n    tester = command_tester_factory(\n        \"config\",\n        poetry=Factory().create_poetry(fixture_dir(\"with_local_config\")),\n    )\n    tester.execute(\"repositories\")\n\n    expected = \"\"\"{}\n\"\"\"\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    (\"setting\", \"expected\"),\n    [\n        (\"repositories\", \"You cannot remove the [repositories] section\"),\n        (\"repositories.test\", \"There is no test repository defined\"),\n    ],\n)\ndef test_unset_nonempty_repositories_section(\n    tester: CommandTester, setting: str, expected: str\n) -> None:\n    tester.execute(\"repositories.foo.url https://bar.com/simple/\")\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(f\"{setting} --unset\")\n\n    assert str(e.value) == expected\n\n\ndef test_set_malformed_repositories_setting(\n    tester: CommandTester,\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"repositories.foo bar baz\")\n\n    assert (\n        str(e.value) == \"You must pass the url. Example: poetry config repositories.foo\"\n        \" https://bar.com\"\n    )\n\n\n@pytest.mark.parametrize(\n    (\"setting\", \"expected\"),\n    [\n        (\"repositories.foo\", \"There is no foo repository defined\"),\n        (\"foo\", \"There is no foo setting.\"),\n    ],\n)\ndef test_display_undefined_setting(\n    tester: CommandTester, setting: str, expected: str\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(setting)\n\n    assert str(e.value) == expected\n\n\ndef test_list_displays_set_get_local_setting(\n    tester: CommandTester,\n    config: Config,\n    config_cache_dir: Path,\n    config_data_dir: Path,\n) -> None:\n    tester.execute(\"virtualenvs.create false --local\")\n\n    tester.execute(\"--list\")\n\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = false\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n\n    assert config.set_config_source.call_count == 1  # type: ignore[attr-defined]\n    assert tester.io.fetch_output() == expected\n\n\ndef test_list_must_not_display_sources_from_pyproject_toml(\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    command_tester_factory: CommandTesterFactory,\n    config_cache_dir: Path,\n    config_data_dir: Path,\n) -> None:\n    source = fixture_dir(\"with_primary_source_implicit\")\n    pyproject_content = (source / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    poetry = project_factory(\"foo\", pyproject_content=pyproject_content)\n    tester = command_tester_factory(\"config\", poetry=poetry)\n\n    tester.execute(\"--list\")\n\n    cache_dir = json.dumps(str(config_cache_dir))\n    data_dir = json.dumps(str(config_data_dir))\n    venv_path = json.dumps(os.path.join(\"{cache-dir}\", \"virtualenvs\"))\n    expected = f\"\"\"cache-dir = {cache_dir}\ndata-dir = {data_dir}\ninstaller.max-workers = null\ninstaller.no-binary = null\ninstaller.only-binary = null\ninstaller.parallel = true\ninstaller.re-resolve = false\nkeyring.enabled = true\npython.installation-dir = {json.dumps(str(Path(\"{data-dir}/python\")))}  # {config_data_dir / \"python\"}\nrepositories.foo.url = \"https://foo.bar/simple/\"\nrequests.max-retries = 0\nsolver.lazy-wheel = true\nsystem-git-client = false\nvirtualenvs.create = true\nvirtualenvs.in-project = null\nvirtualenvs.options.always-copy = false\nvirtualenvs.options.no-pip = false\nvirtualenvs.options.system-site-packages = false\nvirtualenvs.path = {venv_path}  # {config_cache_dir / \"virtualenvs\"}\nvirtualenvs.prompt = \"{{project_name}}-py{{python_version}}\"\nvirtualenvs.use-poetry-python = false\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_set_http_basic(\n    tester: CommandTester, auth_config_source: DictConfigSource\n) -> None:\n    tester.execute(\"http-basic.foo username password\")\n    tester.execute(\"--list\")\n\n    assert auth_config_source.config[\"http-basic\"][\"foo\"] == {\n        \"username\": \"username\",\n        \"password\": \"password\",\n    }\n\n\ndef test_unset_http_basic(\n    tester: CommandTester, auth_config_source: DictConfigSource\n) -> None:\n    tester.execute(\"http-basic.foo username password\")\n    tester.execute(\"http-basic.foo --unset\")\n    tester.execute(\"--list\")\n\n    assert \"foo\" not in auth_config_source.config[\"http-basic\"]\n\n\ndef test_set_http_basic_unsuccessful_multiple_values(\n    tester: CommandTester,\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"http-basic.foo username password password\")\n\n    assert str(e.value) == \"Expected one or two arguments (username, password), got 3\"\n\n\ndef test_set_pypi_token(\n    tester: CommandTester, auth_config_source: DictConfigSource\n) -> None:\n    tester.execute(\"pypi-token.pypi mytoken\")\n    tester.execute(\"--list\")\n\n    assert auth_config_source.config[\"pypi-token\"][\"pypi\"] == \"mytoken\"\n\n\ndef test_unset_pypi_token(\n    tester: CommandTester, auth_config_source: DictConfigSource\n) -> None:\n    tester.execute(\"pypi-token.pypi mytoken\")\n    tester.execute(\"pypi-token.pypi --unset\")\n    tester.execute(\"--list\")\n\n    assert \"pypi\" not in auth_config_source.config[\"pypi-token\"]\n\n\ndef test_set_pypi_token_unsuccessful_multiple_values(\n    tester: CommandTester,\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"pypi-token.pypi mytoken mytoken\")\n\n    assert str(e.value) == \"Expected only one argument (token), got 2\"\n\n\ndef test_set_pypi_token_no_values(\n    tester: CommandTester,\n) -> None:\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"pypi-token.pypi\")\n\n    assert str(e.value) == \"Expected a value for pypi-token.pypi setting.\"\n\n\ndef test_set_client_cert(\n    tester: CommandTester,\n    auth_config_source: DictConfigSource,\n    mocker: MockerFixture,\n) -> None:\n    mocker.spy(ConfigSource, \"__init__\")\n\n    tester.execute(\"certificates.foo.client-cert path/to/cert.pem\")\n\n    assert (\n        auth_config_source.config[\"certificates\"][\"foo\"][\"client-cert\"]\n        == \"path/to/cert.pem\"\n    )\n\n\ndef test_set_client_cert_unsuccessful_multiple_values(\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    mocker.spy(ConfigSource, \"__init__\")\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"certificates.foo.client-cert path/to/cert.pem path/to/cert.pem\")\n\n    assert str(e.value) == \"You must pass exactly 1 value\"\n\n\n@pytest.mark.parametrize(\n    (\"value\", \"result\"),\n    [\n        (\"path/to/ca.pem\", \"path/to/ca.pem\"),\n        (\"true\", True),\n        (\"false\", False),\n    ],\n)\ndef test_set_cert(\n    tester: CommandTester,\n    auth_config_source: DictConfigSource,\n    mocker: MockerFixture,\n    value: str,\n    result: str | bool,\n) -> None:\n    mocker.spy(ConfigSource, \"__init__\")\n\n    tester.execute(f\"certificates.foo.cert {value}\")\n\n    assert auth_config_source.config[\"certificates\"][\"foo\"][\"cert\"] == result\n\n\ndef test_unset_cert(\n    tester: CommandTester,\n    auth_config_source: DictConfigSource,\n    mocker: MockerFixture,\n) -> None:\n    mocker.spy(ConfigSource, \"__init__\")\n\n    tester.execute(\"certificates.foo.cert path/to/ca.pem\")\n\n    assert \"cert\" in auth_config_source.config[\"certificates\"][\"foo\"]\n\n    tester.execute(\"certificates.foo.cert --unset\")\n    assert \"cert\" not in auth_config_source.config[\"certificates\"][\"foo\"]\n\n\ndef test_config_installer_parallel(\n    tester: CommandTester, command_tester_factory: CommandTesterFactory\n) -> None:\n    tester.execute(\"--local installer.parallel\")\n    assert tester.io.fetch_output().strip() == \"true\"\n\n    command = command_tester_factory(\"install\")._command\n    assert isinstance(command, InstallCommand)\n    workers = command.installer._executor._max_workers\n    assert workers > 1\n\n    tester.io.clear_output()\n    tester.execute(\"--local installer.parallel false\")\n    tester.execute(\"--local installer.parallel\")\n    assert tester.io.fetch_output().strip() == \"false\"\n\n    command = command_tester_factory(\"install\")._command\n    assert isinstance(command, InstallCommand)\n    workers = command.installer._executor._max_workers\n    assert workers == 1\n\n\n@pytest.mark.parametrize(\n    (\"setting\",),\n    [\n        (\"installer.no-binary\",),\n        (\"installer.only-binary\",),\n    ],\n)\n@pytest.mark.parametrize(\n    (\"value\", \"expected\"),\n    [\n        (\"true\", [\":all:\"]),\n        (\"1\", [\":all:\"]),\n        (\"false\", [\":none:\"]),\n        (\"0\", [\":none:\"]),\n        (\"pytest\", [\"pytest\"]),\n        (\"PyTest\", [\"pytest\"]),\n        (\"pytest,black\", [\"pytest\", \"black\"]),\n        (\"\", []),\n    ],\n)\ndef test_config_installer_binary_filter_config(\n    tester: CommandTester, setting: str, value: str, expected: list[str]\n) -> None:\n    tester.execute(setting)\n    assert tester.io.fetch_output().strip() == \"null\"\n\n    config = Config.create()\n    assert not config.get(setting)\n\n    tester.execute(f\"{setting} '{value}'\")\n\n    config = Config.create(reload=True)\n    assert not DeepDiff(config.get(setting), expected, ignore_order=True)\n\n\ndef test_config_solver_lazy_wheel(\n    tester: CommandTester, command_tester_factory: CommandTesterFactory\n) -> None:\n    tester.execute(\"--local solver.lazy-wheel\")\n    assert tester.io.fetch_output().strip() == \"true\"\n\n    repo = LegacyRepository(\"foo\", \"https://foo.com\")\n    assert repo._lazy_wheel\n\n    tester.io.clear_output()\n    tester.execute(\"--local solver.lazy-wheel false\")\n    tester.execute(\"--local solver.lazy-wheel\")\n    assert tester.io.fetch_output().strip() == \"false\"\n\n    repo = LegacyRepository(\"foo\", \"https://foo.com\")\n    assert not repo._lazy_wheel\n\n\ncurrent_config = \"\"\"\\\n[experimental]\nsystem-git-client = true\n\n[virtualenvs]\nprefer-active-python = false\n\"\"\"\n\nconfig_migrated = \"\"\"\\\nsystem-git-client = true\n\n[virtualenvs]\nuse-poetry-python = true\n\"\"\"\n\n\n@pytest.mark.parametrize(\n    [\"proceed\", \"expected_config\"],\n    [\n        (\"yes\", config_migrated),\n        (\"no\", current_config),\n    ],\n)\ndef test_config_migrate(\n    proceed: str,\n    expected_config: str,\n    tester: CommandTester,\n    mocker: MockerFixture,\n    tmp_path: Path,\n) -> None:\n    config_dir = tmp_path / \"config\"\n    mocker.patch(\"poetry.locations.CONFIG_DIR\", config_dir)\n\n    config_file = Path(config_dir / \"config.toml\")\n    with config_file.open(\"w\", encoding=\"utf-8\") as fh:\n        fh.write(current_config)\n\n    tester.execute(\"--migrate\", inputs=proceed)\n\n    expected_output = textwrap.dedent(\"\"\"\\\n    Checking for required migrations ...\n    experimental.system-git-client = true -> system-git-client = true\n    virtualenvs.prefer-active-python = false -> virtualenvs.use-poetry-python = true\n    \"\"\")\n\n    output = tester.io.fetch_output()\n    assert output.startswith(expected_output)\n\n    with config_file.open(\"r\", encoding=\"utf-8\") as fh:\n        assert fh.read() == expected_config\n\n\ndef test_config_migrate_local_config(tester: CommandTester, poetry: Poetry) -> None:\n    local_config = poetry.file.path.parent / \"poetry.toml\"\n    config_data = textwrap.dedent(\"\"\"\\\n    [experimental]\n    system-git-client = true\n\n    [virtualenvs]\n    prefer-active-python = false\n    \"\"\")\n\n    with local_config.open(\"w\", encoding=\"utf-8\") as fh:\n        fh.write(config_data)\n\n    tester.execute(\"--migrate --local\", inputs=\"yes\")\n\n    expected_config = textwrap.dedent(\"\"\"\\\n        system-git-client = true\n\n        [virtualenvs]\n        use-poetry-python = true\n        \"\"\")\n\n    with local_config.open(\"r\", encoding=\"utf-8\") as fh:\n        assert fh.read() == expected_config\n\n\ndef test_config_migrate_local_config_should_raise_if_not_found(\n    tester: CommandTester,\n) -> None:\n    with pytest.raises(RuntimeError, match=\"No local config file found\"):\n        tester.execute(\"--migrate --local\", inputs=\"yes\")\n\n\ndef test_config_installer_build_config_settings(\n    tester: CommandTester, config: Config\n) -> None:\n    config_key = \"installer.build-config-settings.demo\"\n    value = {\"CC\": \"gcc\", \"--build-option\": [\"--one\", \"--two\"]}\n\n    tester.execute(f\"{config_key} '{json.dumps(value)}'\")\n    assert not DeepDiff(config.config_source.get_property(config_key), value)\n\n    value_two = {\"CC\": \"g++\"}\n    tester.execute(f\"{config_key} '{json.dumps(value_two)}'\")\n    assert not DeepDiff(\n        config.config_source.get_property(config_key), {**value, **value_two}\n    )\n\n    value_three = {\n        \"--build-option\": [\"--three\", \"--four\"],\n        \"--package-option\": [\"--name=foo\"],\n    }\n    tester.execute(f\"{config_key} '{json.dumps(value_three)}'\")\n    assert not DeepDiff(\n        config.config_source.get_property(config_key),\n        {\n            **value,\n            **value_two,\n            **value_three,\n        },\n    )\n\n    tester.execute(f\"{config_key} --unset\")\n\n    with pytest.raises(PropertyNotFoundError):\n        config.config_source.get_property(config_key)\n\n\n@pytest.mark.parametrize(\n    \"value\",\n    [\n        \"BAD=VALUE\",\n        \"BAD\",\n        json.dumps({\"key\": [\"str\", 0]}),\n    ],\n)\ndef test_config_installer_build_config_settings_bad_values(\n    value: str, tester: CommandTester\n) -> None:\n    config_key = \"installer.build-config-settings.demo\"\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(f\"{config_key} '{value}'\")\n\n    assert str(e.value) == (\n        f\"Invalid build config setting '{value}'. \"\n        f\"It must be a valid JSON with each property \"\n        f\"a string or a list of strings.\"\n    )\n\n\ndef test_command_config_build_config_settings_get(\n    tester: CommandTester, config: Config\n) -> None:\n    setting_group = \"installer.build-config-settings\"\n    setting = f\"{setting_group}.foo\"\n\n    # test when no values are configured\n    tester.execute(setting)\n    assert tester.io.fetch_error() == \"\"\n    assert (\n        tester.io.fetch_output().strip()\n        == \"No build config settings configured for foo.\"\n    )\n\n    tester.execute(setting_group)\n    assert tester.io.fetch_error() == \"\"\n    assert (\n        tester.io.fetch_output().strip()\n        == \"No packages configured with build config settings.\"\n    )\n\n    # test with one value configured\n    value = {\"CC\": \"gcc\", \"--build-options\": [\"--one\", \"--two\"]}\n    tester.execute(f\"{setting} '{json.dumps(value)}'\")\n    assert tester.status_code == 0\n    assert tester.io.fetch_output() == tester.io.fetch_error() == \"\"\n\n    tester.execute(setting)\n    assert tester.io.fetch_error() == \"\"\n    assert tester.io.fetch_output().strip() == json.dumps(value)\n\n    tester.execute(setting_group)\n    assert tester.io.fetch_error() == \"\"\n    assert tester.io.fetch_output().strip().splitlines() == [\n        'foo.--build-options = \"[--one, --two]\"',\n        'foo.CC = \"gcc\"',\n    ]\n\n    # test getting un-configured value\n    tester.execute(f\"{setting_group}.bar\")\n    assert tester.io.fetch_error() == \"\"\n    assert (\n        tester.io.fetch_output().strip()\n        == \"No build config settings configured for bar.\"\n    )\n"
  },
  {
    "path": "tests/console/commands/test_init.py",
    "content": "from __future__ import annotations\n\nimport os\nimport shutil\nimport sys\nimport textwrap\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.testers.command_tester import CommandTester\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.utils.helpers import module_name\n\nfrom poetry.console.application import Application\nfrom poetry.console.commands.init import InitCommand\nfrom poetry.repositories import RepositoryPool\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from unittest.mock import MagicMock\n\n    from poetry.core.packages.package import Package\n    from pytest import FixtureRequest\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from poetry.poetry import Poetry\n    from tests.helpers import PoetryTestApplication\n    from tests.helpers import TestRepository\n    from tests.types import FixtureDirGetter\n    from tests.types import MockedPythonRegister\n\n\n@pytest.fixture\ndef source_dir(tmp_path: Path) -> Iterator[Path]:\n    cwd = Path.cwd()\n    try:\n        os.chdir(tmp_path)\n        yield tmp_path\n    finally:\n        os.chdir(cwd)\n\n\n@pytest.fixture\ndef patches(mocker: MockerFixture, source_dir: Path, repo: TestRepository) -> None:\n    mocker.patch(\"pathlib.Path.cwd\", return_value=source_dir)\n    mocker.patch(\n        \"poetry.console.commands.init.InitCommand._get_pool\",\n        return_value=RepositoryPool([repo]),\n    )\n\n\n@pytest.fixture\ndef tester(patches: None) -> CommandTester:\n    app = Application()\n    return CommandTester(app.find(\"init\"))\n\n\ndef test_basic_interactive(\n    tester: CommandTester, init_basic_inputs: str, init_basic_toml_no_readme: str\n) -> None:\n    tester.execute(inputs=init_basic_inputs)\n    assert init_basic_toml_no_readme in tester.io.fetch_output()\n\n\ndef test_noninteractive(\n    app: PoetryTestApplication,\n    mocker: MockerFixture,\n    poetry: Poetry,\n    repo: TestRepository,\n    tmp_path: Path,\n) -> None:\n    command = app.find(\"init\")\n    assert isinstance(command, InitCommand)\n    command._pool = poetry.pool\n\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    p = mocker.patch(\"pathlib.Path.cwd\")\n    p.return_value = tmp_path\n\n    tester = CommandTester(command)\n    args = \"--name my-package --dependency pytest\"\n    tester.execute(args=args, interactive=False)\n\n    expected = \"Using version ^3.6.0 for pytest\\n\"\n    assert tester.io.fetch_output() == expected\n    assert tester.io.fetch_error() == \"\"\n\n    toml_content = (tmp_path / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    assert 'name = \"my-package\"' in toml_content\n    assert '\"pytest (>=3.6.0,<4.0.0)\"' in toml_content\n\n    expected_build_system = textwrap.dedent(\"\"\"\n    [build-system]\n    requires = [\"poetry-core>=2.0.0,<3.0.0\"]\n    build-backend = \"poetry.core.masonry.api\"\n    \"\"\")\n\n    assert expected_build_system in toml_content\n\n\ndef test_interactive_with_dependencies(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"django-pendulum\", \"0.1.6-pre4\"))\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n    repo.add_package(get_package(\"flask\", \"2.0.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"pendulu\",  # Search for package\n        \"1\",  # Second option is pendulum\n        \"\",  # Do not set constraint\n        \"Flask\",\n        \"0\",\n        \"\",\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"pendulum (>=2.0.0,<3.0.0)\",\n    \"flask (>=2.0.0,<3.0.0)\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\n# Regression test for https://github.com/python-poetry/poetry/issues/2355\ndef test_interactive_with_dependencies_and_no_selection(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"django-pendulum\", \"0.1.6-pre4\"))\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"pendulu\",  # Search for package\n        \"\",  # Do not select an option\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"\",  # Do not select an option\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_empty_license(tester: CommandTester) -> None:\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"\",  # Description\n        \"n\",  # Author\n        \"\",  # License\n        \"\",  # Python\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    python = \".\".join(str(c) for c in sys.version_info[:2])\n    expected = f\"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"\"\nauthors = [\n    {{name = \"Your Name\",email = \"you@example.com\"}}\n]\nrequires-python = \">={python}\"\n\"\"\"\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_git_dependencies(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"git+https://github.com/demo/demo.git\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ git+https://github.com/demo/demo.git\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\n_generate_choice_list_packages_params: list[list[Package]] = [\n    [\n        get_package(\"flask-blacklist\", \"1.0.0\"),\n        get_package(\"Flask-Shelve\", \"1.0.0\"),\n        get_package(\"flask-pwa\", \"1.0.0\"),\n        get_package(\"Flask-test1\", \"1.0.0\"),\n        get_package(\"Flask-test2\", \"1.0.0\"),\n        get_package(\"Flask-test3\", \"1.0.0\"),\n        get_package(\"Flask-test4\", \"1.0.0\"),\n        get_package(\"Flask-test5\", \"1.0.0\"),\n        get_package(\"Flask\", \"1.0.0\"),\n        get_package(\"Flask-test6\", \"1.0.0\"),\n        get_package(\"Flask-test7\", \"1.0.0\"),\n    ],\n    [\n        get_package(\"flask-blacklist\", \"1.0.0\"),\n        get_package(\"Flask-Shelve\", \"1.0.0\"),\n        get_package(\"flask-pwa\", \"1.0.0\"),\n        get_package(\"Flask-test1\", \"1.0.0\"),\n        get_package(\"Flask\", \"1.0.0\"),\n    ],\n]\n\n\n@pytest.fixture(params=_generate_choice_list_packages_params)\ndef _generate_choice_list_packages(request: FixtureRequest) -> list[Package]:\n    packages: list[Package] = request.param\n    return packages\n\n\n@pytest.mark.parametrize(\"package_name\", [\"flask\", \"Flask\", \"flAsK\"])\ndef test_generate_choice_list(\n    tester: CommandTester,\n    package_name: str,\n    _generate_choice_list_packages: list[Package],\n) -> None:\n    init_command = tester.command\n    assert isinstance(init_command, InitCommand)\n\n    packages = _generate_choice_list_packages\n    choices = init_command._generate_choice_list(\n        packages, canonicalize_name(package_name)\n    )\n\n    assert choices[0] == \"Flask\"\n\n\ndef test_interactive_with_git_dependencies_with_reference(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"git+https://github.com/demo/demo.git@develop\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ git+https://github.com/demo/demo.git@develop\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_git_dependencies_and_other_name(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"git+https://github.com/demo/pyproject-demo.git\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ git+https://github.com/demo/pyproject-demo.git\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_directory_dependency(\n    tester: CommandTester,\n    repo: TestRepository,\n    source_dir: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    demo = fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\"\n    shutil.copytree(str(demo), str(source_dir / \"demo\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"./demo\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    demo_uri = (Path.cwd() / \"demo\").as_uri()\n    expected = f\"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {{name = \"Your Name\",email = \"you@example.com\"}}\n]\nlicense = {{text = \"MIT\"}}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ {demo_uri}\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_directory_dependency_and_other_name(\n    tester: CommandTester,\n    repo: TestRepository,\n    source_dir: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    demo = fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"pyproject-demo\"\n    shutil.copytree(str(demo), str(source_dir / \"pyproject-demo\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"./pyproject-demo\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    demo_uri = (Path.cwd() / \"pyproject-demo\").as_uri()\n    expected = f\"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {{name = \"Your Name\",email = \"you@example.com\"}}\n]\nlicense = {{text = \"MIT\"}}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ {demo_uri}\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_file_dependency(\n    tester: CommandTester,\n    repo: TestRepository,\n    source_dir: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    demo = fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n    shutil.copyfile(str(demo), str(source_dir / demo.name))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"./demo-0.1.0-py2.py3-none-any.whl\",  # Search for package\n        \"\",  # Stop searching for packages\n        \"\",  # Interactive dev packages\n        \"pytest\",  # Search for package\n        \"0\",\n        \"\",\n        \"\",\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    demo_uri = (Path.cwd() / \"demo-0.1.0-py2.py3-none-any.whl\").as_uri()\n    expected = f\"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {{name = \"Your Name\",email = \"you@example.com\"}}\n]\nlicense = {{text = \"MIT\"}}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"demo @ {demo_uri}\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_interactive_with_wrong_dependency_inputs(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.8\",  # Python\n        \"\",  # Interactive packages\n        \"foo 1.19.2\",\n        \"pendulum 2.0.0 foo\",  # Package name and constraint (invalid)\n        \"pendulum@^2.0.0\",  # Package name and constraint (valid)\n        \"\",  # End package selection\n        \"\",  # Interactive dev packages\n        \"pytest 3.6.0 foo\",  # Dev package name and constraint (invalid)\n        \"pytest 3.6.0\",  # Dev package name and constraint (invalid)\n        \"pytest@3.6.0\",  # Dev package name and constraint (valid)\n        \"\",  # End package selection\n        \"\\n\",  # Generate\n    ]\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.8\"\ndependencies = [\n    \"foo (==1.19.2)\",\n    \"pendulum (>=2.0.0,<3.0.0)\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (==3.6.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_python_option(tester: CommandTester) -> None:\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n    tester.execute(\"--python '>=3.6'\", inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_predefined_dependency(tester: CommandTester, repo: TestRepository) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n    tester.execute(\"--dependency pendulum\", inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"pendulum (>=2.0.0,<3.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_predefined_and_interactive_dependencies(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pyramid\", \"1.10\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"\",  # Interactive packages\n        \"pyramid\",  # Search for package\n        \"0\",  # First option\n        \"\",  # Do not set constraint\n        \"\",  # Stop searching for packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(\"--dependency pendulum\", inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n    \"pendulum (>=2.0.0,<3.0.0)\",\n    \"pyramid (>=1.10,<2.0)\"\n]\n\"\"\"\n    assert expected in tester.io.fetch_output()\n\n\ndef test_predefined_dev_dependency(tester: CommandTester, repo: TestRepository) -> None:\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(\"--dev-dependency pytest\", inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    assert expected in tester.io.fetch_output()\n\n\ndef test_predefined_and_interactive_dev_dependencies(\n    tester: CommandTester, repo: TestRepository\n) -> None:\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n    repo.add_package(get_package(\"pytest-requests\", \"0.2.0\"))\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.2.3\",  # Version\n        \"This is a description\",  # Description\n        \"n\",  # Author\n        \"MIT\",  # License\n        \">=3.6\",  # Python\n        \"n\",  # Interactive packages\n        \"\",  # Interactive dev packages\n        \"pytest-requests\",  # Search for package\n        \"0\",  # Select first option\n        \"\",  # Do not set constraint\n        \"\",  # Stop searching for dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(\"--dev-dependency pytest\", inputs=\"\\n\".join(inputs))\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.6\"\ndependencies = [\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\",\n    \"pytest-requests (>=0.2.0,<0.3.0)\"\n]\n\"\"\"\n\n    output = tester.io.fetch_output()\n    assert expected in output\n\n\ndef test_predefined_all_options(tester: CommandTester, repo: TestRepository) -> None:\n    repo.add_package(get_package(\"pendulum\", \"2.0.0\"))\n    repo.add_package(get_package(\"pytest\", \"3.6.0\"))\n\n    inputs = [\n        \"1.2.3\",  # Version\n        \"\",  # Author\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(\n        \"--name my-package \"\n        \"--description 'This is a description' \"\n        \"--author 'Foo Bar <foo@example.com>' \"\n        \"--python '>=3.8' \"\n        \"--license MIT \"\n        \"--dependency pendulum \"\n        \"--dev-dependency pytest\",\n        inputs=\"\\n\".join(inputs),\n    )\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\n    {name = \"Foo Bar\",email = \"foo@example.com\"}\n]\nlicense = {text = \"MIT\"}\nrequires-python = \">=3.8\"\ndependencies = [\n    \"pendulum (>=2.0.0,<3.0.0)\"\n]\n\n[dependency-groups]\ndev = [\n    \"pytest (>=3.6.0,<4.0.0)\"\n]\n\"\"\"\n\n    output = tester.io.fetch_output()\n    assert expected in output\n\n\ndef test_add_package_with_extras_and_whitespace(tester: CommandTester) -> None:\n    command = tester.command\n    assert isinstance(command, InitCommand)\n    result = command._parse_requirements([\"databases[postgresql, sqlite]\"])\n\n    assert result[0][\"name\"] == \"databases\"\n    assert len(result[0][\"extras\"]) == 2\n    assert \"postgresql\" in result[0][\"extras\"]\n    assert \"sqlite\" in result[0][\"extras\"]\n\n\ndef test_init_existing_pyproject_simple(\n    tester: CommandTester,\n    source_dir: Path,\n    init_basic_inputs: str,\n    init_basic_toml_no_readme: str,\n) -> None:\n    pyproject_file = source_dir / \"pyproject.toml\"\n    existing_section = \"\"\"\n[tool.black]\nline-length = 88\n\"\"\"\n    pyproject_file.write_text(existing_section, encoding=\"utf-8\")\n    tester.execute(inputs=init_basic_inputs)\n    assert (\n        f\"{existing_section}\\n{init_basic_toml_no_readme}\"\n        in pyproject_file.read_text(encoding=\"utf-8\")\n    )\n\n\n@pytest.mark.parametrize(\"linesep\", [\"\\n\", \"\\r\\n\"])\ndef test_init_existing_pyproject_consistent_linesep(\n    tester: CommandTester,\n    source_dir: Path,\n    init_basic_inputs: str,\n    init_basic_toml_no_readme: str,\n    linesep: str,\n) -> None:\n    pyproject_file = source_dir / \"pyproject.toml\"\n    existing_section = \"\"\"\n[tool.black]\nline-length = 88\n\"\"\".replace(\"\\n\", linesep)\n    with open(pyproject_file, \"w\", newline=\"\", encoding=\"utf-8\") as f:\n        f.write(existing_section)\n    tester.execute(inputs=init_basic_inputs)\n    with open(pyproject_file, newline=\"\", encoding=\"utf-8\") as f:\n        content = f.read()\n    init_basic_toml = init_basic_toml_no_readme.replace(\"\\n\", linesep)\n    assert f\"{existing_section}{linesep}{init_basic_toml}\" in content\n\n\ndef test_init_non_interactive_existing_pyproject_add_dependency(\n    tester: CommandTester,\n    source_dir: Path,\n    init_basic_inputs: str,\n    repo: TestRepository,\n) -> None:\n    pyproject_file = source_dir / \"pyproject.toml\"\n    existing_section = \"\"\"\n[tool.black]\nline-length = 88\n\"\"\"\n    pyproject_file.write_text(existing_section, encoding=\"utf-8\")\n\n    repo.add_package(get_package(\"foo\", \"1.19.2\"))\n\n    tester.execute(\n        \"--author 'Your Name <you@example.com>' \"\n        \"--name 'my-package' \"\n        \"--python '>=3.6' \"\n        \"--dependency foo\",\n        interactive=False,\n    )\n\n    expected = \"\"\"\\\n[project]\nname = \"my-package\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\n    {name = \"Your Name\",email = \"you@example.com\"}\n]\nrequires-python = \">=3.6\"\ndependencies = [\n    \"foo (>=1.19.2,<2.0.0)\"\n]\n\"\"\"\n    assert f\"{existing_section}\\n{expected}\" in pyproject_file.read_text(\n        encoding=\"utf-8\"\n    )\n\n\ndef test_init_existing_pyproject_with_build_system_fails(\n    tester: CommandTester, source_dir: Path, init_basic_inputs: str\n) -> None:\n    pyproject_file = source_dir / \"pyproject.toml\"\n    existing_section = \"\"\"\n[build-system]\nrequires = [\"setuptools >= 40.6.0\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n\"\"\"\n    pyproject_file.write_text(existing_section, encoding=\"utf-8\")\n    tester.execute(inputs=init_basic_inputs)\n    assert (\n        tester.io.fetch_error().strip()\n        == \"A pyproject.toml file with a defined build-system already exists.\"\n    )\n    assert existing_section in pyproject_file.read_text(encoding=\"utf-8\")\n\n\n@pytest.mark.parametrize(\n    \"name\",\n    [\n        None,\n        \"\",\n        \"foo\",\n        \"   foo  \",\n        \"foo==2.0\",\n        \"foo@2.0\",\n        \"  foo@2.0   \",\n        \"foo 2.0\",\n        \"   foo 2.0  \",\n    ],\n)\ndef test_validate_package_valid(name: str | None) -> None:\n    assert InitCommand._validate_package(name) == name\n\n\n@pytest.mark.parametrize(\n    \"name\", [\"foo bar 2.0\", \"   foo bar 2.0   \", \"foo bar foobar 2.0\"]\n)\ndef test_validate_package_invalid(name: str) -> None:\n    with pytest.raises(ValueError):\n        assert InitCommand._validate_package(name)\n\n\n@pytest.mark.parametrize(\n    \"author\",\n    [\n        str(b\"Jos\\x65\\xcc\\x81 Duarte\", \"utf-8\"),\n        str(b\"Jos\\xc3\\xa9 Duarte\", \"utf-8\"),\n    ],\n)\ndef test_validate_author(author: str) -> None:\n    \"\"\"\n    This test was added following issue #8779, hence, we're looking to see if the test\n    no longer throws an exception, hence the seemingly \"useless\" test of just running\n    the method.\n    \"\"\"\n    InitCommand._validate_author(author, \"\")\n\n\n@pytest.mark.parametrize(\n    \"package_name, include\",\n    (\n        (\"mypackage\", None),\n        (\"my-package\", \"my_package\"),\n        (\"my.package\", \"my\"),\n        (\"my-awesome-package\", \"my_awesome_package\"),\n        (\"my.awesome.package\", \"my\"),\n    ),\n)\ndef test_package_include(\n    tester: CommandTester,\n    package_name: str,\n    include: str | None,\n) -> None:\n    tester.execute(\n        inputs=\"\\n\".join(\n            (\n                package_name,\n                \"\",  # Version\n                \"\",  # Description\n                \"poetry\",  # Author\n                \"\",  # License\n                \">=3.10\",  # Python\n                \"n\",  # Interactive packages\n                \"n\",  # Interactive dev packages\n                \"\\n\",  # Generate\n            ),\n        ),\n    )\n\n    packages = \"\"\n    if include and module_name(package_name) != include:\n        packages = f'\\n[tool.poetry]\\npackages = [{{include = \"{include}\"}}]\\n'\n\n    expected = (\n        \"[project]\\n\"\n        f'name = \"{package_name.replace(\".\", \"-\")}\"\\n'  # canonicalized\n        'version = \"0.1.0\"\\n'\n        'description = \"\"\\n'\n        \"authors = [\\n\"\n        '    {name = \"poetry\"}\\n'\n        \"]\\n\"\n        'requires-python = \">=3.10\"\\n'\n        \"dependencies = [\\n\"\n        \"]\\n\"\n        f\"{packages}\"  # This line is optional. Thus, no newline here.\n    )\n    assert expected in tester.io.fetch_output()\n\n\n@pytest.mark.parametrize(\n    [\"use_poetry_python\", \"python\"],\n    [\n        (False, \"1.1\"),\n        (True, f\"{sys.version_info[0]}.{sys.version_info[1]}\"),\n    ],\n)\ndef test_respect_use_poetry_python_on_init(\n    use_poetry_python: bool,\n    python: str,\n    config: Config,\n    tester: CommandTester,\n    source_dir: Path,\n    mocked_python_register: MockedPythonRegister,\n    with_no_active_python: MagicMock,\n) -> None:\n    mocked_python_register(f\"{python}.1\", make_system=True)\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = use_poetry_python\n    pyproject_file = source_dir / \"pyproject.toml\"\n\n    tester.execute(\n        \"--author 'Your Name <you@example.com>' --name 'my-package'\",\n        interactive=False,\n    )\n\n    expected = f\"\"\"\\\nrequires-python = \">={python}\"\n\"\"\"\n\n    assert expected in pyproject_file.read_text(encoding=\"utf-8\")\n\n\ndef test_get_pool(mocker: MockerFixture, source_dir: Path) -> None:\n    \"\"\"\n    Since we are mocking _get_pool() in the other tests, we at least should make\n    sure it works in general. See https://github.com/python-poetry/poetry/issues/8634.\n    \"\"\"\n    mocker.patch(\"pathlib.Path.cwd\", return_value=source_dir)\n\n    app = Application()\n    command = app.find(\"init\")\n    assert isinstance(command, InitCommand)\n    pool = command._get_pool()\n    assert pool.repositories\n\n\ndef test_init_does_not_create_project_structure_in_empty_directory(\n    tester: CommandTester, source_dir: Path\n) -> None:\n    \"\"\"Test that poetry init does not create project structure in empty directory.\"\"\"\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.0.0\",  # Version\n        \"\",  # Description\n        \"n\",  # Author\n        \"\",  # License\n        \"\",  # Python\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    # Should only create pyproject.toml\n    assert (source_dir / \"pyproject.toml\").exists()\n\n    # Should NOT create these\n    assert not (source_dir / \"tests\").exists()\n    assert not (source_dir / \"my_package\").exists()\n    assert not (source_dir / \"src\").exists()\n    assert not (source_dir / \"README.md\").exists()\n\n\ndef test_init_does_not_create_project_structure_in_non_empty_directory(\n    tester: CommandTester, source_dir: Path\n) -> None:\n    \"\"\"Test that poetry init does not create project structure in non-empty directory.\"\"\"\n    # Create some existing files\n    (source_dir / \"existing_file.txt\").write_text(\"existing content\", encoding=\"utf-8\")\n    (source_dir / \"existing_dir\").mkdir()\n\n    inputs = [\n        \"my-package\",  # Package name\n        \"1.0.0\",  # Version\n        \"\",  # Description\n        \"n\",  # Author\n        \"\",  # License\n        \"\",  # Python\n        \"n\",  # Interactive packages\n        \"n\",  # Interactive dev packages\n        \"\\n\",  # Generate\n    ]\n\n    tester.execute(inputs=\"\\n\".join(inputs))\n\n    # Should only create pyproject.toml\n    assert (source_dir / \"pyproject.toml\").exists()\n\n    # Should NOT create these\n    assert not (source_dir / \"tests\").exists()\n    assert not (source_dir / \"my_package\").exists()\n    assert not (source_dir / \"src\").exists()\n    assert not (source_dir / \"README.md\").exists()\n\n    # Existing files should remain\n    assert (source_dir / \"existing_file.txt\").exists()\n    assert (source_dir / \"existing_dir\").exists()\n\n\ndef test_init_adds_readme_key_when_readme_exists(\n    tester: CommandTester, tmp_path: Path\n) -> None:\n    # Arrange: ensure README.md exists\n    readme = tmp_path / \"README.md\"\n    readme.write_text(\"# My Project\\n\", encoding=\"utf-8\")\n    # Act\n    tester.execute(interactive=False)\n    # Assert\n    pyproject = (tmp_path / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    assert 'readme = \"README.md\"' in pyproject\n\n\ndef test_init_does_not_add_readme_key_when_readme_missing(\n    tester: CommandTester, tmp_path: Path\n) -> None:\n    # Arrange: ensure README.md does NOT exist\n    readme = tmp_path / \"README.md\"\n    assert not readme.exists()\n    # Act\n    tester.execute(interactive=False)\n    # Assert\n    pyproject = (tmp_path / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    assert \"readme =\" not in pyproject\n"
  },
  {
    "path": "tests/console/commands/test_install.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.masonry.utils.module import ModuleOrPackageNotFoundError\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\n\nfrom poetry.console.commands.installer_command import InstallerCommand\nfrom poetry.console.exceptions import GroupNotFoundError\nfrom tests.helpers import TestLocker\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\nPYPROJECT_CONTENT = \"\"\"\\\n[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Python Poetry <tests@python-poetry.org>\"\n]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\nfizz = { version = \"^1.0\", optional = true }\nbuzz = { version = \"^2.0\", optional = true }\n\n[tool.poetry.group.foo.dependencies]\nfoo = \"^1.0\"\n\n[tool.poetry.group.bar.dependencies]\nbar = \"^1.1\"\n\n[tool.poetry.group.baz.dependencies]\nbaz = \"^1.2\"\n\n[tool.poetry.group.bim.dependencies]\nbim = \"^1.3\"\n\n[tool.poetry.group.bam]\noptional = true\n\n[tool.poetry.group.bam.dependencies]\nbam = \"^1.4\"\n\n[tool.poetry.extras]\nextras_a = [ \"fizz\" ]\nextras_b = [ \"buzz\" ]\n\"\"\"\n\n\n@pytest.fixture\ndef command() -> str:\n    return \"install\"\n\n\n@pytest.fixture\ndef poetry(project_factory: ProjectFactory) -> Poetry:\n    return project_factory(name=\"export\", pyproject_content=PYPROJECT_CONTENT)\n\n\n@pytest.fixture\ndef tester(\n    command_tester_factory: CommandTesterFactory, command: str, poetry: Poetry\n) -> CommandTester:\n    return command_tester_factory(command)\n\n\ndef _project_factory(\n    fixture_name: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n) -> Poetry:\n    source = fixture_dir(fixture_name)\n    pyproject_content = (source / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    poetry_lock_content = (source / \"poetry.lock\").read_text(encoding=\"utf-8\")\n    return project_factory(\n        name=\"foobar\",\n        pyproject_content=pyproject_content,\n        poetry_lock_content=poetry_lock_content,\n        source=source,\n    )\n\n\n@pytest.mark.parametrize(\n    (\"options\", \"groups\"),\n    [\n        (\"\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\"}),\n        (\"--only-root\", set()),\n        (f\"--only {MAIN_GROUP}\", {MAIN_GROUP}),\n        (\"--only foo\", {\"foo\"}),\n        (\"--only foo,bar\", {\"foo\", \"bar\"}),\n        (\"--only bam\", {\"bam\"}),\n        (\"--with bam\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\", \"bam\"}),\n        (\"--without foo,bar\", {MAIN_GROUP, \"baz\", \"bim\"}),\n        (f\"--without {MAIN_GROUP}\", {\"foo\", \"bar\", \"baz\", \"bim\"}),\n        (\"--with foo,bar --without baz --without bim --only bam\", {\"bam\"}),\n        (\"--all-groups\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\", \"bam\"}),\n        # net result zero options\n        (\"--with foo\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\"}),\n        (\"--without bam\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\"}),\n        (\"--with bam --without bam\", {MAIN_GROUP, \"foo\", \"bar\", \"baz\", \"bim\"}),\n        (\"--with foo --without foo\", {MAIN_GROUP, \"bar\", \"baz\", \"bim\"}),\n    ],\n)\n@pytest.mark.parametrize(\"with_root\", [True, False])\ndef test_group_options_are_passed_to_the_installer(\n    options: str,\n    groups: set[str],\n    with_root: bool,\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    Group options are passed properly to the installer.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n    editable_builder_mock = mocker.patch(\n        \"poetry.masonry.builders.editable.EditableBuilder\",\n        side_effect=ModuleOrPackageNotFoundError(),\n    )\n\n    if not with_root:\n        options = f\"--no-root {options}\"\n\n    status_code = tester.execute(options)\n\n    if options == \"--no-root --only-root\" or with_root:\n        assert status_code == 1\n        return\n    else:\n        assert status_code == 0\n\n    package_groups = set(tester.command.poetry.package._dependency_groups)\n    installer_groups = set(tester.command.installer._groups or [])\n\n    assert installer_groups <= package_groups\n    assert set(installer_groups) == groups\n\n    if with_root:\n        assert editable_builder_mock.call_count == 1\n        assert editable_builder_mock.call_args_list[0][0][0] == tester.command.poetry\n    else:\n        assert editable_builder_mock.call_count == 0\n\n\n@pytest.mark.parametrize(\"sync\", [True, False])\ndef test_sync_option_is_passed_to_the_installer(\n    tester: CommandTester, mocker: MockerFixture, sync: bool\n) -> None:\n    \"\"\"\n    The --sync option is passed properly to the installer.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    tester.execute(\"--sync\" if sync else \"\")\n\n    assert tester.command.installer._requires_synchronization is sync\n\n    error = tester.io.fetch_error()\n    if sync:\n        assert \"deprecated\" in error\n        assert \"poetry sync\" in error\n    else:\n        assert error == \"\"\n\n\n@pytest.mark.parametrize(\"compile\", [False, True])\ndef test_compile_option_is_passed_to_the_installer(\n    tester: CommandTester, mocker: MockerFixture, compile: bool\n) -> None:\n    \"\"\"\n    The --compile option is passed properly to the installer.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n    enable_bytecode_compilation_mock = mocker.patch.object(\n        tester.command.installer.executor._wheel_installer,\n        \"enable_bytecode_compilation\",\n    )\n\n    tester.execute(\"--compile\" if compile else \"\")\n\n    enable_bytecode_compilation_mock.assert_called_once_with(compile)\n\n\n@pytest.mark.parametrize(\"skip_directory_cli_value\", [True, False])\ndef test_no_directory_is_passed_to_installer(\n    tester: CommandTester, mocker: MockerFixture, skip_directory_cli_value: bool\n) -> None:\n    \"\"\"\n    The --no-directory option is passed to the installer.\n    \"\"\"\n\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    if skip_directory_cli_value is True:\n        tester.execute(\"--no-directory\")\n    else:\n        tester.execute()\n\n    assert tester.command.installer._skip_directory is skip_directory_cli_value\n\n\ndef test_no_all_extras_doesnt_populate_installer(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    \"\"\"\n    Not passing --all-extras means the installer doesn't see any extras.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    tester.execute()\n\n    assert not tester.command.installer._extras\n\n\ndef test_all_extras_populates_installer(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    \"\"\"\n    The --all-extras option results in extras passed to the installer.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    tester.execute(\"--all-extras\")\n\n    assert tester.command.installer._extras == [\"extras-a\", \"extras-b\"]\n\n\ndef test_extras_are_parsed_and_populate_installer(\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    tester.execute('--extras \"first second third\"')\n\n    assert tester.command.installer._extras == [\"first\", \"second\", \"third\"]\n\n\n@pytest.mark.parametrize(\n    (\"options\", \"call_count\"),\n    [\n        (\"--no-plugins\", 0),\n        (\"\", 1),\n    ],\n)\ndef test_install_ensures_project_plugins(\n    tester: CommandTester, mocker: MockerFixture, options: str, call_count: int\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n    ensure_project_plugins = mocker.patch(\n        \"poetry.plugins.plugin_manager.PluginManager.ensure_project_plugins\"\n    )\n\n    tester.execute(options)\n\n    assert ensure_project_plugins.call_count == call_count\n\n\ndef test_extras_conflicts_all_extras(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    \"\"\"\n    The --extras doesn't make sense with --all-extras.\n    \"\"\"\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    tester.execute(\"--extras foo --all-extras\")\n\n    assert tester.status_code == 1\n    assert (\n        tester.io.fetch_error()\n        == \"You cannot specify explicit `--extras` while installing using\"\n        \" `--all-extras`.\\n\"\n    )\n\n\n@pytest.mark.parametrize(\n    \"options\",\n    [\n        \"--with foo\",\n        \"--without foo\",\n        \"--with foo,bar --without baz\",\n        \"--only foo\",\n        \"--all-groups\",\n    ],\n)\ndef test_only_root_conflicts_with_without_only_all_groups(\n    options: str,\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    tester.execute(f\"{options} --only-root\")\n\n    assert tester.status_code == 1\n    assert (\n        tester.io.fetch_error()\n        == \"The `--with`, `--without`, `--only` and `--all-groups` options cannot be used with\"\n        \" the `--only-root` option.\\n\"\n    )\n\n\n@pytest.mark.parametrize(\n    \"options\",\n    [\n        \"--with foo\",\n        \"--without foo\",\n        \"--with foo,bar --without baz\",\n        \"--only foo\",\n    ],\n)\ndef test_all_groups_conflicts_with_only_with_without(\n    options: str,\n    tester: CommandTester,\n    mocker: MockerFixture,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    tester.execute(f\"{options} --all-groups\")\n\n    assert tester.status_code == 1\n    assert (\n        tester.io.fetch_error()\n        == \"You cannot specify `--with`, `--without`, or `--only` when using `--all-groups`.\\n\"\n    )\n\n\n@pytest.mark.parametrize(\n    (\"options\", \"valid_groups\", \"should_raise\"),\n    [\n        ({\"--with\": MAIN_GROUP}, {MAIN_GROUP}, False),\n        ({\"--with\": \"spam\"}, set(), True),\n        ({\"--with\": \"spam,foo\"}, {\"foo\"}, True),\n        ({\"--without\": \"spam\"}, set(), True),\n        ({\"--without\": \"spam,bar\"}, {\"bar\"}, True),\n        ({\"--with\": \"eggs,ham\", \"--without\": \"spam\"}, set(), True),\n        ({\"--with\": \"eggs,ham\", \"--without\": \"spam,baz\"}, {\"baz\"}, True),\n        ({\"--only\": \"spam\"}, set(), True),\n        ({\"--only\": \"bim\"}, {\"bim\"}, False),\n        ({\"--only\": MAIN_GROUP}, {MAIN_GROUP}, False),\n    ],\n)\ndef test_invalid_groups_with_without_only(\n    tester: CommandTester,\n    mocker: MockerFixture,\n    options: dict[str, str],\n    valid_groups: set[str],\n    should_raise: bool,\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    cmd_args = \" \".join(f\"{flag} {groups}\" for (flag, groups) in options.items())\n\n    if not should_raise:\n        tester.execute(cmd_args)\n        assert tester.status_code == 1\n    else:\n        with pytest.raises(GroupNotFoundError, match=r\"^Group\\(s\\) not found:\") as e:\n            tester.execute(cmd_args)\n        assert tester.status_code is None\n        for opt, groups in options.items():\n            group_list = groups.split(\",\")\n            invalid_groups = sorted(set(group_list) - valid_groups)\n            for group in invalid_groups:\n                assert (\n                    re.search(rf\"{group} \\(via .*{opt}.*\\)\", str(e.value)) is not None\n                )\n\n\ndef test_dry_run_populates_installer(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    \"\"\"\n    The --dry-run option results in extras passed to the installer.\n    \"\"\"\n\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    tester.execute(\"--dry-run\")\n\n    assert tester.command.installer._dry_run is True\n\n\ndef test_dry_run_does_not_build(tester: CommandTester, mocker: MockerFixture) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n    mocked_editable_builder = mocker.patch(\n        \"poetry.masonry.builders.editable.EditableBuilder\"\n    )\n\n    tester.execute(\"--dry-run\")\n\n    assert mocked_editable_builder.return_value.build.call_count == 0\n\n\ndef test_install_logs_output(tester: CommandTester, mocker: MockerFixture) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n    mocker.patch(\"poetry.masonry.builders.editable.EditableBuilder\")\n\n    tester.execute()\n\n    assert tester.status_code == 0\n    assert (\n        tester.io.fetch_output()\n        == \"\\nInstalling the current project: simple-project (1.2.3)\\n\"\n    )\n\n\ndef test_install_logs_output_decorated(\n    tester: CommandTester, mocker: MockerFixture\n) -> None:\n    assert isinstance(tester.command, InstallerCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n    mocker.patch(\"poetry.masonry.builders.editable.EditableBuilder\")\n\n    tester.execute(decorated=True)\n\n    expected = (\n        \"\\n\"\n        \"\\x1b[39;1mInstalling\\x1b[39;22m the current project: \"\n        \"\\x1b[36msimple-project\\x1b[39m (\\x1b[39;1m1.2.3\\x1b[39;22m)\"\n        \"\\x1b[1G\\x1b[2K\"\n        \"\\x1b[39;1mInstalling\\x1b[39;22m the current project: \"\n        \"\\x1b[36msimple-project\\x1b[39m (\\x1b[32m1.2.3\\x1b[39m)\"\n        \"\\n\"\n    )\n    assert tester.status_code == 0\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\"with_root\", [True, False])\n@pytest.mark.parametrize(\"error\", [\"module\", \"readme\", \"\"])\ndef test_install_warning_corrupt_root(\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    project_factory: ProjectFactory,\n    with_root: bool,\n    error: str,\n) -> None:\n    name = \"corrupt\"\n    content = f\"\"\"\\\n[tool.poetry]\nname = \"{name}\"\nversion = \"1.2.3\"\ndescription = \"\"\nauthors = []\n\"\"\"\n    if error == \"readme\":\n        content += 'readme = \"missing_readme.md\"\\n'\n    poetry = project_factory(name=name, pyproject_content=content)\n    if error != \"module\":\n        (poetry.pyproject_path.parent / f\"{name}.py\").touch()\n\n    tester = command_tester_factory(command, poetry=poetry)\n    tester.execute(\"\" if with_root else \"--no-root\")\n\n    if error and with_root:\n        assert tester.status_code == 1\n    else:\n        assert tester.status_code == 0\n\n    if with_root and error:\n        assert \"The current project could not be installed: \" in tester.io.fetch_error()\n    else:\n        assert tester.io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--without dev\"])\n@pytest.mark.parametrize(\n    \"project\", [\"missing_directory_dependency\", \"missing_file_dependency\"]\n)\ndef test_install_path_dependency_does_not_exist(\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    project: str,\n    options: str,\n) -> None:\n    poetry = _project_factory(project, project_factory, fixture_dir)\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.locked(True)\n    tester = command_tester_factory(command, poetry=poetry)\n    if options:\n        tester.execute(options)\n    else:\n        with pytest.raises(ValueError, match=\"does not exist\"):\n            tester.execute(options)\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--extras notinstallable\"])\ndef test_install_extra_path_dependency_does_not_exist(\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    options: str,\n) -> None:\n    project = \"missing_extra_directory_dependency\"\n    poetry = _project_factory(project, project_factory, fixture_dir)\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.locked(True)\n    tester = command_tester_factory(command, poetry=poetry)\n    if not options:\n        tester.execute(options)\n    else:\n        with pytest.raises(ValueError, match=\"does not exist\"):\n            tester.execute(options)\n\n\n@pytest.mark.parametrize(\"options\", [\"\", \"--no-directory\"])\ndef test_install_missing_directory_dependency_with_no_directory(\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    options: str,\n) -> None:\n    poetry = _project_factory(\n        \"missing_directory_dependency\", project_factory, fixture_dir\n    )\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.locked(True)\n    tester = command_tester_factory(command, poetry=poetry)\n    if options:\n        tester.execute(options)\n    else:\n        with pytest.raises(ValueError, match=\"does not exist\"):\n            tester.execute(options)\n\n\ndef test_non_package_mode_does_not_try_to_install_root(\n    command_tester_factory: CommandTesterFactory,\n    command: str,\n    project_factory: ProjectFactory,\n) -> None:\n    content = \"\"\"\\\n[tool.poetry]\npackage-mode = false\n\"\"\"\n    poetry = project_factory(name=\"non-package-mode\", pyproject_content=content)\n\n    tester = command_tester_factory(command, poetry=poetry)\n    tester.execute()\n\n    assert tester.status_code == 0\n    assert tester.io.fetch_error() == \"\"\n"
  },
  {
    "path": "tests/console/commands/test_lock.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.packages import Locker\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.poetry import Poetry\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef source_dir(tmp_path: Path) -> Path:\n    return Path(tmp_path.as_posix())\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"lock\")\n\n\ndef _project_factory(\n    fixture_name: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n) -> Poetry:\n    source = fixture_dir(fixture_name)\n    pyproject_content = (source / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    poetry_lock_content = (source / \"poetry.lock\").read_text(encoding=\"utf-8\")\n    return project_factory(\n        name=\"foobar\",\n        pyproject_content=pyproject_content,\n        poetry_lock_content=poetry_lock_content,\n        source=source,\n    )\n\n\n@pytest.fixture\ndef poetry_with_outdated_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"outdated_lock\", project_factory, fixture_dir)\n\n\n@pytest.fixture\ndef poetry_with_up_to_date_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"up_to_date_lock\", project_factory, fixture_dir)\n\n\n@pytest.fixture\ndef poetry_with_old_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"old_lock\", project_factory, fixture_dir)\n\n\n@pytest.fixture\ndef poetry_with_nested_path_deps_old_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"old_lock_path_dependency\", project_factory, fixture_dir)\n\n\n@pytest.fixture\ndef poetry_with_incompatible_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"incompatible_lock\", project_factory, fixture_dir)\n\n\n@pytest.fixture\ndef poetry_with_invalid_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    return _project_factory(\"invalid_lock\", project_factory, fixture_dir)\n\n\ndef test_lock_does_not_update_if_not_necessary(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_old_lockfile: Poetry,\n    repo: TestRepository,\n) -> None:\n    package = get_package(\"sampleproject\", \"1.3.1\")\n    repo.add_package(package)\n    repo.add_package(get_package(\"sampleproject\", \"2.0.0\"))\n\n    locker = Locker(\n        lock=poetry_with_old_lockfile.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry_with_old_lockfile.locker._pyproject_data,\n    )\n    poetry_with_old_lockfile.set_locker(locker)\n\n    locked_repository = poetry_with_old_lockfile.locker.locked_repository()\n    assert (\n        poetry_with_old_lockfile.locker.lock_data[\"metadata\"].get(\"lock-version\")\n        == \"1.0\"\n    )\n\n    # set correct files to avoid cache refresh\n    package.files = (\n        locker.locked_repository()\n        .package(\"sampleproject\", Version.parse(\"1.3.1\"))\n        .files\n    )\n\n    tester = command_tester_factory(\"lock\", poetry=poetry_with_old_lockfile)\n    tester.execute()\n\n    locker = Locker(\n        lock=poetry_with_old_lockfile.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data={},\n    )\n    packages = locker.locked_repository().packages\n\n    assert len(packages) == len(locked_repository.packages)\n\n    assert locker.lock_data[\"metadata\"].get(\"lock-version\") == \"2.1\"\n\n    for package in packages:\n        assert locked_repository.find_packages(package.to_dependency())\n\n\n@pytest.mark.parametrize(\"regenerate\", [True, False])\ndef test_lock_always_updates_path_dependencies(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_nested_path_deps_old_lockfile: Poetry,\n    repo: TestRepository,\n    regenerate: bool,\n) -> None:\n    \"\"\"\n    The lock file contains a variant of the directory dependency \"quix\" that does\n    not depend on \"sampleproject\". Although the version of \"quix\" has not been changed,\n    it should be re-solved because there is always only one valid version\n    of a directory dependency at any time.\n    \"\"\"\n    repo.add_package(get_package(\"sampleproject\", \"1.3.1\"))\n\n    locker = Locker(\n        lock=poetry_with_nested_path_deps_old_lockfile.pyproject.file.path.parent\n        / \"poetry.lock\",\n        pyproject_data=poetry_with_nested_path_deps_old_lockfile.locker._pyproject_data,\n    )\n    poetry_with_nested_path_deps_old_lockfile.set_locker(locker)\n\n    tester = command_tester_factory(\n        \"lock\", poetry=poetry_with_nested_path_deps_old_lockfile\n    )\n    tester.execute(\"--regenerate\" if regenerate else \"\")\n\n    packages = locker.locked_repository().packages\n\n    assert {p.name for p in packages} == {\"quix\", \"sampleproject\"}\n\n\n@pytest.mark.parametrize(\"regenerate\", [True, False])\n@pytest.mark.parametrize(\n    \"project\", [\"missing_directory_dependency\", \"missing_file_dependency\"]\n)\ndef test_lock_path_dependency_does_not_exist(\n    command_tester_factory: CommandTesterFactory,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    project: str,\n    regenerate: bool,\n) -> None:\n    poetry = _project_factory(project, project_factory, fixture_dir)\n    locker = Locker(\n        lock=poetry.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry.locker._pyproject_data,\n    )\n    poetry.set_locker(locker)\n    options = \"--regenerate\" if regenerate else \"\"\n\n    tester = command_tester_factory(\"lock\", poetry=poetry)\n    if regenerate or \"directory\" in project:\n        # directory dependencies are always updated\n        with pytest.raises(ValueError, match=\"does not exist\"):\n            tester.execute(options)\n    else:\n        tester.execute(options)\n\n\n@pytest.mark.parametrize(\"regenerate\", [True, False])\n@pytest.mark.parametrize(\n    \"project\", [\"deleted_directory_dependency\", \"deleted_file_dependency\"]\n)\ndef test_lock_path_dependency_deleted_from_pyproject(\n    command_tester_factory: CommandTesterFactory,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    project: str,\n    regenerate: bool,\n) -> None:\n    poetry = _project_factory(project, project_factory, fixture_dir)\n    locker = Locker(\n        lock=poetry.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry.locker._pyproject_data,\n    )\n    poetry.set_locker(locker)\n\n    tester = command_tester_factory(\"lock\", poetry=poetry)\n    tester.execute(\"--regenerate\" if regenerate else \"\")\n\n    packages = locker.locked_repository().packages\n\n    assert {p.name for p in packages} == set()\n\n\n@pytest.mark.parametrize(\"regenerate\", [True, False])\ndef test_lock_with_incompatible_lockfile(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_incompatible_lockfile: Poetry,\n    repo: TestRepository,\n    regenerate: bool,\n) -> None:\n    repo.add_package(get_package(\"sampleproject\", \"1.3.1\"))\n\n    locker = Locker(\n        lock=poetry_with_incompatible_lockfile.pyproject.file.path.parent\n        / \"poetry.lock\",\n        pyproject_data=poetry_with_incompatible_lockfile.locker._pyproject_data,\n    )\n    poetry_with_incompatible_lockfile.set_locker(locker)\n\n    tester = command_tester_factory(\"lock\", poetry=poetry_with_incompatible_lockfile)\n    if regenerate:\n        # still possible because lock file is not required\n        status_code = tester.execute(\"--regenerate\")\n        assert status_code == 0\n    else:\n        # not possible because of incompatible lock file\n        expected = (\n            \"(?s)lock file is not compatible .*\"\n            \" regenerate the lock file with the `poetry lock` command\"\n        )\n        with pytest.raises(RuntimeError, match=expected):\n            tester.execute()\n\n\n@pytest.mark.parametrize(\"regenerate\", [True, False])\ndef test_lock_with_invalid_lockfile(\n    command_tester_factory: CommandTesterFactory,\n    poetry_with_invalid_lockfile: Poetry,\n    repo: TestRepository,\n    regenerate: bool,\n) -> None:\n    repo.add_package(get_package(\"sampleproject\", \"1.3.1\"))\n\n    locker = Locker(\n        lock=poetry_with_invalid_lockfile.pyproject.file.path.parent / \"poetry.lock\",\n        pyproject_data=poetry_with_invalid_lockfile.locker._pyproject_data,\n    )\n    poetry_with_invalid_lockfile.set_locker(locker)\n\n    tester = command_tester_factory(\"lock\", poetry=poetry_with_invalid_lockfile)\n    if regenerate:\n        # still possible because lock file is not required\n        status_code = tester.execute(\"--regenerate\")\n        assert status_code == 0\n    else:\n        # not possible because of broken lock file\n        with pytest.raises(RuntimeError, match=\"Unable to read the lock file\"):\n            tester.execute()\n"
  },
  {
    "path": "tests/console/commands/test_new.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.utils.helpers import module_name\n\nfrom poetry.factory import Factory\n\n\nif TYPE_CHECKING:\n    from unittest.mock import MagicMock\n\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.config.config import Config\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import MockedPythonRegister\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"new\")\n\n\ndef verify_project_directory(\n    path: Path,\n    package_name: str,\n    package_path: str | Path,\n    is_flat: bool = False,\n) -> Poetry:\n    package_path = Path(package_path)\n    assert path.is_dir()\n\n    pyproject = path / \"pyproject.toml\"\n    assert pyproject.is_file()\n\n    init_file = path / package_path / \"__init__.py\"\n    assert init_file.is_file()\n\n    tests_init_file = path / \"tests\" / \"__init__.py\"\n    assert tests_init_file.is_file()\n\n    poetry = Factory().create_poetry(cwd=path)\n    assert poetry.package.name == package_name\n\n    if is_flat:\n        package_include = {\"include\": package_path.parts[0]}\n    else:\n        package_include = {\n            \"include\": package_path.relative_to(\"src\").parts[0],\n            \"from\": \"src\",\n        }\n\n    name = poetry.package.name\n    packages = poetry.local_config.get(\"packages\")\n\n    if not packages:\n        assert module_name(name) == package_include.get(\"include\")\n    else:\n        assert len(packages) == 1\n        assert packages[0] == package_include\n\n    return poetry\n\n\n@pytest.mark.parametrize(\n    \"options,directory,package_name,package_path,include_from\",\n    [\n        ([\"--flat\"], \"package\", \"package\", \"package\", None),\n        ([], \"package\", \"package\", \"src/package\", \"src\"),\n        (\n            [\"--flat\", \"--name namespace.package\"],\n            \"namespace-package\",\n            \"namespace-package\",\n            \"namespace/package\",\n            None,\n        ),\n        (\n            [\"--name namespace.package\"],\n            \"namespace-package\",\n            \"namespace-package\",\n            \"src/namespace/package\",\n            \"src\",\n        ),\n        (\n            [\"--flat\", \"--name namespace.package_a\"],\n            \"namespace-package_a\",\n            \"namespace-package-a\",\n            \"namespace/package_a\",\n            None,\n        ),\n        (\n            [\"--name namespace.package_a\"],\n            \"namespace-package_a\",\n            \"namespace-package-a\",\n            \"src/namespace/package_a\",\n            \"src\",\n        ),\n        (\n            [\"--flat\", \"--name namespace_package\"],\n            \"namespace-package\",\n            \"namespace-package\",\n            \"namespace_package\",\n            None,\n        ),\n        (\n            [\"--name namespace_package\"],\n            \"namespace-package\",\n            \"namespace-package\",\n            \"src/namespace_package\",\n            \"src\",\n        ),\n        (\n            [\"--flat\", \"--name namespace.package\"],\n            \"package\",\n            \"namespace-package\",\n            \"namespace/package\",\n            None,\n        ),\n        (\n            [\"--name namespace.package\"],\n            \"package\",\n            \"namespace-package\",\n            \"src/namespace/package\",\n            \"src\",\n        ),\n        (\n            [\"--name namespace.package\", \"--flat\"],\n            \"package\",\n            \"namespace-package\",\n            \"namespace/package\",\n            None,\n        ),\n        (\n            [\"--name namespace.package\"],\n            \"package\",\n            \"namespace-package\",\n            \"src/namespace/package\",\n            \"src\",\n        ),\n        (\n            [\"--flat\"],\n            \"namespace_package\",\n            \"namespace-package\",\n            \"namespace_package\",\n            None,\n        ),\n        (\n            [\"--name namespace_package\"],\n            \"namespace_package\",\n            \"namespace-package\",\n            \"src/namespace_package\",\n            \"src\",\n        ),\n    ],\n)\ndef test_command_new(\n    options: list[str],\n    directory: str,\n    package_name: str,\n    package_path: str,\n    include_from: str | None,\n    tester: CommandTester,\n    tmp_path: Path,\n) -> None:\n    path = tmp_path / directory\n    options.append(str(path))\n    tester.execute(\" \".join(options))\n    verify_project_directory(path, package_name, package_path, \"--flat\" in options)\n\n\n@pytest.mark.parametrize((\"fmt\",), [(None,), (\"md\",), (\"rst\",), (\"adoc\",), (\"creole\",)])\ndef test_command_new_with_readme(\n    fmt: str | None, tester: CommandTester, tmp_path: Path\n) -> None:\n    package = \"package\"\n    path = tmp_path / package\n    options = [path.as_posix()]\n\n    if fmt:\n        options.insert(0, f\"--readme {fmt}\")\n\n    tester.execute(\" \".join(options))\n\n    poetry = verify_project_directory(path, package, Path(\"src\") / package)\n    project_section = poetry.pyproject.data[\"project\"]\n    assert isinstance(project_section, dict)\n\n    readme_file = path / f\"README.{fmt or 'md'}\"\n    assert readme_file.exists()\n\n    assert \"readme\" in project_section\n    assert project_section[\"readme\"] == readme_file.name\n\n\n@pytest.mark.parametrize(\n    [\"use_poetry_python\", \"python\"],\n    [\n        (False, \"1.1\"),\n        (True, f\"{sys.version_info[0]}.{sys.version_info[1]}\"),\n    ],\n)\ndef test_respect_use_poetry_python_on_new(\n    use_poetry_python: bool,\n    python: str,\n    config: Config,\n    tester: CommandTester,\n    tmp_path: Path,\n    mocked_python_register: MockedPythonRegister,\n    with_no_active_python: MagicMock,\n) -> None:\n    mocked_python_register(f\"{python}.1\", make_system=True)\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = use_poetry_python\n\n    package = \"package\"\n    path = tmp_path / package\n    options = [str(path)]\n    tester.execute(\" \".join(options))\n\n    pyproject_file = path / \"pyproject.toml\"\n\n    expected = f\"\"\"\\\nrequires-python = \">={python}\"\n\"\"\"\n\n    assert expected in pyproject_file.read_text(encoding=\"utf-8\")\n\n\ndef test_basic_interactive_new(\n    tester: CommandTester, tmp_path: Path, init_basic_inputs: str, new_basic_toml: str\n) -> None:\n    path = tmp_path / \"somepackage\"\n    tester.execute(f\"--interactive {path.as_posix()}\", inputs=init_basic_inputs)\n    verify_project_directory(path, \"my-package\", \"src/my_package\")\n    assert new_basic_toml in tester.io.fetch_output()\n\n\ndef test_new_creates_structure_in_empty_existing_directory(\n    tester: CommandTester, tmp_path: Path\n) -> None:\n    \"\"\"Test that poetry new creates structure in existing but empty directory.\"\"\"\n    # Create empty directory\n    package_dir = tmp_path / \"my-package\"\n    package_dir.mkdir()\n\n    tester.execute(str(package_dir))\n\n    # Should create full project structure\n    verify_project_directory(package_dir, \"my-package\", \"src/my_package\")\n\n    assert (package_dir / \"tests\").exists()\n    assert (package_dir / \"src\" / \"my_package\").exists()\n    assert (package_dir / \"pyproject.toml\").exists()\n    assert (package_dir / \"README.md\").exists()\n\n\ndef test_new_with_dot_in_empty_directory(tester: CommandTester, tmp_path: Path) -> None:\n    \"\"\"Test that poetry new . works in empty directory and creates structure.\"\"\"\n    import os\n\n    test_dir = \"test_new_with_dot_in_empty_directory\"\n\n    # Change to the temporary directory\n    original_cwd = os.getcwd()\n    os.chdir(tmp_path)\n    os.mkdir(test_dir)\n    tmp_path = Path(original_cwd) / test_dir\n    os.chdir(tmp_path)\n\n    try:\n        tester.execute(\".\")\n\n        # Should create full project structure\n        assert (tmp_path / \"tests\").exists()\n        assert (tmp_path / \"src\").exists()\n        assert (tmp_path / \"pyproject.toml\").exists()\n        assert (tmp_path / \"README.md\").exists()\n    finally:\n        # Always restore original directory\n        os.chdir(original_cwd)\n"
  },
  {
    "path": "tests/console/commands/test_publish.py",
    "content": "from __future__ import annotations\n\nimport shutil\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import NoReturn\n\nimport pytest\nimport requests\nimport responses\n\nfrom poetry.factory import Factory\n\n\nif TYPE_CHECKING:\n    from cleo.testers.application_tester import ApplicationTester\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.env import VirtualEnv\n    from tests.helpers import PoetryTestApplication\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n\n\ndef test_publish_not_possible_in_non_package_mode(\n    fixture_dir: FixtureDirGetter,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    source_dir = fixture_dir(\"non_package_mode\")\n\n    poetry = Factory().create_poetry(source_dir)\n    tester = command_tester_factory(\"publish\", poetry)\n\n    assert tester.execute() == 1\n    assert (\n        tester.io.fetch_error()\n        == \"Publishing a package is not possible in non-package mode.\\n\"\n    )\n\n\ndef test_publish_returns_non_zero_code_for_upload_errors(\n    app: PoetryTestApplication,\n    app_tester: ApplicationTester,\n    http: responses.RequestsMock,\n) -> None:\n    http.post(\"https://upload.pypi.org/legacy/\", status=400, body=\"Bad Request\")\n\n    exit_code = app_tester.execute(\"publish --username foo --password bar\")\n\n    assert exit_code == 1\n\n    expected_output = \"\"\"\nPublishing simple-project (1.2.3) to PyPI\n\"\"\"\n    expected_error_output = \"\"\"\\\nHTTP Error 400: Bad Request | b'Bad Request'\n\"\"\"\n\n    assert expected_output in app_tester.io.fetch_output()\n    assert expected_error_output in app_tester.io.fetch_error()\n\n\n@pytest.mark.filterwarnings(\"ignore::pytest.PytestUnhandledThreadExceptionWarning\")\ndef test_publish_returns_non_zero_code_for_connection_errors(\n    app: PoetryTestApplication,\n    app_tester: ApplicationTester,\n    http: responses.RequestsMock,\n) -> None:\n    def request_callback(request: requests.PreparedRequest) -> NoReturn:\n        raise requests.ConnectionError\n\n    http.add_callback(\n        responses.POST, \"https://upload.pypi.org/legacy/\", callback=request_callback\n    )\n\n    exit_code = app_tester.execute(\"publish --username foo --password bar\")\n\n    assert exit_code == 1\n\n    assert \"Error connecting to repository\" in app_tester.io.fetch_error()\n\n\ndef test_publish_with_cert(\n    app_tester: ApplicationTester, mocker: MockerFixture\n) -> None:\n    publisher_publish = mocker.patch(\"poetry.publishing.Publisher.publish\")\n\n    app_tester.execute(\"publish --cert path/to/ca.pem\")\n\n    assert [\n        (None, None, None, Path(\"path/to/ca.pem\"), None, False, False)\n    ] == publisher_publish.call_args\n\n\ndef test_publish_with_client_cert(\n    app_tester: ApplicationTester, mocker: MockerFixture\n) -> None:\n    publisher_publish = mocker.patch(\"poetry.publishing.Publisher.publish\")\n\n    app_tester.execute(\"publish --client-cert path/to/client.pem\")\n    assert [\n        (None, None, None, None, Path(\"path/to/client.pem\"), False, False)\n    ] == publisher_publish.call_args\n\n\n@pytest.mark.parametrize(\n    \"options\",\n    [\n        \"--dry-run\",\n        \"--skip-existing\",\n        \"--dry-run --skip-existing\",\n    ],\n)\ndef test_publish_dry_run_skip_existing(\n    app_tester: ApplicationTester, http: responses.RequestsMock, options: str\n) -> None:\n    http.post(\"https://upload.pypi.org/legacy/\", status=409, body=\"Conflict\")\n\n    exit_code = app_tester.execute(f\"publish {options} --username foo --password bar\")\n\n    assert exit_code == 0\n\n    output = app_tester.io.fetch_output()\n    error = app_tester.io.fetch_error()\n\n    assert \"Publishing simple-project (1.2.3) to PyPI\" in output\n    assert \"- Uploading simple_project-1.2.3.tar.gz\" in error\n    assert \"- Uploading simple_project-1.2.3-py2.py3-none-any.whl\" in error\n\n\ndef test_skip_existing_output(\n    app_tester: ApplicationTester, http: responses.RequestsMock\n) -> None:\n    http.post(\"https://upload.pypi.org/legacy/\", status=409, body=\"Conflict\")\n\n    exit_code = app_tester.execute(\n        \"publish --skip-existing --username foo --password bar\"\n    )\n\n    assert exit_code == 0\n\n    error = app_tester.io.fetch_error()\n    assert \"- Uploading simple_project-1.2.3.tar.gz File exists. Skipping\" in error\n\n\n@pytest.mark.parametrize(\"dist_dir\", [None, \"dist\", \"other_dist/dist\", \"absolute\"])\ndef test_publish_dist_dir_option(\n    http: responses.RequestsMock,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    tmp_venv: VirtualEnv,\n    command_tester_factory: CommandTesterFactory,\n    dist_dir: str | None,\n) -> None:\n    source_dir = fixture_dir(\"with_multiple_dist_dir\")\n    target_dir = tmp_path / \"project\"\n    shutil.copytree(str(source_dir), str(target_dir))\n\n    http.post(\"https://upload.pypi.org/legacy/\", status=409, body=\"Conflict\")\n\n    poetry = Factory().create_poetry(target_dir)\n    tester = command_tester_factory(\"publish\", poetry, environment=tmp_venv)\n\n    if dist_dir is None:\n        exit_code = tester.execute(\"--dry-run\")\n    elif dist_dir == \"absolute\":\n        exit_code = tester.execute(f\"--dist-dir {target_dir / 'dist'} --dry-run\")\n    else:\n        exit_code = tester.execute(f\"--dist-dir {dist_dir} --dry-run\")\n\n    assert exit_code == 0\n\n    output = tester.io.fetch_output()\n    error = tester.io.fetch_error()\n\n    assert \"Publishing simple-project (1.2.3) to PyPI\" in output\n    assert \"- Uploading simple_project-1.2.3.tar.gz\" in error\n    assert \"- Uploading simple_project-1.2.3-py2.py3-none-any.whl\" in error\n\n\n@pytest.mark.parametrize(\"dist_dir\", [\"../dist\", \"tmp/dist\", \"absolute\"])\ndef test_publish_dist_dir_and_build_options(\n    http: responses.RequestsMock,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    tmp_venv: VirtualEnv,\n    command_tester_factory: CommandTesterFactory,\n    dist_dir: str | None,\n) -> None:\n    source_dir = fixture_dir(\"simple_project\")\n    target_dir = tmp_path / \"project\"\n    shutil.copytree(str(source_dir), str(target_dir))\n\n    # Remove dist dir because as it will be built again\n    shutil.rmtree(target_dir / \"dist\")\n\n    http.post(\"https://upload.pypi.org/legacy/\", status=409, body=\"Conflict\")\n\n    poetry = Factory().create_poetry(target_dir)\n    tester = command_tester_factory(\"publish\", poetry, environment=tmp_venv)\n\n    if dist_dir == \"absolute\":\n        exit_code = tester.execute(\n            f\"--dist-dir {target_dir / 'test/dist'} --dry-run --build\"\n        )\n    else:\n        exit_code = tester.execute(f\"--dist-dir {dist_dir} --dry-run --build\")\n\n    assert exit_code == 0\n\n    output = tester.io.fetch_output()\n    error = tester.io.fetch_error()\n\n    assert \"Publishing simple-project (1.2.3) to PyPI\" in output\n    assert \"- Uploading simple_project-1.2.3.tar.gz\" in error\n    assert \"- Uploading simple_project-1.2.3-py2.py3-none-any.whl\" in error\n"
  },
  {
    "path": "tests/console/commands/test_remove.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import cast\n\nimport pytest\nimport tomlkit\n\nfrom poetry.core.packages.dependency_group import DependencyGroup\nfrom poetry.core.packages.package import Package\n\nfrom poetry.factory import Factory\nfrom tests.helpers import TestLocker\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n    from tomlkit import TOMLDocument\n\n    from poetry.poetry import Poetry\n    from poetry.repositories import Repository\n    from tests.helpers import PoetryTestApplication\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef poetry_with_up_to_date_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Callable[[str], Poetry]:\n    def get_poetry(fixture_name: str) -> Poetry:\n        source = fixture_dir(fixture_name)\n\n        poetry = project_factory(\n            name=\"foobar\",\n            pyproject_content=(source / \"pyproject.toml\").read_text(encoding=\"utf-8\"),\n            poetry_lock_content=(source / \"poetry.lock\").read_text(encoding=\"utf-8\"),\n        )\n\n        assert isinstance(poetry.locker, TestLocker)\n        poetry.locker.locked(True)\n        return poetry\n\n    return get_poetry\n\n\n@pytest.fixture()\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"remove\")\n\n\ndef test_remove_from_project_and_poetry(\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"bar\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n\n    project_dependencies: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[project]\ndependencies = [\n    \"foo>=2.0\",\n    \"bar>=1.0\",\n]\n\"\"\"\n    )\n\n    poetry_dependencies: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[tool.poetry.dependencies]\nfoo = \"^2.0.0\"\nbar = \"^1.0.0\"\n\n\"\"\"\n    )\n\n    pyproject[\"project\"][\"dependencies\"] = project_dependencies[\"project\"][\n        \"dependencies\"\n    ]\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"] = poetry_dependencies[\"tool\"][\"poetry\"][\n        \"dependencies\"\n    ]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(Factory.create_dependency(\"bar\", \"^1.0.0\"))\n\n    tester.execute(\"foo\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    project_dependencies = pyproject[\"project\"][\"dependencies\"]\n    assert \"foo>=2.0\" not in project_dependencies\n    assert \"bar>=1.0\" in project_dependencies\n    poetry_dependencies = pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n    assert \"foo\" not in poetry_dependencies\n    assert \"bar\" in poetry_dependencies\n\n    expected_project_string = \"\"\"\\\ndependencies = [\n    \"bar>=1.0\",\n]\n\"\"\"\n    expected_poetry_string = \"\"\"\\\n\n[tool.poetry.dependencies]\nbar = \"^1.0.0\"\n\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected_project_string = expected_project_string.replace(\"\\n\", \"\\r\\n\")\n        expected_poetry_string = expected_poetry_string.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected_project_string in string_content\n    assert expected_poetry_string in string_content\n\n\ndef test_remove_from_pep735_group_and_poetry_group(\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"bar\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n\n    pep735_dependencies: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[dependency-groups]\ndev = [\n    \"foo>=2.0\",\n    \"bar>=1.0\",\n]\n\"\"\"\n    )\n\n    poetry_dependencies: dict[str, Any] = tomlkit.parse(\n        \"\"\"\\\n[tool.poetry.group.dev.dependencies]\nfoo = \"^2.0.0\"\nbar = \"^1.0.0\"\n\n\"\"\"\n    )\n\n    pyproject[\"dependency-groups\"] = pep735_dependencies[\"dependency-groups\"]\n    pyproject[\"tool\"][\"poetry\"][\"group\"] = poetry_dependencies[\"tool\"][\"poetry\"][\n        \"group\"\n    ]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"dev\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"bar\", \"^1.0.0\", groups=[\"dev\"])\n    )\n\n    tester.execute(\"foo\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    pep735_dependencies = pyproject[\"dependency-groups\"][\"dev\"]\n    assert \"foo>=2.0\" not in pep735_dependencies\n    assert \"bar>=1.0\" in pep735_dependencies\n    poetry_dependencies = pyproject[\"tool\"][\"poetry\"][\"group\"][\"dev\"][\"dependencies\"]\n    assert \"foo\" not in poetry_dependencies\n    assert \"bar\" in poetry_dependencies\n\n    expected_pep735_string = \"\"\"\\\n[dependency-groups]\ndev = [\n    \"bar>=1.0\",\n]\n\"\"\"\n    expected_poetry_string = \"\"\"\\\n\n[tool.poetry.group.dev.dependencies]\nbar = \"^1.0.0\"\n\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected_pep735_string = expected_pep735_string.replace(\"\\n\", \"\\r\\n\")\n        expected_poetry_string = expected_poetry_string.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected_pep735_string in string_content\n    assert expected_poetry_string in string_content\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\ndef test_remove_without_specific_group_removes_from_all_groups(\n    pep_735: bool,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    Removing without specifying a group removes packages from all groups.\n    \"\"\"\n    installed.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^2.0.0\"\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\nbaz = \"^1.0.0\"\n\n\"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"bar\"])\n    )\n\n    tester.execute(\"foo\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n    assert \"foo\" not in content[\"dependencies\"]\n\n    if pep_735:\n        assert not any(\"foo\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        assert any(\"baz\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        expected = \"\"\"\\\n[dependency-groups]\nbar = [\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n    else:\n        assert \"foo\" not in content[\"group\"][\"bar\"][\"dependencies\"]\n        assert \"baz\" in content[\"group\"][\"bar\"][\"dependencies\"]\n\n        expected = \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nbaz = \"^1.0.0\"\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected = expected.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected in string_content\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\ndef test_remove_with_specific_group_removes_from_specific_groups(\n    pep_735: bool,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    Removing with a specific group given removes packages only from this group.\n    \"\"\"\n    installed.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^2.0.0\"\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n    \"baz (>=1.0,<2.0)\",\n]\n    \"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\nbaz = \"^1.0.0\"\n    \"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"bar\"])\n    )\n\n    tester.execute(\"foo --group bar\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n    assert \"foo\" in content[\"dependencies\"]\n\n    if pep_735:\n        assert not any(\"foo\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        assert any(\"baz\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        expected = \"\"\"\\\n[dependency-groups]\nbar = [\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n    else:\n        assert \"foo\" not in content[\"group\"][\"bar\"][\"dependencies\"]\n        assert \"baz\" in content[\"group\"][\"bar\"][\"dependencies\"]\n\n        expected = \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nbaz = \"^1.0.0\"\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected = expected.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected in string_content\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\ndef test_remove_does_not_keep_empty_groups(\n    pep_735: bool,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    Empty groups are automatically discarded after package removal.\n    \"\"\"\n    installed.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^2.0.0\"\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n    \"baz (>=1.0,<2.0)\",\n]\n    \"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\nbaz = \"^1.0.0\"\n\n\"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"bar\"])\n    )\n\n    tester.execute(\"foo baz --group bar\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"foo\" in content[\"dependencies\"]\n\n    if pep_735:\n        assert \"bar\" not in pyproject.get(\"dependency-groups\", {})\n        assert \"dependency-groups\" not in pyproject\n    else:\n        # The group 'bar' should be removed entirely from the configuration\n        assert \"group\" not in content\n        content = cast(\"TOMLDocument\", content)\n        assert \"[tool.poetry.group.bar]\" not in content.as_string()\n        assert \"[tool.poetry.group]\" not in content.as_string()\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\ndef test_remove_canonicalized_named_removes_dependency_correctly(\n    pep_735: bool,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    Removing a dependency using a canonicalized named removes the dependency.\n    \"\"\"\n    installed.add_package(Package(\"foo-bar\", \"2.0.0\"))\n    repo.add_package(Package(\"foo-bar\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo-bar\"] = \"^2.0.0\"\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo-bar (>=2.0,<3.0)\",\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo-bar = \"^2.0.0\"\nbaz = \"^1.0.0\"\n\n\"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"].value._insert_after(\n            \"dependencies\", \"group\", groups_content[\"tool\"][\"poetry\"][\"group\"]\n        )\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo-bar\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo-bar\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"bar\"])\n    )\n\n    tester.execute(\"Foo_Bar\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    assert \"foo-bar\" not in content[\"dependencies\"]\n\n    if pep_735:\n        assert not any(\"foo\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        assert any(\"baz\" in dep for dep in pyproject[\"dependency-groups\"][\"bar\"])\n        expected = \"\"\"\\\n[dependency-groups]\nbar = [\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n    else:\n        assert \"foo-bar\" not in content[\"group\"][\"bar\"][\"dependencies\"]\n        assert \"baz\" in content[\"group\"][\"bar\"][\"dependencies\"]\n\n        expected = \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nbaz = \"^1.0.0\"\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected = expected.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected in string_content\n\n\ndef test_remove_package_does_not_exist(\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n\n    original_content = app.poetry.file.read().as_string()\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"foo\")\n\n    assert str(e.value) == \"The following packages were not found: foo\"\n    assert app.poetry.file.read().as_string() == original_content\n\n\ndef test_remove_package_no_dependencies(\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    assert \"dependencies\" not in pyproject[\"project\"]\n    del pyproject[\"tool\"][\"poetry\"][\"dependencies\"]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n    app.poetry.package._dependency_groups = {}\n\n    with pytest.raises(ValueError) as e:\n        tester.execute(\"foo\")\n\n    assert str(e.value) == \"The following packages were not found: foo\"\n\n\ndef test_remove_command_should_not_write_changes_upon_installer_errors(\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n\n    command_tester_factory(\"add\").execute(\"foo\")\n\n    mocker.patch(\"poetry.installation.installer.Installer.run\", return_value=1)\n\n    original_content = app.poetry.file.read().as_string()\n\n    tester.execute(\"foo\")\n\n    assert app.poetry.file.read().as_string() == original_content\n\n\n@pytest.mark.parametrize(\n    \"fixture_name\", [\"up_to_date_lock\", \"up_to_date_lock_non_package\"]\n)\ndef test_remove_with_dry_run_keep_files_intact(\n    fixture_name: str,\n    poetry_with_up_to_date_lockfile: Callable[[str], Poetry],\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    poetry = poetry_with_up_to_date_lockfile(fixture_name)\n    tester = command_tester_factory(\"remove\", poetry=poetry)\n\n    original_pyproject_content = poetry.file.read()\n    original_lockfile_content = poetry._locker.lock_data\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    tester.execute(\"docker --dry-run\")\n\n    assert poetry.file.read() == original_pyproject_content\n    assert poetry._locker.lock_data == original_lockfile_content\n\n\n@pytest.mark.parametrize(\n    \"fixture_name\", [\"up_to_date_lock\", \"up_to_date_lock_non_package\"]\n)\ndef test_remove_performs_uninstall_op(\n    fixture_name: str,\n    poetry_with_up_to_date_lockfile: Callable[[str], Poetry],\n    command_tester_factory: CommandTesterFactory,\n    installed: Repository,\n) -> None:\n    installed.add_package(get_package(\"docker\", \"4.3.1\"))\n    poetry = poetry_with_up_to_date_lockfile(fixture_name)\n    tester = command_tester_factory(\"remove\", poetry=poetry)\n\n    tester.execute(\"docker\")\n\n    expected = \"\"\"\\\nUpdating dependencies\nResolving dependencies...\n\nPackage operations: 0 installs, 0 updates, 1 removal\n\n  - Removing docker (4.3.1)\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    \"fixture_name\", [\"up_to_date_lock\", \"up_to_date_lock_non_package\"]\n)\ndef test_remove_with_lock_does_not_perform_uninstall_op(\n    fixture_name: str,\n    poetry_with_up_to_date_lockfile: Callable[[str], Poetry],\n    command_tester_factory: CommandTesterFactory,\n    installed: Repository,\n) -> None:\n    installed.add_package(get_package(\"docker\", \"4.3.1\"))\n    poetry = poetry_with_up_to_date_lockfile(fixture_name)\n    tester = command_tester_factory(\"remove\", poetry=poetry)\n\n    tester.execute(\"docker --lock\")\n\n    expected = \"\"\"\\\nUpdating dependencies\nResolving dependencies...\n\nWriting lock file\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\ndef test_remove_from_nested_pep735_group_and_poetry_group(\n    pep_735: bool,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    Test removing packages from nested dependency groups with `include-group`(pep735) or `include-groups`(poetry).\n    \"\"\"\n    installed.add_package(Package(\"foo\", \"2.0.0\"))\n    installed.add_package(Package(\"baz\", \"1.0.0\"))\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n    pyproject[\"tool\"][\"poetry\"][\"dependencies\"][\"foo\"] = \"^2.0.0\"\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n]\nfoobar = [\n    { include-group = \"bar\" },\n    \"baz (>=1.0,<2.0)\",\n]\n\"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\n\n[tool.poetry.group.foobar]\ninclude-groups = [\n    \"bar\",\n]\n\n[tool.poetry.group.foobar.dependencies]\nbaz = \"^1.0.0\"\n\"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(Factory.create_dependency(\"foo\", \"^2.0.0\"))\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"foobar\"])\n    )\n\n    tester.execute(\"baz\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    if pep_735:\n        assert \"baz\" not in pyproject[\"dependency-groups\"][\"foobar\"]\n        assert any(\n            isinstance(item, dict) and item.get(\"include-group\") == \"bar\"\n            for item in pyproject[\"dependency-groups\"][\"foobar\"]\n        )\n        expected = \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n]\nfoobar = [\n    { include-group = \"bar\" },\n]\n\"\"\"\n    else:\n        # This also checks that the include-groups section is not removed even if its explicit dependencies are empty\n        assert \"dependencies\" not in content[\"group\"][\"foobar\"]\n        expected = \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\n\n[tool.poetry.group.foobar]\ninclude-groups = [\n    \"bar\",\n]\n\"\"\"\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    string_content = pyproject.as_string()\n    if \"\\r\\n\" in string_content:\n        # consistent line endings\n        expected = expected.replace(\"\\n\", \"\\r\\n\")\n\n    assert expected in string_content\n\n\n@pytest.mark.parametrize(\"pep_735\", [True, False])\n@pytest.mark.parametrize(\"args\", [\"\", \" --group bar\"])\ndef test_remove_group_cleans_up_include_group_references(\n    pep_735: bool,\n    args: str,\n    tester: CommandTester,\n    app: PoetryTestApplication,\n    repo: TestRepository,\n    installed: Repository,\n) -> None:\n    \"\"\"\n    When a group is removed, any `include-group` references to it in other\n    groups should also be cleaned up.\n    \"\"\"\n    installed.add_package(Package(\"foo\", \"2.0.0\"))\n    installed.add_package(Package(\"baz\", \"1.0.0\"))\n    repo.add_package(Package(\"foo\", \"2.0.0\"))\n    repo.add_package(Package(\"baz\", \"1.0.0\"))\n\n    pyproject: dict[str, Any] = app.poetry.file.read()\n\n    if pep_735:\n        groups_content: dict[str, Any] = tomlkit.parse(\n            \"\"\"\\\n[dependency-groups]\nbar = [\n    \"foo (>=2.0,<3.0)\",\n]\nfoobar = [\n    { include-group = \"bar\" },\n]\nfoobar2 = [\n    \"baz (>=1.0)\",\n    { include-group = \"bar\" },\n    \"baz (<=3.0)\",\n]\nfoobar3 = [\n    { include-group = \"bar\" },\n    { include-group = \"foobar2\" },\n]\n\"\"\"\n        )\n        pyproject[\"dependency-groups\"] = groups_content[\"dependency-groups\"]\n    else:\n        groups_content = tomlkit.parse(\n            \"\"\"\\\n[tool.poetry.group.bar.dependencies]\nfoo = \"^2.0.0\"\n\n[tool.poetry.group.foobar]\ninclude-groups = [\n    \"bar\",\n]\n\n[tool.poetry.group.foobar2]\ninclude-groups = [\n    \"bar\",\n]\n\n[tool.poetry.group.foobar2.dependencies]\nbaz = \"(>=1.0,<=3.0)\"\n\n[tool.poetry.group.foobar3]\ninclude-groups = [\n    \"bar\",\n    \"foobar2\",\n]\n\"\"\"\n        )\n        groups_content = cast(\"dict[str, Any]\", groups_content)\n        pyproject[\"tool\"][\"poetry\"][\"group\"] = groups_content[\"tool\"][\"poetry\"][\"group\"]\n\n    pyproject = cast(\"TOMLDocument\", pyproject)\n    app.poetry.file.write(pyproject)\n\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"foo\", \"^2.0.0\", groups=[\"bar\"])\n    )\n    app.poetry.package.add_dependency(\n        Factory.create_dependency(\"baz\", \"^1.0.0\", groups=[\"foobar2\"])\n    )\n    foobar = DependencyGroup(\"foobar\")\n    foobar.include_dependency_group(app.poetry.package.dependency_group(\"bar\"))\n    app.poetry.package.add_dependency_group(foobar)\n    foobar2 = DependencyGroup(\"foobar2\")\n    foobar2.include_dependency_group(app.poetry.package.dependency_group(\"bar\"))\n    app.poetry.package.add_dependency_group(foobar2)\n    foobar3 = DependencyGroup(\"foobar3\")\n    foobar3.include_dependency_group(app.poetry.package.dependency_group(\"bar\"))\n    foobar3.include_dependency_group(app.poetry.package.dependency_group(\"foobar2\"))\n    app.poetry.package.add_dependency_group(foobar3)\n\n    # Remove all packages from the \"bar\" group, which should delete the group\n    # and also clean up references to it in \"foobar\"\n    tester.execute(f\"foo{args}\")\n\n    pyproject = app.poetry.file.read()\n    pyproject = cast(\"dict[str, Any]\", pyproject)\n    content = pyproject[\"tool\"][\"poetry\"]\n\n    if pep_735:\n        # \"bar\" group should be removed\n        assert \"bar\" not in pyproject.get(\"dependency-groups\", {})\n        # \"foobar\" group should also be removed since it only had include-group\n        assert \"foobar\" not in pyproject.get(\"dependency-groups\", {})\n        # \"foobar2\" should have its include-group cleaned up\n        assert \"foobar2\" in pyproject.get(\"dependency-groups\", {})\n        assert pyproject[\"dependency-groups\"][\"foobar2\"] == [\n            \"baz (>=1.0)\",\n            \"baz (<=3.0)\",\n        ]\n        # \"foobar3\" should have its include-groups cleaned up\n        assert \"foobar3\" in pyproject.get(\"dependency-groups\", {})\n        assert pyproject[\"dependency-groups\"][\"foobar3\"] == [\n            {\"include-group\": \"foobar2\"}\n        ]\n    else:\n        # \"bar\" group should be removed\n        assert \"bar\" not in content.get(\"group\", {})\n        # \"foobar\" group should also be removed since it only had include-group\n        assert \"foobar\" not in content.get(\"group\", {})\n        # \"foobar2\" should have its include-groups cleaned up\n        assert \"foobar2\" in content.get(\"group\", {})\n        assert \"include-groups\" not in content[\"group\"][\"foobar2\"]\n        assert \"dependencies\" in content[\"group\"][\"foobar2\"]\n        assert content[\"group\"][\"foobar2\"][\"dependencies\"] == {\"baz\": \"(>=1.0,<=3.0)\"}\n        # \"foobar3\" should have its include-groups cleaned up\n        assert \"foobar3\" in content.get(\"group\", {})\n        assert \"include-groups\" in content[\"group\"][\"foobar3\"]\n        assert content[\"group\"][\"foobar3\"][\"include-groups\"] == [\"foobar2\"]\n"
  },
  {
    "path": "tests/console/commands/test_run.py",
    "content": "from __future__ import annotations\n\nimport subprocess\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils._compat import WINDOWS\n\n\nif TYPE_CHECKING:\n    from cleo.testers.application_tester import ApplicationTester\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env import MockEnv\n    from poetry.utils.env import VirtualEnv\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"run\")\n\n\n@pytest.fixture(autouse=True)\ndef patches(mocker: MockerFixture, env: MockEnv) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=env)\n\n\n@pytest.fixture\ndef poetry_with_scripts(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    source = fixture_dir(\"scripts\")\n\n    return project_factory(\n        name=\"scripts\",\n        pyproject_content=(source / \"pyproject.toml\").read_text(encoding=\"utf-8\"),\n        source=source,\n    )\n\n\ndef test_run_passes_all_args(app_tester: ApplicationTester, env: MockEnv) -> None:\n    app_tester.execute(\"run python -V\")\n    assert env.executed == [[\"python\", \"-V\"]]\n\n\ndef test_run_is_not_eager(app_tester: ApplicationTester, env: MockEnv) -> None:\n    app_tester.execute(\"--no-ansi -C run -install\", decorated=True)\n    assert (\n        app_tester.io.fetch_error().strip()\n        == \"Specified path 'run' is not a valid directory.\"\n    )\n    assert env.executed == []\n\n\ndef test_run_passes_args_after_run_before_command(\n    app_tester: ApplicationTester, env: MockEnv\n) -> None:\n    app_tester.execute(\"run -P. python -V\", decorated=True)\n    assert env.executed == [[\"python\", \"-V\"]]\n\n\n@pytest.mark.parametrize(\n    \"args\",\n    [\n        \"-vP run run\",\n        \"run -vP run\",\n        \"-vPrun run\",\n        \"run -vPrun \",\n        \"-v --project=run run\",\n        \"-v run --project=run\",\n        \"-v --directory=run run\",\n        \"run -v --directory=run\",\n    ],\n)\ndef test_run_passes_args_after_run_before_command_name_conflict(\n    args: str,\n    app_tester: ApplicationTester,\n    env: MockEnv,\n    project_factory: ProjectFactory,\n) -> None:\n    poetry = project_factory(\"run\")\n    path = poetry.file.path.parent\n    path.rename(path.parent / \"run\")\n\n    app_tester.execute(f\"{args} python -V\", decorated=True)\n    assert (\n        app_tester.io.remove_format(app_tester.io.fetch_error())\n        == f\"Using virtualenv: {env.path}\\n\"\n    )\n    assert env.executed == [[\"python\", \"-V\"]]\n\n\ndef test_run_keeps_options_passed_before_command_args_combined_short_opts(\n    app_tester: ApplicationTester, env: MockEnv\n) -> None:\n    app_tester.execute(\"run -VP. --no-ansi python\", decorated=True)\n\n    assert not app_tester.io.is_decorated()\n    assert app_tester.io.fetch_output() == app_tester.io.remove_format(\n        app_tester.application.long_version + \"\\n\"\n    )\n    assert env.executed == []\n\n\ndef test_run_keeps_options_passed_before_command_args(\n    app_tester: ApplicationTester, env: MockEnv\n) -> None:\n    app_tester.execute(\"run -V --no-ansi python\", decorated=True)\n\n    assert not app_tester.io.is_decorated()\n    assert app_tester.io.fetch_output() == app_tester.io.remove_format(\n        app_tester.application.long_version + \"\\n\"\n    )\n    assert env.executed == []\n\n\ndef test_run_keeps_options_passed_before_command(\n    app_tester: ApplicationTester, env: MockEnv\n) -> None:\n    app_tester.execute(\"-V --no-ansi run python\", decorated=True)\n\n    assert not app_tester.io.is_decorated()\n    assert app_tester.io.fetch_output() == app_tester.io.remove_format(\n        app_tester.application.long_version + \"\\n\"\n    )\n    assert env.executed == []\n\n\ndef test_run_has_helpful_error_when_command_not_found(\n    app_tester: ApplicationTester, env: MockEnv, capfd: pytest.CaptureFixture[str]\n) -> None:\n    nonexistent_command = \"nonexistent-command\"\n    env._execute = True\n    app_tester.execute(f\"run {nonexistent_command}\")\n\n    assert env.executed == [[nonexistent_command]]\n    assert app_tester.status_code == 1\n    if WINDOWS:\n        # On Windows we use a shell to run commands which provides its own error\n        # message when a command is not found that is not captured by the\n        # ApplicationTester but is captured by pytest, and we can access it via capfd.\n        # The exact error message depends on the system language. Thus, we check only\n        # for the name of the command.\n        assert nonexistent_command in capfd.readouterr().err\n    else:\n        assert (\n            app_tester.io.fetch_error() == f\"Command not found: {nonexistent_command}\\n\"\n        )\n\n\n@pytest.mark.skipif(\n    not WINDOWS,\n    reason=(\n        \"Poetry only installs CMD script files for console scripts of editable\"\n        \" dependencies on Windows\"\n    ),\n)\ndef test_run_console_scripts_of_editable_dependencies_on_windows(\n    tmp_venv: VirtualEnv,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    \"\"\"\n    On Windows, Poetry installs console scripts of editable dependencies by creating\n    in the environment's `Scripts/` directory both:\n\n        A) a Python file named after the console script (no `.py` extension) which\n            imports and calls the console script using Python code\n        B) a CMD script file also named after the console script\n            (with `.cmd` extension) which calls `python.exe` to execute (A)\n\n    This configuration enables calling the console script by name from `cmd.exe`\n    because the `.cmd` file extension appears by default in the PATHEXT environment\n    variable that `cmd.exe` uses to determine which file should be executed if a\n    filename without an extension is executed as a command.\n\n    This test validates that you can also run such a CMD script file via `poetry run`\n    just by providing the script's name without the `.cmd` extension.\n    \"\"\"\n    tester = command_tester_factory(\"run\", environment=tmp_venv)\n\n    cmd_script_file = tmp_venv._bin_dir / \"quix.cmd\"\n    # `/b` ensures we only exit the script instead of any cmd.exe proc that called it\n    cmd_script_file.write_text(\"exit /b 123\", encoding=\"locale\")\n    # We prove that the CMD script executed successfully by verifying the exit code\n    # matches what we wrote in the script\n    assert tester.execute(\"quix\") == 123\n\n\ndef test_run_script_exit_code(\n    poetry_with_scripts: Poetry,\n    command_tester_factory: CommandTesterFactory,\n    tmp_venv: VirtualEnv,\n    mocker: MockerFixture,\n) -> None:\n    mocker.patch(\n        \"os.execvpe\",\n        lambda file, args, env: subprocess.call([file, *args[1:]], env=env),\n    )\n    install_tester = command_tester_factory(\n        \"install\",\n        poetry=poetry_with_scripts,\n        environment=tmp_venv,\n    )\n    assert install_tester.execute() == 0\n    tester = command_tester_factory(\n        \"run\", poetry=poetry_with_scripts, environment=tmp_venv\n    )\n    assert tester.execute(\"exit-code\") == 42\n    assert tester.execute(\"return-code\") == 42\n\n\n@pytest.mark.parametrize(\n    \"installed_script\", [False, True], ids=[\"not installed\", \"installed\"]\n)\ndef test_run_script_sys_argv0(\n    installed_script: bool,\n    poetry_with_scripts: Poetry,\n    command_tester_factory: CommandTesterFactory,\n    tmp_venv: VirtualEnv,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    If RunCommand calls an installed script defined in pyproject.toml,\n    sys.argv[0] must be set to the full path of the script.\n    \"\"\"\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=tmp_venv)\n    mocker.patch(\n        \"os.execvpe\",\n        lambda file, args, env: subprocess.call([file, *args[1:]], env=env),\n    )\n\n    install_tester = command_tester_factory(\n        \"install\",\n        poetry=poetry_with_scripts,\n        environment=tmp_venv,\n    )\n    assert install_tester.execute() == 0\n    if not installed_script:\n        for path in tmp_venv.script_dirs[0].glob(\"check-argv0*\"):\n            path.unlink()\n\n    tester = command_tester_factory(\n        \"run\", poetry=poetry_with_scripts, environment=tmp_venv\n    )\n    argv1 = \"absolute\" if installed_script else \"relative\"\n    assert tester.execute(f\"check-argv0 {argv1}\") == 0\n\n    if installed_script:\n        expected_message = \"\"\n    else:\n        expected_message = \"\"\"\\\nWarning: 'check-argv0' is an entry point defined in pyproject.toml, but it's not \\\ninstalled as a script. You may get improper `sys.argv[0]`.\n\nThe support to run uninstalled scripts will be removed in a future release.\n\nRun `poetry install` to resolve and get rid of this message.\n\n\"\"\"\n    assert tester.io.fetch_error() == expected_message\n"
  },
  {
    "path": "tests/console/commands/test_search.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.repositories.pypi_repository import PyPiRepository\n\n\nif TYPE_CHECKING:\n    import responses\n\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.poetry import Poetry\n    from poetry.repositories.legacy_repository import LegacyRepository\n    from tests.types import CommandTesterFactory\n\n\nSQLALCHEMY_SEARCH_OUTPUT_PYPI = \"\"\"\\\n Package                  Version Source Description\n broadway-sqlalchemy      0.0.1   PyPI   A broadway extension wrapping Flask-SQLAlchemy\n cherrypy-sqlalchemy      0.5.3   PyPI   Use SQLAlchemy with CherryPy\n graphene-sqlalchemy      2.2.2   PyPI   Graphene SQLAlchemy integration\n jsonql-sqlalchemy        1.0.1   PyPI   Simple JSON-Based CRUD Query Language for SQLAlchemy\n paginate-sqlalchemy      0.3.0   PyPI   Extension to paginate.Page that supports SQLAlchemy queries\n sqlalchemy               1.3.10  PyPI   Database Abstraction Library\n sqlalchemy-audit         0.1.0   PyPI   sqlalchemy-audit provides an easy way to set up revision tracking for your data.\n sqlalchemy-dao           1.3.1   PyPI   Simple wrapper for sqlalchemy.\n sqlalchemy-diff          0.1.3   PyPI   Compare two database schemas using sqlalchemy.\n sqlalchemy-equivalence   0.1.1   PyPI   Provides natural equivalence support for SQLAlchemy declarative models.\n sqlalchemy-filters       0.10.0  PyPI   A library to filter SQLAlchemy queries.\n sqlalchemy-nav           0.0.2   PyPI   SQLAlchemy-Nav provides SQLAlchemy Mixins for creating navigation bars compatible with Bootstrap\n sqlalchemy-plus          0.2.0   PyPI   Create Views and Materialized Views with SqlAlchemy\n sqlalchemy-repr          0.0.1   PyPI   Automatically generates pretty repr of a SQLAlchemy model.\n sqlalchemy-schemadisplay 1.3     PyPI   Turn SQLAlchemy DB Model into a graph\n sqlalchemy-sqlany        1.0.3   PyPI   SAP Sybase SQL Anywhere dialect for SQLAlchemy\n sqlalchemy-traversal     0.5.2   PyPI   UNKNOWN\n sqlalchemy-utcdatetime   1.0.4   PyPI   Convert to/from timezone aware datetimes when storing in a DBMS\n sqlalchemy-wrap          2.1.7   PyPI   Python wrapper for the CircleCI API\n transmogrify-sqlalchemy  1.0.2   PyPI   Feed data from SQLAlchemy into a transmogrifier pipeline\n\"\"\"\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"search\")\n\n\ndef clean_output(text: str) -> str:\n    return re.sub(r\"\\s+\\n\", \"\\n\", text)\n\n\ndef test_search(\n    tester: CommandTester, http: responses.RequestsMock, poetry: Poetry\n) -> None:\n    # we expect PyPI in the default behaviour\n    poetry.pool.add_repository(PyPiRepository())\n\n    tester.execute(\"sqlalchemy\")\n\n    output = clean_output(tester.io.fetch_output())\n\n    assert output == SQLALCHEMY_SEARCH_OUTPUT_PYPI\n\n\ndef test_search_empty_results(\n    tester: CommandTester,\n    http: responses.RequestsMock,\n    poetry: Poetry,\n    legacy_repository: LegacyRepository,\n) -> None:\n    poetry.pool.add_repository(legacy_repository)\n\n    tester.execute(\"does-not-exist\")\n\n    output = tester.io.fetch_output()\n    assert output.strip() == \"No matching packages were found.\"\n\n\ndef test_search_with_legacy_repository(\n    tester: CommandTester,\n    http: responses.RequestsMock,\n    poetry: Poetry,\n    legacy_repository: LegacyRepository,\n) -> None:\n    poetry.pool.add_repository(PyPiRepository())\n    poetry.pool.add_repository(legacy_repository)\n\n    tester.execute(\"sqlalchemy\")\n\n    line_before = \" sqlalchemy-filters       0.10.0  PyPI   A library to filter SQLAlchemy queries.\"\n    additional_line = \" sqlalchemy-legacy        4.3.4   legacy\"\n    expected = SQLALCHEMY_SEARCH_OUTPUT_PYPI.replace(\n        line_before, f\"{line_before}\\n{additional_line}\"\n    )\n\n    output = clean_output(tester.io.fetch_output())\n\n    assert output == expected\n\n\ndef test_search_only_legacy_repository(\n    tester: CommandTester,\n    http: responses.RequestsMock,\n    poetry: Poetry,\n    legacy_repository: LegacyRepository,\n) -> None:\n    poetry.pool.add_repository(legacy_repository)\n\n    tester.execute(\"ipython\")\n\n    expected = \"\"\"\\\n Package Version  Source Description\n ipython 4.1.0rc1 legacy\n ipython 5.7.0    legacy\n ipython 7.5.0    legacy\n\"\"\"\n\n    output = clean_output(tester.io.fetch_output())\n    assert output == expected\n\n\ndef test_search_multiple_queries(\n    tester: CommandTester,\n    http: responses.RequestsMock,\n    poetry: Poetry,\n    legacy_repository: LegacyRepository,\n) -> None:\n    poetry.pool.add_repository(legacy_repository)\n\n    tester.execute(\"ipython isort\")\n\n    expected = \"\"\"\\\n Package        Version  Source Description\n ipython        4.1.0rc1 legacy\n ipython        5.7.0    legacy\n ipython        7.5.0    legacy\n isort          4.3.4    legacy\n isort-metadata 4.3.4    legacy\n\"\"\"\n\n    output = clean_output(tester.io.fetch_output())\n\n    # we use a set here to avoid ordering issues\n    assert set(output.split(\"\\n\")) == set(expected.split(\"\\n\"))\n"
  },
  {
    "path": "tests/console/commands/test_show.py",
    "content": "from __future__ import annotations\n\nimport json\n\nfrom collections.abc import Callable\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import TypeVar\nfrom typing import cast\n\nimport pytest\n\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.dependency_group import DependencyGroup\n\nfrom poetry.factory import Factory\nfrom poetry.utils._compat import tomllib\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import TestLocker\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.poetry import Poetry\n    from poetry.repositories import Repository\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"show\")\n\n\nF = TypeVar(\"F\", bound=Callable[..., Any])\n\n\ndef output_format_parametrize(func: F) -> F:\n    formats = [\"\", \"--format json\"]\n    return cast(\"F\", pytest.mark.parametrize(\"output_format\", formats)(func))\n\n\n@output_format_parametrize\ndef test_show_basic_with_installed_packages(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"pytest\", \"^3.7.3\", groups=[\"dev\"])\n    )\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    pytest_373 = get_package(\"pytest\", \"3.7.3\")\n    pytest_373.description = \"Pytest package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n    installed.add_package(pytest_373)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": [], \"pytest\": []},\n            },\n        }\n    )\n\n    tester.execute(output_format)\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"pytest\",\n                \"version\": \"3.7.3\",\n                \"description\": \"Pytest package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy    0.1.0 Cachy package\npendulum 2.0.0 Pendulum package\npytest   3.7.3 Pytest package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\ndef _configure_project_with_groups(poetry: Poetry, installed: Repository) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n\n    poetry.package.add_dependency_group(DependencyGroup(name=\"time\", optional=True))\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"pendulum\", \"^2.0.0\", groups=[\"time\"])\n    )\n\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"pytest\", \"^3.7.3\", groups=[\"test\"])\n    )\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    pytest_373 = get_package(\"pytest\", \"3.7.3\")\n    pytest_373.description = \"Pytest package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n    installed.add_package(pytest_373)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": [], \"pytest\": []},\n            },\n        }\n    )\n\n\n@pytest.mark.parametrize(\n    (\"options\", \"expected\"),\n    [\n        (\n            \"\",\n            \"\"\"\\\ncachy  0.1.0 Cachy package\npytest 3.7.3 Pytest package\n\"\"\",\n        ),\n        (\n            \"--format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--with time\",\n            \"\"\"\\\ncachy    0.1.0 Cachy package\npendulum 2.0.0 Pendulum package\npytest   3.7.3 Pytest package\n\"\"\",\n        ),\n        (\n            \"--with time --format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--without test\",\n            \"\"\"\\\ncachy 0.1.0 Cachy package\n\"\"\",\n        ),\n        (\n            \"--without test --format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            f\"--without {MAIN_GROUP}\",\n            \"\"\"\\\npytest 3.7.3 Pytest package\n\"\"\",\n        ),\n        (\n            f\"--without {MAIN_GROUP} --format json\",\n            [\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            f\"--only {MAIN_GROUP}\",\n            \"\"\"\\\ncachy 0.1.0 Cachy package\n\"\"\",\n        ),\n        (\n            f\"--only {MAIN_GROUP} --format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--with time --without test\",\n            \"\"\"\\\ncachy    0.1.0 Cachy package\npendulum 2.0.0 Pendulum package\n\"\"\",\n        ),\n        (\n            \"--with time --without test --format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            f\"--with time --without {MAIN_GROUP},test\",\n            \"\"\"\\\npendulum 2.0.0 Pendulum package\n\"\"\",\n        ),\n        (\n            f\"--with time --without {MAIN_GROUP},test --format json\",\n            [\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--only time\",\n            \"\"\"\\\npendulum 2.0.0 Pendulum package\n\"\"\",\n        ),\n        (\n            \"--only time --format json\",\n            [\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--only time --with test\",\n            \"\"\"\\\npendulum 2.0.0 Pendulum package\n\"\"\",\n        ),\n        (\n            \"--only time --with test --format json\",\n            [\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n        (\n            \"--with time\",\n            \"\"\"\\\ncachy    0.1.0 Cachy package\npendulum 2.0.0 Pendulum package\npytest   3.7.3 Pytest package\n\"\"\",\n        ),\n        (\n            \"--with time --format json\",\n            [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"installed_status\": \"installed\",\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.7.3\",\n                    \"description\": \"Pytest package\",\n                    \"installed_status\": \"installed\",\n                },\n            ],\n        ),\n    ],\n)\ndef test_show_basic_with_group_options(\n    options: str,\n    expected: str | list[dict[str, str]],\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    _configure_project_with_groups(poetry, installed)\n\n    tester.execute(options)\n\n    if \"json\" in options:\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_basic_with_installed_packages_single(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    installed.add_package(cachy_010)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"cachy {output_format}\")\n\n    expected: dict[str, str] | list[str] = {}\n    if \"json\" in output_format:\n        expected = {\"name\": \"cachy\", \"version\": \"0.1.0\", \"description\": \"Cachy package\"}\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = [\n            \"name         : cachy\",\n            \"version      : 0.1.0\",\n            \"description  : Cachy package\",\n        ]\n        assert [\n            line.strip() for line in tester.io.fetch_output().splitlines()\n        ] == expected\n\n\n@output_format_parametrize\ndef test_show_basic_with_installed_packages_single_canonicalized(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"foo-bar\", \"^0.1.0\"))\n\n    foo_bar = get_package(\"foo-bar\", \"0.1.0\")\n    foo_bar.description = \"Foobar package\"\n\n    installed.add_package(foo_bar)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"foo-bar\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Foobar package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"foo-bar\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"Foo_Bar {output_format}\")\n\n    expected: dict[str, str] | list[str] = {}\n    if \"json\" in output_format:\n        expected = {\n            \"name\": \"foo-bar\",\n            \"version\": \"0.1.0\",\n            \"description\": \"Foobar package\",\n        }\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = [\n            \"name         : foo-bar\",\n            \"version      : 0.1.0\",\n            \"description  : Foobar package\",\n        ]\n        assert [\n            line.strip() for line in tester.io.fetch_output().splitlines()\n        ] == expected\n\n\n@output_format_parametrize\ndef test_show_basic_with_not_installed_packages_non_decorated(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(output_format)\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"not-installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy        0.1.0 Cachy package\npendulum (!) 2.0.0 Pendulum package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\ndef test_show_basic_with_not_installed_packages_decorated(\n    tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(decorated=True)\n\n    expected = \"\"\"\\\n\\033[36mcachy   \\033[39m \\033[39;1m0.1.0\\033[39;22m Cachy package\n\\033[31mpendulum\\033[39m \\033[39;1m2.0.0\\033[39;22m Pendulum package\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_latest_non_decorated(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n    pendulum_201 = get_package(\"pendulum\", \"2.0.1\")\n    pendulum_201.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n    repo.add_package(pendulum_201)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--latest {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"latest_version\": \"2.0.1\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy    0.1.0 0.2.0 Cachy package\npendulum 2.0.0 2.0.1 Pendulum package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\ndef test_show_latest_decorated(\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n    pendulum_201 = get_package(\"pendulum\", \"2.0.1\")\n    pendulum_201.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n    repo.add_package(pendulum_201)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(\"--latest\", decorated=True)\n\n    expected = \"\"\"\\\n\\033[36mcachy   \\033[39m \\033[39;1m0.1.0\\033[39;22m\\\n \\033[33m0.2.0\\033[39m Cachy package\n\\033[36mpendulum\\033[39m \\033[39;1m2.0.0\\033[39;22m\\\n \\033[31m2.0.1\\033[39m Pendulum package\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_outdated(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy 0.1.0 0.2.0 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_outdated_with_only_up_to_date_packages(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    installed.add_package(cachy_020)\n    repo.add_package(cachy_020)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = []\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_outdated_has_prerelease_but_not_allowed(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n    cachy_030dev = get_package(\"cachy\", \"0.3.0.dev123\")\n    cachy_030dev.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n\n    # sorting isn't used, so this has to be the first element to\n    # replicate the issue in PR #1548\n    repo.add_package(cachy_030dev)\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy 0.1.0 0.2.0 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_outdated_has_prerelease_and_allowed(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(\n        Factory.create_dependency(\n            \"cachy\", {\"version\": \">=0.0.1\", \"allow-prereleases\": True}\n        )\n    )\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010dev = get_package(\"cachy\", \"0.1.0.dev1\")\n    cachy_010dev.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n    cachy_030dev = get_package(\"cachy\", \"0.3.0.dev123\")\n    cachy_030dev.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010dev)\n    installed.add_package(pendulum_200)\n\n    # sorting isn't used, so this has to be the first element to\n    # replicate the issue in PR #1548\n    repo.add_package(cachy_030dev)\n    repo.add_package(cachy_010dev)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0.dev1\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0.dev1\",\n                \"latest_version\": \"0.3.0.dev123\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy 0.1.0.dev1 0.3.0.dev123 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_outdated_formatting(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n    pendulum_201 = get_package(\"pendulum\", \"2.0.1\")\n    pendulum_201.description = \"Pendulum package\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n    repo.add_package(pendulum_201)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"latest_version\": \"2.0.1\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy    0.1.0 0.2.0 Cachy package\npendulum 2.0.0 2.0.1 Pendulum package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\n    (\"project_directory\", \"required_fixtures\"),\n    [\n        (\n            \"project_with_local_dependencies\",\n            [\"distributions/demo-0.1.0-py2.py3-none-any.whl\", \"project_with_setup\"],\n        ),\n    ],\n)\n@output_format_parametrize\ndef test_show_outdated_local_dependencies(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n    cachy_030 = get_package(\"cachy\", \"0.3.0\")\n    cachy_030.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    demo_010 = get_package(\"demo\", \"0.1.0\")\n    demo_010.description = \"\"\n\n    my_package_011 = get_package(\"project-with-setup\", \"0.1.1\")\n    my_package_011.description = \"Demo project.\"\n\n    installed.add_package(cachy_020)\n    installed.add_package(pendulum_200)\n    installed.add_package(demo_010)\n    installed.add_package(my_package_011)\n\n    repo.add_package(cachy_020)\n    repo.add_package(cachy_030)\n    repo.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"demo\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Demo package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"source\": {\n                        \"type\": \"file\",\n                        \"reference\": \"\",\n                        \"url\": \"../distributions/demo-0.1.0-py2.py3-none-any.whl\",\n                    },\n                },\n                {\n                    \"name\": \"project-with-setup\",\n                    \"version\": \"0.1.1\",\n                    \"description\": \"Demo project.\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\n                        \"pendulum\": \">=1.4.4\",\n                        \"cachy\": {\"version\": \">=0.2.0\", \"extras\": [\"msgpack\"]},\n                    },\n                    \"source\": {\n                        \"type\": \"directory\",\n                        \"reference\": \"\",\n                        \"url\": \"../project_with_setup\",\n                    },\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\n                    \"cachy\": [],\n                    \"pendulum\": [],\n                    \"demo\": [],\n                    \"project-with-setup\": [],\n                },\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.2.0\",\n                \"latest_version\": \"0.3.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"project-with-setup\",\n                \"version\": \"0.1.1 ../project_with_setup\",\n                \"latest_version\": \"0.1.2 ../project_with_setup\",\n                \"description\": \"Demo project.\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy              0.2.0                       0.3.0\nproject-with-setup 0.1.1 ../project_with_setup 0.1.2 ../project_with_setup\n\"\"\"\n        assert (\n            \"\\n\".join(line.rstrip() for line in tester.io.fetch_output().splitlines())\n            == expected.rstrip()\n        )\n\n\n@pytest.mark.parametrize(\"project_directory\", [\"project_with_git_dev_dependency\"])\n@output_format_parametrize\ndef test_show_outdated_git_dev_dependency(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    demo_011 = get_package(\"demo\", \"0.1.1\")\n    demo_011.description = \"Demo package\"\n\n    pytest = get_package(\"pytest\", \"3.4.3\")\n    pytest.description = \"Pytest\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n    installed.add_package(demo_011)\n    installed.add_package(pytest)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n    repo.add_package(pytest)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"demo\",\n                    \"version\": \"0.1.1\",\n                    \"description\": \"Demo package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"source\": {\n                        \"type\": \"git\",\n                        \"reference\": MOCK_DEFAULT_GIT_REVISION,\n                        \"resolved_reference\": MOCK_DEFAULT_GIT_REVISION,\n                        \"url\": \"https://github.com/demo/demo.git\",\n                    },\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.4.3\",\n                    \"description\": \"Pytest\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": [], \"demo\": [], \"pytest\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"demo\",\n                \"version\": \"0.1.1 9cf87a2\",\n                \"latest_version\": \"0.1.2 9cf87a2\",\n                \"description\": \"Demo package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy 0.1.0         0.2.0         Cachy package\ndemo  0.1.1 9cf87a2 0.1.2 9cf87a2 Demo package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@pytest.mark.parametrize(\"project_directory\", [\"project_with_git_dev_dependency\"])\n@output_format_parametrize\ndef test_show_outdated_no_dev_git_dev_dependency(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n    cachy_020 = get_package(\"cachy\", \"0.2.0\")\n    cachy_020.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    demo_011 = get_package(\"demo\", \"0.1.1\")\n    demo_011.description = \"Demo package\"\n\n    pytest = get_package(\"pytest\", \"3.4.3\")\n    pytest.description = \"Pytest\"\n\n    installed.add_package(cachy_010)\n    installed.add_package(pendulum_200)\n    installed.add_package(demo_011)\n    installed.add_package(pytest)\n\n    repo.add_package(cachy_010)\n    repo.add_package(cachy_020)\n    repo.add_package(pendulum_200)\n    repo.add_package(pytest)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"demo\",\n                    \"version\": \"0.1.1\",\n                    \"description\": \"Demo package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"source\": {\n                        \"type\": \"git\",\n                        \"reference\": MOCK_DEFAULT_GIT_REVISION,\n                        \"url\": \"https://github.com/demo/pyproject-demo.git\",\n                    },\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"3.4.3\",\n                    \"description\": \"Pytest\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": [], \"demo\": [], \"pytest\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--outdated --without dev {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"latest_version\": \"0.2.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy 0.1.0 0.2.0 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_hides_incompatible_package(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"cachy\", {\"version\": \"^0.1.0\", \"python\": \"< 2.0\"})\n    )\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"^2.0.0\"))\n\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(output_format)\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\npendulum 2.0.0 Pendulum package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_all_shows_incompatible_package(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    cachy_010 = get_package(\"cachy\", \"0.1.0\")\n    cachy_010.description = \"Cachy package\"\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n\n    installed.add_package(pendulum_200)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"requirements\": {\"python\": \"1.0\"},\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--all {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"not-installed\",\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"2.0.0\",\n                \"description\": \"Pendulum package\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy     0.1.0 Cachy package\npendulum  2.0.0 Pendulum package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_hides_incompatible_package_with_duplicate(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"cachy\", {\"version\": \"0.1.0\", \"platform\": \"linux\"})\n    )\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"cachy\", {\"version\": \"0.1.1\", \"platform\": \"darwin\"})\n    )\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"files\": [],\n                },\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.1\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"files\": [],\n                },\n            ],\n            \"metadata\": {\"content-hash\": \"123456789\"},\n        }\n    )\n\n    tester.execute(output_format)\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.1\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"not-installed\",\n            }\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy (!) 0.1.1 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_all_shows_all_duplicates(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"cachy\", {\"version\": \"0.1.0\", \"platform\": \"linux\"})\n    )\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"cachy\", {\"version\": \"0.1.1\", \"platform\": \"darwin\"})\n    )\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"files\": [],\n                },\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.1.1\",\n                    \"description\": \"Cachy package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"files\": [],\n                },\n            ],\n            \"metadata\": {\"content-hash\": \"123456789\"},\n        }\n    )\n\n    tester.execute(f\"--all {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.0\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"not-installed\",\n            },\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.1.1\",\n                \"description\": \"Cachy package\",\n                \"installed_status\": \"not-installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\ncachy     0.1.0 Cachy package\ncachy (!) 0.1.1 Cachy package\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\ndef test_show_tree(\n    tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.2.0\"))\n\n    cachy2 = get_package(\"cachy\", \"0.2.0\")\n    cachy2.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n\n    installed.add_package(cachy2)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n                },\n                {\n                    \"name\": \"msgpack-python\",\n                    \"version\": \"0.5.1\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"msgpack-python\": []},\n            },\n        }\n    )\n\n    tester.execute(\"--tree\", supports_utf8=False)\n\n    expected = \"\"\"\\\ncachy 0.2.0\n`-- msgpack-python >=0.5 <0.6\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_show_tree_no_dev(\n    tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.2.0\"))\n    poetry.package.add_dependency(\n        Factory.create_dependency(\"pytest\", \"^6.1.0\", groups=[\"dev\"])\n    )\n\n    cachy2 = get_package(\"cachy\", \"0.2.0\")\n    cachy2.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n    installed.add_package(cachy2)\n\n    pytest = get_package(\"pytest\", \"6.1.1\")\n    installed.add_package(pytest)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n                },\n                {\n                    \"name\": \"msgpack-python\",\n                    \"version\": \"0.5.1\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n                {\n                    \"name\": \"pytest\",\n                    \"version\": \"6.1.1\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"msgpack-python\": [], \"pytest\": []},\n            },\n        }\n    )\n\n    tester.execute(\"--tree --without dev\")\n\n    expected = \"\"\"\\\ncachy 0.2.0\n└── msgpack-python >=0.5 <0.6\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_show_tree_why_package(\n    tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"a\", \"=0.0.1\"))\n\n    a = get_package(\"a\", \"0.0.1\")\n    installed.add_package(a)\n    a.add_dependency(Factory.create_dependency(\"b\", \"=0.0.1\"))\n\n    b = get_package(\"b\", \"0.0.1\")\n    a.add_dependency(Factory.create_dependency(\"c\", \"=0.0.1\"))\n    installed.add_package(b)\n\n    c = get_package(\"c\", \"0.0.1\")\n    installed.add_package(c)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"a\",\n                    \"version\": \"0.0.1\",\n                    \"dependencies\": {\"b\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"b\",\n                    \"version\": \"0.0.1\",\n                    \"dependencies\": {\"c\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"c\",\n                    \"version\": \"0.0.1\",\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"a\": [], \"b\": [], \"c\": []},\n            },\n        }\n    )\n\n    tester.execute(\"--tree --why b\")\n\n    expected = \"\"\"\\\na 0.0.1\n└── b =0.0.1\n    └── c =0.0.1 \\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\ndef test_show_tree_why(\n    tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"a\", \"=0.0.1\"))\n\n    a = get_package(\"a\", \"0.0.1\")\n    installed.add_package(a)\n    a.add_dependency(Factory.create_dependency(\"b\", \"=0.0.1\"))\n\n    b = get_package(\"b\", \"0.0.1\")\n    b.add_dependency(Factory.create_dependency(\"c\", \"=0.0.1\"))\n    installed.add_package(b)\n\n    c = get_package(\"c\", \"0.0.1\")\n    installed.add_package(c)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"a\",\n                    \"version\": \"0.0.1\",\n                    \"dependencies\": {\"b\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"b\",\n                    \"version\": \"0.0.1\",\n                    \"dependencies\": {\"c\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"c\",\n                    \"version\": \"0.0.1\",\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"a\": [], \"b\": [], \"c\": []},\n            },\n        }\n    )\n\n    tester.execute(\"--why\")\n\n    # this has to be on a single line due to the padding whitespace, which gets stripped\n    # by pre-commit.\n    expected = \"\"\"a 0.0.1        \\nb 0.0.1 from a \\nc 0.0.1 from b \\n\"\"\"\n\n    assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_why(\n    output_format: str, tester: CommandTester, poetry: Poetry, installed: Repository\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"a\", \"=0.0.1\"))\n\n    a = get_package(\"a\", \"0.0.1\")\n    a.description = \"Package A\"\n    a.add_dependency(Factory.create_dependency(\"b\", \"=0.0.1\"))\n    a.add_dependency(Factory.create_dependency(\"c\", \"=0.0.1\"))\n    installed.add_package(a)\n\n    b = get_package(\"b\", \"0.0.1\")\n    b.description = \"Package B\"\n    b.add_dependency(Factory.create_dependency(\"c\", \"=0.0.1\"))\n    installed.add_package(b)\n\n    c = get_package(\"c\", \"0.0.1\")\n    c.description = \"Package C\"\n    installed.add_package(c)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"a\",\n                    \"version\": \"0.0.1\",\n                    \"description\": \"Package A\",\n                    \"dependencies\": {\"b\": \"=0.0.1\", \"c\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"b\",\n                    \"version\": \"0.0.1\",\n                    \"description\": \"Package B\",\n                    \"dependencies\": {\"c\": \"=0.0.1\"},\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n                {\n                    \"name\": \"c\",\n                    \"version\": \"0.0.1\",\n                    \"description\": \"Package C\",\n                    \"python-versions\": \"*\",\n                    \"optional\": False,\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"a\": [], \"b\": [], \"c\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--why {output_format}\")\n\n    expected: str | list[dict[str, str | list[str]]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"a\",\n                \"version\": \"0.0.1\",\n                \"description\": \"Package A\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"b\",\n                \"version\": \"0.0.1\",\n                \"description\": \"Package B\",\n                \"installed_status\": \"installed\",\n                \"required_by\": [\"a\"],\n            },\n            {\n                \"name\": \"c\",\n                \"version\": \"0.0.1\",\n                \"description\": \"Package C\",\n                \"installed_status\": \"installed\",\n                \"required_by\": [\"a\", \"b\"],\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\na 0.0.1          Package A\nb 0.0.1 from a   Package B\nc 0.0.1 from a,b Package C\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_required_by_deps(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.2.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"pendulum\", \"2.0.0\"))\n\n    cachy2 = get_package(\"cachy\", \"0.2.0\")\n    cachy2.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n\n    pendulum = get_package(\"pendulum\", \"2.0.0\")\n    pendulum.add_dependency(Factory.create_dependency(\"CachY\", \"^0.2.0\"))\n\n    installed.add_package(cachy2)\n    installed.add_package(pendulum)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"cachy\": \">=0.2.0 <0.3.0\"},\n                },\n                {\n                    \"name\": \"msgpack-python\",\n                    \"version\": \"0.5.1\",\n                    \"description\": \"\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"pendulum\": [], \"msgpack-python\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"cachy {output_format}\")\n\n    expected: str | dict[str, str | dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = {\n            \"name\": \"cachy\",\n            \"version\": \"0.2.0\",\n            \"description\": \"\",\n            \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n            \"required_by\": {\"pendulum\": \">=0.2.0 <0.3.0\"},\n        }\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"\\\n name         : cachy\n version      : 0.2.0\n description  :\n\ndependencies\n - msgpack-python >=0.5 <0.6\n\nrequired by\n - pendulum requires >=0.2.0 <0.3.0\n\"\"\"\n        actual = [line.rstrip() for line in tester.io.fetch_output().splitlines()]\n        assert actual == expected.splitlines()\n\n\n@pytest.mark.parametrize(\"truncate\", [False, True])\ndef test_show_entire_description_truncate(\n    tester: CommandTester, poetry: Poetry, installed: Repository, truncate: str\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.2.0\"))\n\n    cachy2 = get_package(\"cachy\", \"0.2.0\")\n    cachy2.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n\n    installed.add_package(cachy2)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"This is a veeeeeeeery long description that might be truncated.\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n                },\n                {\n                    \"name\": \"msgpack-python\",\n                    \"version\": \"0.5.1\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"msgpack-python\": []},\n            },\n        }\n    )\n\n    tester.execute(\"\" if truncate else \"--no-truncate\")\n\n    if truncate:\n        expected = \"\"\"\\\ncachy              0.2.0 This is a veeeeeeeery long description that might ...\nmsgpack-python (!) 0.5.1\"\"\"\n    else:\n        expected = \"\"\"\\\ncachy              0.2.0 This is a veeeeeeeery long description that might be truncated.\nmsgpack-python (!) 0.5.1\"\"\"\n\n    assert tester.io.fetch_output().strip() == expected\n\n\ndef test_show_errors_without_lock_file(tester: CommandTester, poetry: Poetry) -> None:\n    assert not poetry.locker.lock.exists()\n\n    tester.execute()\n\n    expected = \"Error: poetry.lock not found. Run `poetry lock` to create it.\\n\"\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == 1\n\n\n@output_format_parametrize\ndef test_show_dependency_installed_from_git_in_dev(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    # Add a regular dependency for a package in main, and a git dependency for the same\n    # package in dev.\n    poetry.package.add_dependency(Factory.create_dependency(\"demo\", \"^0.1.1\"))\n    poetry.package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"git\": \"https://github.com/demo/demo.git\"}, groups=[\"dev\"]\n        )\n    )\n\n    demo_011 = get_package(\"demo\", \"0.1.1\")\n    demo_011.description = \"Demo package\"\n    repo.add_package(demo_011)\n\n    pendulum_200 = get_package(\"pendulum\", \"2.0.0\")\n    pendulum_200.description = \"Pendulum package\"\n    repo.add_package(pendulum_200)\n\n    # The git package is the one that gets into the lockfile.\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"demo\",\n                    \"version\": \"0.1.2\",\n                    \"description\": \"Demo package\",\n                    \"optional\": False,\n                    \"python-versions\": \"*\",\n                    \"develop\": False,\n                    \"source\": {\n                        \"type\": \"git\",\n                        \"reference\": MOCK_DEFAULT_GIT_REVISION,\n                        \"resolved_reference\": MOCK_DEFAULT_GIT_REVISION,\n                        \"url\": \"https://github.com/demo/demo.git\",\n                    },\n                },\n                {\n                    \"name\": \"pendulum\",\n                    \"version\": \"2.0.0\",\n                    \"description\": \"Pendulum package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"demo\": [], \"pendulum\": []},\n            },\n        }\n    )\n\n    # Nothing needs updating, there is no confusion between the git and not-git\n    # packages.\n    tester.execute(f\"--outdated {output_format}\")\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = []\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_url_dependency_is_not_outdated_by_repository_package(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    demo_url = (\n        \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    )\n    poetry.package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\"url\": demo_url},\n        )\n    )\n\n    # A newer version of demo is available in the repository.\n    demo_100 = get_package(\"demo\", \"1.0.0\")\n    repo.add_package(demo_100)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"demo\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"Demo package\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"source\": {\n                        \"type\": \"url\",\n                        \"url\": demo_url,\n                    },\n                }\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"hashes\": {\"demo\": []},\n            },\n        }\n    )\n\n    # The url dependency on demo is not made outdated by the existence of a newer\n    # version in the repository.\n    tester.execute(f\"--outdated {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = []\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_top_level(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"cachy\", \"^0.2.0\"))\n\n    cachy2 = get_package(\"cachy\", \"0.2.0\")\n    cachy2.add_dependency(Factory.create_dependency(\"msgpack-python\", \">=0.5 <0.6\"))\n\n    installed.add_package(cachy2)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"cachy\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"msgpack-python\": \">=0.5 <0.6\"},\n                },\n                {\n                    \"name\": \"msgpack-python\",\n                    \"version\": \"0.5.1\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"cachy\": [], \"msgpack-python\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--top-level {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"cachy\",\n                \"version\": \"0.2.0\",\n                \"description\": \"\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"cachy              0.2.0 \\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_top_level_with_explicitly_defined_dependency(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    poetry.package.add_dependency(Factory.create_dependency(\"a\", \"^0.1.0\"))\n    poetry.package.add_dependency(Factory.create_dependency(\"b\", \"^0.2.0\"))\n\n    a = get_package(\"a\", \"0.1.0\")\n    a.add_dependency(Factory.create_dependency(\"b\", \"0.2.0\"))\n    b = get_package(\"b\", \"0.2.0\")\n\n    installed.add_package(a)\n    installed.add_package(b)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"a\",\n                    \"version\": \"0.1.0\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\"b\": \"0.2.0\"},\n                },\n                {\n                    \"name\": \"b\",\n                    \"version\": \"0.2.0\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"a\": [], \"b\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--top-level {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"a\",\n                \"version\": \"0.1.0\",\n                \"description\": \"\",\n                \"installed_status\": \"installed\",\n            },\n            {\n                \"name\": \"b\",\n                \"version\": \"0.2.0\",\n                \"description\": \"\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"a 0.1.0 \\nb 0.2.0 \\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\n@output_format_parametrize\ndef test_show_top_level_with_extras(\n    output_format: str,\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n) -> None:\n    black_dep = Factory.create_dependency(\n        \"black\", {\"version\": \"23.3.0\", \"extras\": [\"d\"]}\n    )\n    poetry.package.add_dependency(black_dep)\n\n    black_package = get_package(\"black\", \"23.3.0\")\n    black_package.add_dependency(\n        Factory.create_dependency(\n            \"aiohttp\",\n            {\n                \"version\": \">=3.7.4\",\n                \"optional\": True,\n                \"markers\": 'extra == \"d\"',\n            },\n        )\n    )\n    installed.add_package(black_package)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(\n        {\n            \"package\": [\n                {\n                    \"name\": \"black\",\n                    \"version\": \"23.3.0\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                    \"dependencies\": {\n                        \"aiohttp\": {\n                            \"version\": \">=3.7.4\",\n                            \"optional\": True,\n                            \"markers\": 'extra == \"d\"',\n                        }\n                    },\n                },\n                {\n                    \"name\": \"aiohttp\",\n                    \"version\": \"3.8.4\",\n                    \"description\": \"\",\n                    \"category\": \"main\",\n                    \"optional\": False,\n                    \"platform\": \"*\",\n                    \"python-versions\": \"*\",\n                    \"checksum\": [],\n                },\n            ],\n            \"metadata\": {\n                \"python-versions\": \"*\",\n                \"platform\": \"*\",\n                \"content-hash\": \"123456789\",\n                \"files\": {\"black\": [], \"aiohttp\": []},\n            },\n        }\n    )\n\n    tester.execute(f\"--top-level {output_format}\")\n\n    expected: str | list[dict[str, str]] = \"\"\n    if \"json\" in output_format:\n        expected = [\n            {\n                \"name\": \"black\",\n                \"version\": \"23.3.0\",\n                \"description\": \"\",\n                \"installed_status\": \"installed\",\n            },\n        ]\n        assert json.loads(tester.io.fetch_output()) == expected\n    else:\n        expected = \"\"\"black 23.3.0 \\n\"\"\"\n        assert tester.io.fetch_output() == expected\n\n\ndef test_show_error_top_level_with_tree(tester: CommandTester) -> None:\n    expected = \"Error: Cannot use --tree and --top-level at the same time.\\n\"\n    tester.execute(\"--top-level --tree\")\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == 1\n\n\ndef test_show_error_top_level_with_single_package(tester: CommandTester) -> None:\n    expected = \"Error: Cannot use --top-level when displaying a single package.\\n\"\n    tester.execute(\"--top-level some_package_name\")\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == 1\n\n\n@pytest.mark.parametrize(\n    (\"project_directory\", \"required_fixtures\"),\n    [\n        (\n            \"deleted_directory_dependency\",\n            [],\n        ),\n    ],\n)\ndef test_show_outdated_missing_directory_dependency(\n    tester: CommandTester,\n    poetry: Poetry,\n    installed: Repository,\n    repo: TestRepository,\n) -> None:\n    with (poetry.pyproject.file.path.parent / \"poetry.lock\").open(mode=\"rb\") as f:\n        data = tomllib.load(f)\n\n    assert isinstance(poetry.locker, TestLocker)\n    poetry.locker.mock_lock_data(data)\n\n    poetry.package.add_dependency(\n        Factory.create_dependency(\n            \"missing\",\n            {\"path\": data[\"package\"][0][\"source\"][\"url\"]},\n        )\n    )\n\n    with pytest.raises(ValueError, match=\"does not exist\"):\n        tester.execute(\"\")\n\n\ndef test_show_error_invalid_output_format(\n    tester: CommandTester,\n) -> None:\n    expected = \"Error: Invalid output format. Supported formats are: json, text.\\n\"\n    tester.execute(\"--format invalid\")\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == 1\n\n\ndef test_show_error_invalid_output_format_with_tree_option(\n    tester: CommandTester,\n) -> None:\n    expected = \"Error: --tree option can only be used with the text output option.\\n\"\n    tester.execute(\"--format json --tree\")\n    assert tester.io.fetch_error() == expected\n    assert tester.status_code == 1\n"
  },
  {
    "path": "tests/console/commands/test_sync.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.exceptions import CleoNoSuchOptionError\n\nfrom poetry.console.commands.sync import SyncCommand\n\n# import all tests from the install command\n# and run them for sync by overriding the command fixture\nfrom tests.console.commands.test_install import *  # noqa: F403\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n    from pytest_mock import MockerFixture\n\n\n@pytest.fixture  # type: ignore[no-redef]\ndef command() -> str:\n    return \"sync\"\n\n\n@pytest.mark.skip(\"Only relevant for `poetry install`\")  # type: ignore[no-redef]\ndef test_sync_option_is_passed_to_the_installer() -> None:\n    \"\"\"The only test from the install command that does not work for sync.\"\"\"\n\n\ndef test_sync_option_not_available(tester: CommandTester) -> None:\n    with pytest.raises(CleoNoSuchOptionError):\n        tester.execute(\"--sync\")\n\n\ndef test_synced_installer(tester: CommandTester, mocker: MockerFixture) -> None:\n    assert isinstance(tester.command, SyncCommand)\n    mock = mocker.patch(\n        \"poetry.console.commands.install.InstallCommand.installer\",\n        new_callable=mocker.PropertyMock,\n    )\n\n    tester.execute()\n\n    mock.return_value.requires_synchronization.assert_called_with(True)\n"
  },
  {
    "path": "tests/console/commands/test_update.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.console.commands.update import UpdateCommand\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.helpers import TestRepository\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef poetry_with_outdated_lockfile(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    source = fixture_dir(\"outdated_lock\")\n\n    return project_factory(\n        name=\"foobar\",\n        pyproject_content=(source / \"pyproject.toml\").read_text(encoding=\"utf-8\"),\n        poetry_lock_content=(source / \"poetry.lock\").read_text(encoding=\"utf-8\"),\n    )\n\n\n@pytest.mark.parametrize(\n    \"command\",\n    [\n        \"--dry-run\",\n        \"docker --dry-run\",\n    ],\n)\ndef test_update_with_dry_run_keep_files_intact(\n    command: str,\n    poetry_with_outdated_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n\n    original_pyproject_content = poetry_with_outdated_lockfile.file.read()\n    original_lockfile_content = poetry_with_outdated_lockfile._locker.lock_data\n\n    repo.add_package(get_package(\"docker\", \"4.3.0\"))\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    tester.execute(command)\n\n    assert poetry_with_outdated_lockfile.file.read() == original_pyproject_content\n    assert poetry_with_outdated_lockfile._locker.lock_data == original_lockfile_content\n\n\n@pytest.mark.parametrize(\n    (\"command\", \"expected\"),\n    [\n        (\"\", True),\n        (\"--dry-run\", True),\n        (\"--lock\", False),\n    ],\n)\ndef test_update_prints_operations(\n    command: str,\n    expected: bool,\n    poetry_with_outdated_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n\n    repo.add_package(get_package(\"docker\", \"4.3.0\"))\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    tester.execute(command)\n    output = tester.io.fetch_output()\n\n    assert (\"Package operations:\" in output) is expected\n    assert (\"Installing docker (4.3.1)\" in output) is expected\n\n\ndef test_update_sync_option_is_passed_to_the_installer(\n    poetry_with_outdated_lockfile: Poetry,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    The --sync option is passed properly to the installer from update.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n    assert isinstance(tester.command, UpdateCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=1)\n\n    tester.execute(\"--sync\")\n\n    assert tester.command.installer._requires_synchronization\n\n\ndef test_update_with_valid_package_name(\n    poetry_with_outdated_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    Specifying a valid dependency should not raise an error.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n    assert isinstance(tester.command, UpdateCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    status = tester.execute(\"docker\")\n\n    assert status == 0\n    assert tester.io.fetch_error() == \"\"\n\n\ndef test_update_with_non_normalized_package_name(\n    poetry_with_outdated_lockfile: Poetry,\n    repo: TestRepository,\n    command_tester_factory: CommandTesterFactory,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    Package names that differ only in normalization (e.g. 'Docker' vs 'docker')\n    should be accepted.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n    assert isinstance(tester.command, UpdateCommand)\n    mocker.patch.object(tester.command.installer, \"run\", return_value=0)\n\n    repo.add_package(get_package(\"docker\", \"4.3.1\"))\n\n    status = tester.execute(\"Docker\")\n\n    assert status == 0\n    assert tester.io.fetch_error() == \"\"\n\n\ndef test_update_with_invalid_package_name_shows_error(\n    poetry_with_outdated_lockfile: Poetry,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    \"\"\"\n    Providing non-existent package names should raise an error.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n\n    status = tester.execute(\"nonexistent-package\")\n\n    assert status == 1\n    assert (\n        \"The following packages are not dependencies of this project: nonexistent-package\"\n        in tester.io.fetch_error()\n    )\n\n\ndef test_update_with_multiple_invalid_package_names_shows_error(\n    poetry_with_outdated_lockfile: Poetry,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    \"\"\"\n    Providing multiple non-existent package names should list all of them in the error.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n\n    status = tester.execute(\"fake1 fake2 fake3\")\n\n    assert status == 1\n    error = tester.io.fetch_error()\n    assert \"The following packages are not dependencies of this project\" in error\n    assert \"fake1\" in error\n    assert \"fake2\" in error\n    assert \"fake3\" in error\n\n\n@pytest.mark.parametrize(\n    \"package_spec\",\n    [\n        \"docker==1.2.3\",\n        \"docker>=1.0,<2.0\",\n        \"docker!=1.0\",\n        \"docker[extra]>=1.0\",\n        \"nonexistent==1.2.3\",\n        \"nonexistent>=1.0\",\n    ],\n)\ndef test_update_with_version_specifier_raises_error(\n    package_spec: str,\n    poetry_with_outdated_lockfile: Poetry,\n    command_tester_factory: CommandTesterFactory,\n) -> None:\n    \"\"\"\n    The update command only accepts bare package names. Passing requirement\n    strings with version specifiers should raise a clear error pointing\n    to poetry add, regardless of whether the package is a dependency.\n    \"\"\"\n    tester = command_tester_factory(\"update\", poetry=poetry_with_outdated_lockfile)\n\n    status = tester.execute(package_spec)\n\n    assert status == 1\n    error = tester.io.fetch_error()\n    assert \"Version specifiers are not allowed\" in error\n    assert \"poetry update\" in error\n    assert \"poetry add\" in error\n"
  },
  {
    "path": "tests/console/commands/test_version.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.console.commands.version import VersionCommand\n\n\nif TYPE_CHECKING:\n    from cleo.testers.command_tester import CommandTester\n\n    from poetry.poetry import Poetry\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture()\ndef command() -> VersionCommand:\n    return VersionCommand()\n\n\n@pytest.fixture\ndef tester(command_tester_factory: CommandTesterFactory) -> CommandTester:\n    return command_tester_factory(\"version\")\n\n\n@pytest.fixture\ndef poetry_with_underscore(\n    project_factory: ProjectFactory, fixture_dir: FixtureDirGetter\n) -> Poetry:\n    source = fixture_dir(\"simple_project\")\n    pyproject_content = (source / \"pyproject.toml\").read_text(encoding=\"utf-8\")\n    pyproject_content = pyproject_content.replace(\"simple-project\", \"simple_project\")\n    return project_factory(\n        \"project_with_underscore\", pyproject_content=pyproject_content\n    )\n\n\n@pytest.mark.parametrize(\n    \"version, rule, expected\",\n    [\n        (\"0.0.0\", \"patch\", \"0.0.1\"),\n        (\"0.0.0\", \"minor\", \"0.1.0\"),\n        (\"0.0.0\", \"major\", \"1.0.0\"),\n        (\"0.0\", \"major\", \"1.0\"),\n        (\"0.0\", \"minor\", \"0.1\"),\n        (\"0.0\", \"patch\", \"0.0.1\"),\n        (\"1.2.3\", \"patch\", \"1.2.4\"),\n        (\"1.2.3\", \"minor\", \"1.3.0\"),\n        (\"1.2.3\", \"major\", \"2.0.0\"),\n        (\"1.2.3\", \"prepatch\", \"1.2.4a0\"),\n        (\"1.2.3\", \"preminor\", \"1.3.0a0\"),\n        (\"1.2.3\", \"premajor\", \"2.0.0a0\"),\n        (\"1.2.3-beta.1\", \"patch\", \"1.2.3\"),\n        (\"1.2.3-beta.1\", \"minor\", \"1.3.0\"),\n        (\"1.2.3-beta.1\", \"major\", \"2.0.0\"),\n        (\"1.2.3-beta.1\", \"prerelease\", \"1.2.3b2\"),\n        (\"1.2.3-beta1\", \"prerelease\", \"1.2.3b2\"),\n        (\"1.2.3beta1\", \"prerelease\", \"1.2.3b2\"),\n        (\"1.2.3b1\", \"prerelease\", \"1.2.3b2\"),\n        (\"1.2.3\", \"prerelease\", \"1.2.4a0\"),\n        (\"0.0.0\", \"1.2.3\", \"1.2.3\"),\n    ],\n)\ndef test_increment_version(\n    version: str, rule: str, expected: str, command: VersionCommand\n) -> None:\n    assert command.increment_version(version, rule).text == expected\n\n\n@pytest.mark.parametrize(\n    \"version, rule, expected\",\n    [\n        (\"1.2.3\", \"prerelease\", \"1.2.4a0\"),\n        (\"1.2.3a0\", \"prerelease\", \"1.2.3b0\"),\n        (\"1.2.3a1\", \"prerelease\", \"1.2.3b0\"),\n        (\"1.2.3b1\", \"prerelease\", \"1.2.3rc0\"),\n        (\"1.2.3rc0\", \"prerelease\", \"1.2.3\"),\n        (\"1.2.3-beta.1\", \"prerelease\", \"1.2.3rc0\"),\n        (\"1.2.3-beta1\", \"prerelease\", \"1.2.3rc0\"),\n        (\"1.2.3beta1\", \"prerelease\", \"1.2.3rc0\"),\n    ],\n)\ndef test_next_phase_version(\n    version: str, rule: str, expected: str, command: VersionCommand\n) -> None:\n    assert command.increment_version(version, rule, True).text == expected\n\n\ndef test_version_show(tester: CommandTester) -> None:\n    tester.execute()\n    assert tester.io.fetch_output() == \"simple-project 1.2.3\\n\"\n\n\ndef test_version_show_with_underscore(\n    command_tester_factory: CommandTesterFactory, poetry_with_underscore: Poetry\n) -> None:\n    tester = command_tester_factory(\"version\", poetry=poetry_with_underscore)\n    tester.execute()\n    assert tester.io.fetch_output() == \"simple_project 1.2.3\\n\"\n\n\ndef test_short_version_show(tester: CommandTester) -> None:\n    tester.execute(\"--short\")\n    assert tester.io.fetch_output() == \"1.2.3\\n\"\n\n\ndef test_version_update(tester: CommandTester) -> None:\n    tester.execute(\"2.0.0\")\n    assert tester.io.fetch_output() == \"Bumping version from 1.2.3 to 2.0.0\\n\"\n\n\ndef test_short_version_update(tester: CommandTester) -> None:\n    tester.execute(\"--short 2.0.0\")\n    assert tester.io.fetch_output() == \"2.0.0\\n\"\n\n\ndef test_phase_version_update(tester: CommandTester) -> None:\n    assert isinstance(tester.command, VersionCommand)\n    tester.command.poetry.package._set_version(\"1.2.4a0\")\n    tester.execute(\"prerelease --next-phase\")\n    assert tester.io.fetch_output() == \"Bumping version from 1.2.4a0 to 1.2.4b0\\n\"\n\n\ndef test_dry_run(tester: CommandTester) -> None:\n    assert isinstance(tester.command, VersionCommand)\n    old_pyproject = tester.command.poetry.file.path.read_text(encoding=\"utf-8\")\n    tester.execute(\"--dry-run major\")\n\n    new_pyproject = tester.command.poetry.file.path.read_text(encoding=\"utf-8\")\n    assert tester.io.fetch_output() == \"Bumping version from 1.2.3 to 2.0.0\\n\"\n    assert old_pyproject == new_pyproject\n"
  },
  {
    "path": "tests/console/conftest.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom cleo.testers.application_tester import ApplicationTester\nfrom cleo.testers.command_tester import CommandTester\n\nfrom poetry.installation import Installer\nfrom poetry.utils.env import MockEnv\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import PoetryTestApplication\nfrom tests.helpers import TestExecutor\nfrom tests.helpers import mock_clone\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from pathlib import Path\n\n    from pytest_mock import MockerFixture\n\n    from poetry.installation.executor import Executor\n    from poetry.poetry import Poetry\n    from poetry.repositories import Repository\n    from poetry.utils.env import Env\n    from tests.conftest import Config\n    from tests.types import CommandTesterFactory\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef env(tmp_path: Path) -> MockEnv:\n    path = tmp_path / \".venv\"\n    path.mkdir(parents=True)\n    return MockEnv(path=path, is_venv=True)\n\n\n@pytest.fixture(autouse=True)\ndef setup(\n    mocker: MockerFixture,\n    installed: Repository,\n    config: Config,\n    env: MockEnv,\n) -> Iterator[None]:\n    # Do not run pip commands of the executor\n    mocker.patch(\"poetry.installation.executor.Executor.run_pip\")\n\n    p = mocker.patch(\"poetry.installation.installer.Installer._get_installed\")\n    p.return_value = installed\n\n    p = mocker.patch(\n        \"poetry.repositories.installed_repository.InstalledRepository.load\"\n    )\n    p.return_value = installed\n\n    # Patch git module to not actually clone projects\n    mocker.patch(\"poetry.vcs.git.Git.clone\", new=mock_clone)\n    p = mocker.patch(\"poetry.vcs.git.Git.get_revision\")\n    p.return_value = MOCK_DEFAULT_GIT_REVISION\n\n    # Patch the virtual environment creation do actually do nothing\n    mocker.patch(\"poetry.utils.env.EnvManager.create_venv\", return_value=env)\n\n    # Patch the virtual environment creation do actually do nothing\n    mocker.patch(\"poetry.utils.env.EnvManager.create_venv\", return_value=env)\n\n    # Setting terminal width\n    environ = dict(os.environ)\n    os.environ[\"COLUMNS\"] = \"80\"\n\n    yield\n\n    os.environ.clear()\n    os.environ.update(environ)\n\n\n@pytest.fixture\ndef project_directory() -> str:\n    return \"simple_project\"\n\n\n@pytest.fixture\ndef poetry(\n    project_directory: str,\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n) -> Poetry:\n    return project_factory(name=\"simple\", source=fixture_dir(project_directory))\n\n\n@pytest.fixture\ndef app(poetry: Poetry) -> PoetryTestApplication:\n    app_ = PoetryTestApplication(poetry)\n    io = NullIO()\n    app_._load_plugins(io)\n    return app_\n\n\n@pytest.fixture\ndef app_tester(app: PoetryTestApplication) -> ApplicationTester:\n    return ApplicationTester(app)\n\n\n@pytest.fixture()\ndef executor(poetry: Poetry, config: Config, env: MockEnv) -> TestExecutor:\n    return TestExecutor(env, poetry.pool, config, NullIO())\n\n\n@pytest.fixture\ndef command_tester_factory(\n    app: PoetryTestApplication, env: MockEnv\n) -> CommandTesterFactory:\n    def _tester(\n        command: str,\n        poetry: Poetry | None = None,\n        installer: Installer | None = None,\n        executor: Executor | None = None,\n        environment: Env | None = None,\n    ) -> CommandTester:\n        command_obj = app.find(command)\n        tester = CommandTester(command_obj)\n\n        # Setting the formatter from the application\n        # TODO: Find a better way to do this in Cleo\n        app_io = app.create_io()\n        formatter = app_io.output.formatter\n        tester.io.output.set_formatter(formatter)\n        tester.io.error_output.set_formatter(formatter)\n\n        if poetry:\n            app._poetry = poetry\n\n        poetry = app.poetry\n\n        if hasattr(command_obj, \"set_env\"):\n            command_obj.set_env(environment or env)\n\n        if hasattr(command_obj, \"set_installer\"):\n            installer = installer or Installer(\n                tester.io,\n                env,\n                poetry.package,\n                poetry.locker,\n                poetry.pool,\n                poetry.config,\n                executor=executor\n                or TestExecutor(env, poetry.pool, poetry.config, tester.io),\n            )\n            command_obj.set_installer(installer)\n\n        return tester\n\n    return _tester\n\n\n@pytest.fixture\ndef do_lock(command_tester_factory: CommandTesterFactory, poetry: Poetry) -> None:\n    command_tester_factory(\"lock\").execute()\n    assert poetry.locker.lock.exists()\n"
  },
  {
    "path": "tests/console/logging/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/logging/formatters/__init__.py",
    "content": ""
  },
  {
    "path": "tests/console/logging/formatters/test_builder_formatter.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.console.logging.formatters.builder_formatter import BuilderLogFormatter\n\n\n@pytest.mark.parametrize(\n    \"input_msg, expected_output\",\n    [\n        (\"Building package\", \"  - Building <info>package</info>\"),\n        (\"Built package\", \"  - Built <success>package</success>\"),\n        (\"Adding: dependency\", \"  - Adding: <b>dependency</b>\"),\n        (\n            \"Executing build script: setup.py\",\n            \"  - Executing build script: <b>setup.py</b>\",\n        ),\n        (\"Some other message\", \"Some other message\"),  # No formatting should be applied\n        (\"\", \"\"),  # Edge case: Empty string\n        (\n            \"  Building package  \",\n            \"  Building package  \",\n        ),  # Edge case: Whitespace handling\n        (\"building package\", \"building package\"),  # Edge case: Case sensitivity\n    ],\n)\ndef test_builder_log_formatter(input_msg: str, expected_output: str) -> None:\n    formatter = BuilderLogFormatter()\n    assert formatter.format(input_msg) == expected_output\n"
  },
  {
    "path": "tests/console/logging/test_io_formatter.py",
    "content": "from __future__ import annotations\n\nfrom logging import LogRecord\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.console.logging.io_formatter import IOFormatter\nfrom poetry.console.logging.io_formatter import _log_prefix\nfrom poetry.console.logging.io_formatter import _path_to_package\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n\n@pytest.mark.parametrize(\n    (\"record_name\", \"record_pathname\", \"record_msg\", \"expected\"),\n    [\n        (\"poetry\", \"foo/bar.py\", \"msg\", \"msg\"),\n        (\"poetry.core\", \"foo/bar.py\", \"msg\", \"msg\"),\n        (\"baz\", \"syspath/foo/bar.py\", \"msg\", \"[foo:baz] msg\"),\n        (\"root\", \"syspath/foo/bar.py\", \"1\\n\\n2\", \"[foo] 1\\n[foo] \\n[foo] 2\"),\n    ],\n)\ndef test_format(\n    mocker: MockerFixture,\n    record_name: str,\n    record_pathname: str,\n    record_msg: str,\n    expected: str,\n) -> None:\n    mocker.patch(\"sys.path\", [str(Path(\"syspath\"))])\n    record = LogRecord(record_name, 0, record_pathname, 0, record_msg, (), None)\n    formatter = IOFormatter()\n    assert formatter.format(record) == expected\n\n\n@pytest.mark.parametrize(\n    (\"record_name\", \"record_pathname\", \"expected\"),\n    [\n        (\"root\", \"syspath/foo/bar.py\", \"foo\"),\n        (\"baz\", \"syspath/foo/bar.py\", \"foo:baz\"),\n        (\"baz\", \"unexpected/foo/bar.py\", \"bar:baz\"),\n    ],\n)\ndef test_log_prefix(\n    mocker: MockerFixture,\n    record_name: str,\n    record_pathname: str,\n    expected: str,\n) -> None:\n    mocker.patch(\"sys.path\", [str(Path(\"syspath\"))])\n    record = LogRecord(record_name, 0, record_pathname, 0, \"msg\", (), None)\n    assert _log_prefix(record) == expected\n\n\n@pytest.mark.parametrize(\n    (\"path\", \"expected\"),\n    [\n        (\"python-l/lib/python3.9/site-packages/foo/bar/baz.py\", \"foo\"),  # Linux\n        (\"python-w/lib/site-packages/foo/bar/baz.py\", \"foo\"),  # Windows\n        (\"unexpected/foo/bar/baz.py\", None),  # unexpected\n    ],\n)\ndef test_path_to_package(\n    mocker: MockerFixture, path: str, expected: str | None\n) -> None:\n    mocker.patch(\n        \"sys.path\",\n        # We just put the Linux and the Windows variants in the path,\n        # so we do not have to create different mocks based on the subtest.\n        [\n            # On Linux, only the site-packages directory is in the path.\n            str(Path(\"python-l/lib/python3.9/site-packages\")),\n            # On Windows, both the base directory and the site-packages directory\n            # are in the path.\n            str(Path(\"python-w\")),\n            str(Path(\"python-w/other\")),  # this one is just to test for robustness\n            str(Path(\"python-w/lib/site-packages\")),\n            str(Path(\"python-w/lib\")),  # this one is just to test for robustness\n        ],\n    )\n    assert _path_to_package(Path(path)) == expected\n"
  },
  {
    "path": "tests/console/test_application.py",
    "content": "from __future__ import annotations\n\nimport re\nimport shutil\n\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\nfrom typing import cast\n\nimport pytest\n\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import Application\nfrom poetry.console.commands.command import Command\nfrom poetry.plugins.application_plugin import ApplicationPlugin\nfrom poetry.plugins.plugin_manager import ProjectPluginCache\nfrom poetry.repositories.cached_repository import CachedRepository\nfrom poetry.utils.authenticator import Authenticator\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import MockEnv\nfrom tests.helpers import mock_metadata_entry_points\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from cleo.io.inputs.argv_input import ArgvInput\n    from pytest_mock import MockerFixture\n\n    from tests.helpers import PoetryTestApplication\n    from tests.types import FixtureDirGetter\n    from tests.types import SetProjectContext\n\n\nclass FooCommand(Command):\n    name = \"foo\"\n\n    description = \"Foo Command\"\n\n    def handle(self) -> int:\n        self.line(\"foo called\")\n\n        return 0\n\n\nclass AddCommandPlugin(ApplicationPlugin):\n    commands: ClassVar[list[type[Command]]] = [FooCommand]\n\n\n@pytest.fixture\ndef with_add_command_plugin(mocker: MockerFixture) -> None:\n    mock_metadata_entry_points(mocker, AddCommandPlugin)\n\n\ndef test_application_with_plugins(with_add_command_plugin: None) -> None:\n    app = Application()\n\n    tester = ApplicationTester(app)\n    tester.execute(\"\")\n\n    assert re.search(r\"\\s+foo\\s+Foo Command\", tester.io.fetch_output()) is not None\n    assert tester.status_code == 0\n\n\ndef test_application_with_plugins_disabled(with_add_command_plugin: None) -> None:\n    app = Application()\n\n    tester = ApplicationTester(app)\n    tester.execute(\"--no-plugins\")\n\n    assert re.search(r\"\\s+foo\\s+Foo Command\", tester.io.fetch_output()) is None\n    assert tester.status_code == 0\n\n\ndef test_application_execute_plugin_command(with_add_command_plugin: None) -> None:\n    app = Application()\n\n    tester = ApplicationTester(app)\n    tester.execute(\"foo\")\n\n    assert tester.io.fetch_output() == \"foo called\\n\"\n    assert tester.status_code == 0\n\n\ndef test_application_execute_plugin_command_with_plugins_disabled(\n    with_add_command_plugin: None,\n) -> None:\n    app = Application()\n\n    tester = ApplicationTester(app)\n    tester.execute(\"foo --no-plugins\")\n\n    assert tester.io.fetch_output() == \"\"\n    assert \"The requested command foo does not exist.\" in tester.io.fetch_error()\n    assert tester.status_code == 1\n\n\n@pytest.mark.parametrize(\"with_project_plugins\", [False, True])\n@pytest.mark.parametrize(\"no_plugins\", [False, True])\ndef test_application_project_plugins(\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    no_plugins: bool,\n    with_project_plugins: bool,\n    mocker: MockerFixture,\n    set_project_context: SetProjectContext,\n) -> None:\n    env = MockEnv(\n        path=tmp_path / \"env\", version_info=(3, 8, 0), sys_path=[str(tmp_path / \"env\")]\n    )\n    mocker.patch.object(EnvManager, \"get_system_env\", return_value=env)\n\n    orig_dir = fixture_dir(\"project_plugins\")\n    project_path = tmp_path / \"project\"\n    project_path.mkdir()\n    shutil.copy(orig_dir / \"pyproject.toml\", project_path / \"pyproject.toml\")\n    project_plugin_path = project_path / ProjectPluginCache.PATH\n    if with_project_plugins:\n        project_plugin_path.mkdir(parents=True)\n\n    with set_project_context(project_path, in_place=True):\n        app = Application()\n\n        tester = ApplicationTester(app)\n        tester.execute(\"--no-plugins\" if no_plugins else \"\")\n\n    assert tester.status_code == 0\n    sys_path = EnvManager.get_system_env(naive=True).sys_path\n    if with_project_plugins and not no_plugins:\n        assert sys_path[0] == str(project_plugin_path)\n    else:\n        assert sys_path[0] != str(project_plugin_path)\n\n\n@pytest.mark.parametrize(\"disable_cache\", [True, False])\ndef test_application_verify_source_cache_flag(\n    disable_cache: bool, set_project_context: SetProjectContext\n) -> None:\n    with set_project_context(\"sample_project\"):\n        app = Application()\n\n        tester = ApplicationTester(app)\n        command = \"debug info\"\n\n        if disable_cache:\n            command = f\"{command} --no-cache\"\n\n        assert not app._poetry\n\n        tester.execute(command)\n\n        assert app.poetry.pool.repositories\n\n        for repo in app.poetry.pool.repositories:\n            assert isinstance(repo, CachedRepository)\n            assert repo._disable_cache == disable_cache\n\n\n@pytest.mark.parametrize(\"disable_cache\", [True, False])\ndef test_application_verify_cache_flag_at_install(\n    mocker: MockerFixture,\n    disable_cache: bool,\n    set_project_context: SetProjectContext,\n) -> None:\n    import poetry.utils.authenticator\n\n    # Set default authenticator to None so that it is recreated for each test\n    # and we get a consistent call_count.\n    poetry.utils.authenticator._authenticator = None\n\n    with set_project_context(\"sample_project\"):\n        app = Application()\n\n        tester = ApplicationTester(app)\n        command = \"install --dry-run\"\n\n        if disable_cache:\n            command = f\"{command} --no-cache\"\n\n        spy = mocker.spy(Authenticator, \"__init__\")\n\n        tester.execute(command)\n\n        # The third call is the default authenticator, which ignores the cache flag.\n        assert spy.call_count == 3\n        for call in spy.mock_calls[:2]:\n            (_name, _args, kwargs) = call\n            assert \"disable_cache\" in kwargs\n            assert disable_cache is kwargs[\"disable_cache\"]\n\n\n@pytest.mark.parametrize(\n    (\"tokens\", \"result\"),\n    [\n        (\n            [\"-C\", \"/path/working/dir\", \"env\", \"list\"],\n            [\"--directory\", \"/path/working/dir\", \"env\", \"list\"],\n        ),\n        (\n            [\"-P\", \"/path/project/dir\", \"env\", \"list\"],\n            [\"--project\", \"/path/project/dir\", \"env\", \"list\"],\n        ),\n        (\n            [\"-P/path/project/dir\", \"env\", \"list\"],\n            [\"--project\", \"/path/project/dir\", \"env\", \"list\"],\n        ),\n        (\n            [\"-P/path/project/dir\", \"env\", \"list\"],\n            [\"--project\", \"/path/project/dir\", \"env\", \"list\"],\n        ),\n        (\n            [\"-v\", \"run\", \"-P/path/project/dir\", \"echo\", \"--help\"],\n            [\n                \"--verbose\",\n                \"--project\",\n                \"/path/project/dir\",\n                \"run\",\n                \"--\",\n                \"echo\",\n                \"--help\",\n            ],\n        ),\n        (\n            [\"--no-ansi\", \"run\", \"-V\", \"python\", \"-V\"],\n            [\"--version\", \"--no-ansi\", \"run\", \"--\", \"python\", \"-V\"],\n        ),\n        (\n            [\"--no-ansi\", \"run\", \"-V\", \"--\", \"python\", \"-V\"],\n            [\"--version\", \"--no-ansi\", \"run\", \"--\", \"python\", \"-V\"],\n        ),\n    ],\n)\ndef test_application_input_configuration_and_sorting(\n    tokens: list[str], result: list[str], app: PoetryTestApplication\n) -> None:\n    app.create_io()\n    assert app._io is not None\n\n    io_input = cast(\"ArgvInput\", app._io.input)\n    io_input._tokens = tokens\n\n    app._configure_io(app._io)\n    app._sort_global_options(app._io)\n\n    io_input = cast(\"ArgvInput\", app._io.input)\n    assert io_input._tokens == result\n"
  },
  {
    "path": "tests/console/test_application_command_not_found.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import Application\n\n\nif TYPE_CHECKING:\n    from tests.types import CommandFactory\n\n\n@pytest.fixture\ndef tester() -> ApplicationTester:\n    return ApplicationTester(Application())\n\n\n@pytest.mark.parametrize(\n    (\"command\", \"suggested\"),\n    [\n        (\"x\", None),\n        (\"en\", [\"env activate\", \"env info\", \"env list\", \"env remove\", \"env use\"]),\n        (\"sou\", [\"source add\", \"source remove\", \"source show\"]),\n    ],\n)\ndef test_application_command_not_found_messages(\n    command: str,\n    suggested: list[str] | None,\n    tester: ApplicationTester,\n    command_factory: CommandFactory,\n) -> None:\n    tester.execute(f\"{command}\")\n    assert tester.status_code != 0\n\n    stderr = tester.io.fetch_error()\n    assert f\"The requested command {command} does not exist.\" in stderr\n\n    if suggested is None:\n        assert \"Did you mean one of these perhaps?\" not in stderr\n    else:\n        for suggestion in suggested:\n            assert suggestion in stderr\n\n\n@pytest.mark.parametrize(\n    \"namespace\",\n    [\"cache\", \"debug\", \"env\", \"self\", \"source\"],\n)\ndef test_application_namespaced_command_not_found_messages(\n    namespace: str,\n    tester: ApplicationTester,\n    command_factory: CommandFactory,\n) -> None:\n    tester.execute(f\"{namespace} xxx\")\n    assert tester.status_code != 0\n\n    stderr = tester.io.fetch_error()\n    assert (\n        f\"The requested command does not exist in the {namespace} namespace.\" in stderr\n    )\n"
  },
  {
    "path": "tests/console/test_application_global_options.py",
    "content": "from __future__ import annotations\n\nimport re\nimport textwrap\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom cleo.io.inputs.string_input import StringInput\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import Application\nfrom poetry.console.commands.command import Command\nfrom poetry.console.commands.version import VersionCommand\nfrom poetry.plugins import ApplicationPlugin\nfrom tests.helpers import mock_metadata_entry_points\nfrom tests.helpers import switch_working_directory\n\n\nif TYPE_CHECKING:\n    from pytest import TempPathFactory\n    from pytest_mock import MockerFixture\n\n    from tests.types import FixtureCopier\n\n\nNO_PYPROJECT_TOML_ERROR = \"Poetry could not find a pyproject.toml file in\"\n\n\nclass CheckProjectPathCommand(Command):\n    name = \"check-project-path\"\n\n    description = \"Check Project Path Command\"\n\n    def handle(self) -> int:\n        if not self.poetry.pyproject_path.exists():\n            raise RuntimeError(\n                f\"Wrong project path in handle: {self.poetry.pyproject_path}\\nWorking directory: {Path.cwd()}\"\n            )\n\n        return 0\n\n\nclass EarlyPoetryAccessPlugin(ApplicationPlugin):\n    commands: ClassVar[list[type[Command]]] = [CheckProjectPathCommand]\n\n    def activate(self, application: Application) -> None:\n        super().activate(application)\n\n        # access application.poetry\n        # see https://github.com/nat-n/poethepoet/issues/288\n        if not application.poetry.pyproject_path.exists():\n            raise RuntimeError(\n                f\"Wrong project path in activate: {application.poetry.pyproject_path}\\nWorking directory: {Path.cwd()}\"\n            )\n\n\n@pytest.fixture\ndef with_early_poetry_access_plugin(mocker: MockerFixture) -> None:\n    mock_metadata_entry_points(mocker, EarlyPoetryAccessPlugin)\n\n\n@pytest.fixture\ndef project_source_directory(fixture_copier: FixtureCopier) -> Path:\n    return fixture_copier(\"up_to_date_lock\")\n\n\n@pytest.fixture\ndef relative_project_source_directory(project_source_directory: Path) -> Path:\n    # ensure pre-conditions are met\n    cwd = Path.cwd()\n    assert project_source_directory.is_relative_to(cwd)\n\n    # construct relative path\n    relative_source_directory = project_source_directory.relative_to(cwd)\n    assert relative_source_directory.as_posix() != project_source_directory.as_posix()\n    assert not relative_source_directory.is_absolute()\n\n    return relative_source_directory\n\n\n@pytest.fixture\ndef tester() -> ApplicationTester:\n    return ApplicationTester(Application())\n\n\n@pytest.fixture\ndef with_mocked_version_command(mocker: MockerFixture) -> None:\n    orig_version_command = VersionCommand.handle\n\n    def mock_handle(command: VersionCommand) -> int:\n        exit_code = orig_version_command(command)\n\n        command.io.write_line(f\"ProjectPath: {command.poetry.pyproject_path.parent}\")\n        command.io.write_line(f\"WorkingDirectory: {Path.cwd()}\")\n\n        return exit_code\n\n    mocker.patch(\"poetry.console.commands.version.VersionCommand.handle\", mock_handle)\n\n\ndef test_application_global_option_ensure_error_when_context_invalid(\n    tester: ApplicationTester,\n) -> None:\n    # command fails due to lack of pyproject.toml file in cwd\n    tester.execute(\"show --only main\")\n    assert tester.status_code != 0\n\n    stderr = tester.io.fetch_error()\n    assert NO_PYPROJECT_TOML_ERROR in stderr\n\n\n@pytest.mark.parametrize(\"parameter\", [\"-C\", \"--directory\", \"-P\", \"--project\"])\n@pytest.mark.parametrize(\n    \"command_args\",\n    [\n        \"{option} show --only main\",\n        \"show {option} --only main\",\n        \"show --only main {option}\",\n    ],\n)\ndef test_application_global_option_position_does_not_matter(\n    parameter: str,\n    command_args: str,\n    tester: ApplicationTester,\n    project_source_directory: Path,\n) -> None:\n    cwd = Path.cwd()\n    assert cwd != project_source_directory\n\n    option = f\"{parameter} {project_source_directory.as_posix()}\"\n    tester.execute(command_args.format(option=option))\n    assert tester.status_code == 0\n\n    stdout = tester.io.fetch_output()\n    stderr = tester.io.fetch_error()\n\n    assert NO_PYPROJECT_TOML_ERROR not in stderr\n    assert NO_PYPROJECT_TOML_ERROR not in stdout\n\n    assert \"certifi\" in stdout\n    assert len(stdout.splitlines()) == 8\n\n\n@pytest.mark.parametrize(\"parameter\", [\"-C\", \"--directory\", \"-P\", \"--project\"])\n@pytest.mark.parametrize(\n    \"invalid_source_directory\",\n    [\n        \"/invalid/path\",  # non-existent path\n        __file__,  # not a directory\n    ],\n)\ndef test_application_global_option_context_is_validated(\n    parameter: str,\n    tester: ApplicationTester,\n    invalid_source_directory: str,\n) -> None:\n    option = f\"{parameter} '{invalid_source_directory}'\"\n    tester.execute(f\"show {option}\")\n    assert tester.status_code != 0\n\n    stdout = tester.io.fetch_output()\n    assert stdout == \"\"\n\n    stderr = tester.io.fetch_error()\n    assert re.match(\n        r\"\\nSpecified path '(.+)?' is not a valid directory.\\n\",\n        stderr,\n    )\n\n\n@pytest.mark.parametrize(\"parameter\", [\"project\", \"directory\"])\ndef test_application_with_context_parameters(\n    parameter: str,\n    tester: ApplicationTester,\n    project_source_directory: Path,\n    with_mocked_version_command: None,\n) -> None:\n    # ensure pre-conditions are met\n    assert project_source_directory != Path.cwd()\n\n    is_directory_param = parameter == \"directory\"\n\n    tester.execute(f\"--{parameter} {project_source_directory} version\")\n    assert tester.io.fetch_error() == \"\"\n    assert tester.status_code == 0\n\n    output = tester.io.fetch_output()\n    assert output == textwrap.dedent(f\"\"\"\\\n    foobar 0.1.0\n    ProjectPath: {project_source_directory}\n    WorkingDirectory: {project_source_directory if is_directory_param else Path.cwd()}\n    \"\"\")\n\n\ndef test_application_with_relative_project_parameter(\n    tester: ApplicationTester,\n    project_source_directory: Path,\n    relative_project_source_directory: Path,\n    with_mocked_version_command: None,\n    tmp_path_factory: TempPathFactory,\n) -> None:\n    cwd = Path.cwd()\n    # we expect application run to be executed within current cwd\n    # but project to be a subdirectory\n    args = f\"--directory '{cwd}' --project {relative_project_source_directory} version\"\n\n    # we switch cwd to a new temporary directory unrelated to the project directory\n    new_working_dir = tmp_path_factory.mktemp(\"unrelated-working-directory\")\n    with switch_working_directory(new_working_dir):\n        assert Path.cwd() == new_working_dir\n\n        tester.execute(args)\n        assert tester.io.fetch_error() == \"\"\n        assert tester.status_code == 0\n\n        output = tester.io.fetch_output()\n        assert output == textwrap.dedent(f\"\"\"\\\n        foobar 0.1.0\n        ProjectPath: {project_source_directory}\n        WorkingDirectory: {cwd}\n        \"\"\")\n\n\ndef test_application_with_relative_directory_parameter_and_early_poetry_access_plugin(\n    tester: ApplicationTester,\n    with_early_poetry_access_plugin: None,\n    relative_project_source_directory: Path,\n) -> None:\n    \"\"\"see https://github.com/nat-n/poethepoet/issues/288\"\"\"\n    tester.execute(\n        f\"--directory {relative_project_source_directory} check-project-path\"\n    )\n\n    assert tester.status_code == 0, tester.io.fetch_error()\n\n\n@pytest.mark.parametrize(\n    (\"parameter\", \"check\", \"result\"),\n    [\n        (\"--ansi\", \"is_decorated\", True),\n        (\"--no-ansi\", \"is_decorated\", False),\n        (\"--no-interaction\", \"is_interactive\", False),\n        (\"--verbose\", \"is_verbose\", True),\n        (\"-vv\", \"is_verbose\", True),\n        (\"-vv\", \"is_very_verbose\", True),\n        (\"-vv\", \"is_debug\", False),\n        (\"-vvv\", \"is_debug\", True),\n    ],\n)\ndef test_application_io_options_are_set(\n    parameter: str, check: str, result: bool\n) -> None:\n    # we use an actual application here to avoid cleo's testing overrides\n    application = Application()\n    application.auto_exits(False)\n    application._io = BufferedIO()\n\n    assert application.run(StringInput(f\"{parameter} about\")) == 0\n    assert getattr(application._io, check)() == result\n"
  },
  {
    "path": "tests/console/test_application_removed_commands.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.testers.application_tester import ApplicationTester\n\nfrom poetry.console.application import COMMAND_NOT_FOUND_PREFIX_MESSAGE\nfrom poetry.console.application import Application\n\n\nif TYPE_CHECKING:\n    from tests.types import CommandFactory\n\n\n@pytest.fixture\ndef tester() -> ApplicationTester:\n    return ApplicationTester(Application())\n\n\ndef test_application_removed_command_default_message(\n    tester: ApplicationTester,\n) -> None:\n    tester.execute(\"nonexistent\")\n    assert tester.status_code != 0\n\n    stderr = tester.io.fetch_error()\n    assert COMMAND_NOT_FOUND_PREFIX_MESSAGE not in stderr\n    assert \"The requested command nonexistent does not exist.\" in stderr\n\n\n@pytest.mark.parametrize(\n    (\"command\", \"message\"),\n    [\n        (\"shell\", \"shell command is not installed by default\"),\n    ],\n)\ndef test_application_removed_command_messages(\n    command: str,\n    message: str,\n    tester: ApplicationTester,\n    command_factory: CommandFactory,\n) -> None:\n    # ensure precondition is met\n    assert not tester.application.has(command)\n\n    # verify that the custom message is returned and command fails\n    tester.execute(command)\n    assert tester.status_code != 0\n\n    stderr = tester.io.fetch_error()\n    assert COMMAND_NOT_FOUND_PREFIX_MESSAGE in stderr\n    assert message in stderr\n\n    # flush any output/error messages to ensure consistency\n    tester.io.clear()\n\n    # add a mock command and verify the command succeeds and no error message is provided\n    message = \"The shell command was called\"\n    tester.application.add(command_factory(command, command_handler=message))\n    assert tester.application.has(command)\n\n    tester.execute(command)\n    assert tester.status_code == 0\n\n    stdout = tester.io.fetch_output()\n    stderr = tester.io.fetch_error()\n    assert message in stdout\n    assert COMMAND_NOT_FOUND_PREFIX_MESSAGE not in stderr\n    assert stderr == \"\"\n"
  },
  {
    "path": "tests/console/test_exceptions_console_message.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.console.exceptions import ConsoleMessage\n\n\n@pytest.mark.parametrize(\n    (\"text\", \"expected_stripped\"),\n    [\n        (\"<info>Hello, World!</info>\", \"Hello, World!\"),\n        (\"<b>Bold</b>\", \"Bold\"),\n        (\"<i>Italic</i>\", \"Italic\"),\n    ],\n)\ndef test_stripped_property(text: str, expected_stripped: str) -> None:\n    \"\"\"Test the stripped property with various tagged inputs.\"\"\"\n    message = ConsoleMessage(text)\n    assert message.stripped == expected_stripped\n\n\n@pytest.mark.parametrize(\n    (\"text\", \"tag\", \"expected\"),\n    [\n        (\"Hello, World!\", \"info\", \"<info>Hello, World!</>\"),\n        (\"Error occurred\", \"error\", \"<error>Error occurred</>\"),\n        (\"\", \"info\", \"\"),  # Test with empty input\n    ],\n)\ndef test_wrap(text: str, tag: str, expected: str) -> None:\n    \"\"\"Test the wrap method with various inputs.\"\"\"\n    message = ConsoleMessage(text)\n    assert message.wrap(tag).text == expected\n\n\n@pytest.mark.parametrize(\n    (\"text\", \"indent\", \"expected\"),\n    [\n        (\"Hello, World!\", \"    \", \"    Hello, World!\"),\n        (\"Line 1\\nLine 2\", \">>\", \">>Line 1\\n>>Line 2\"),\n        (\"\", \"  \", \"\"),  # Test with empty input\n        (\" \", \"  \", \"  \"),  # Test with whitespace input\n    ],\n)\ndef test_indent(text: str, indent: str, expected: str) -> None:\n    \"\"\"Test the indent method with various inputs.\"\"\"\n    message = ConsoleMessage(text)\n    assert message.indent(indent).text == expected\n\n\n@pytest.mark.parametrize(\n    (\"text\", \"title\", \"indent\", \"expected\"),\n    [\n        (\"Hello, World!\", \"Greeting\", \"\", \"<b>Greeting:</>\\nHello, World!\"),\n        (\n            \"This is a message.\",\n            \"Section Title\",\n            \"  \",\n            \"<b>Section Title:</>\\n  This is a message.\",\n        ),\n        (\"\", \"Title\", \"\", \"\"),  # Test with empty text\n        (\"Multi-line\\nText\", \"Title\", \">>>\", \"<b>Title:</>\\n>>>Multi-line\\n>>>Text\"),\n    ],\n)\ndef test_make_section(text: str, title: str, indent: str, expected: str) -> None:\n    \"\"\"Test the make_section method with various inputs.\"\"\"\n    message = ConsoleMessage(text)\n    assert message.make_section(title, indent).text == expected\n"
  },
  {
    "path": "tests/console/test_exections_poetry_runtime_error.py",
    "content": "from __future__ import annotations\n\nfrom subprocess import CalledProcessError\n\nimport pytest\n\nfrom poetry.console.exceptions import ConsoleMessage\nfrom poetry.console.exceptions import PoetryRuntimeError\n\n\n@pytest.mark.parametrize(\n    (\"reason\", \"messages\", \"exit_code\", \"expected_reason\"),\n    [\n        (\"Error occurred!\", None, 1, \"Error occurred!\"),  # Default scenario\n        (\n            \"Specific error\",\n            [ConsoleMessage(\"Additional details.\")],\n            2,\n            \"Specific error\",\n        ),  # Custom exit code and messages\n        (\"Minimal error\", [], 0, \"Minimal error\"),  # No additional messages\n    ],\n)\ndef test_poetry_runtime_error_init(\n    reason: str,\n    messages: list[ConsoleMessage] | None,\n    exit_code: int,\n    expected_reason: str,\n) -> None:\n    \"\"\"Test the basic initialization of the PoetryRuntimeError class.\"\"\"\n    error = PoetryRuntimeError(reason, messages, exit_code)\n    assert error.exit_code == exit_code\n    assert str(error) == expected_reason\n    assert isinstance(error._messages[0], ConsoleMessage)\n    assert error._messages[0].text == reason\n\n\n@pytest.mark.parametrize(\n    (\"debug\", \"strip\", \"indent\", \"messages\", \"expected_text\"),\n    [\n        (\n            False,\n            False,\n            \"\",\n            [\n                ConsoleMessage(\"Basic message\"),\n                ConsoleMessage(\"Debug message\", debug=True),\n            ],\n            \"Error\\n\\nBasic message\\n\\nYou can also run your <c1>poetry</> command with <c1>-v</> to see more information.\",\n        ),  # Debug message ignored\n        (\n            True,\n            False,\n            \"\",\n            [\n                ConsoleMessage(\"Info message\"),\n                ConsoleMessage(\"Debug message\", debug=True),\n            ],\n            \"Error\\n\\nInfo message\\n\\nDebug message\",\n        ),  # Debug message included in verbose mode\n        (\n            True,\n            True,\n            \"\",\n            [\n                ConsoleMessage(\"<b>Bolded message</b>\"),\n                ConsoleMessage(\"<i>Debug Italics Message</i>\", debug=True),\n            ],\n            \"Error\\n\\nBolded message\\n\\nDebug Italics Message\",\n        ),  # Stripped tags and debug message\n        (\n            False,\n            False,\n            \"    \",\n            [ConsoleMessage(\"Error occurred!\")],\n            \"    Error\\n    \\n    Error occurred!\",\n        ),  # Indented message\n    ],\n)\ndef test_poetry_runtime_error_get_text(\n    debug: bool,\n    strip: bool,\n    indent: str,\n    messages: list[ConsoleMessage],\n    expected_text: str,\n) -> None:\n    \"\"\"Test the get_text method of PoetryRuntimeError.\"\"\"\n    error = PoetryRuntimeError(\"Error\", messages)\n    text = error.get_text(debug=debug, strip=strip, indent=indent)\n    assert text == expected_text\n\n\n@pytest.mark.parametrize(\n    (\"reason\", \"exception\", \"info\", \"expected_message_texts\"),\n    [\n        (\n            \"Command failed\",\n            None,\n            None,\n            [\"Command failed\", \"\"],  # No exception or additional info\n        ),\n        (\n            \"Command failure\",\n            Exception(\"An exception occurred\"),\n            None,\n            [\n                \"Command failure\",\n                \"<b>Exception:</>\\n    | An exception occurred\",\n                \"\",\n            ],  # Exception message included\n        ),\n        (\n            \"Subprocess error\",\n            CalledProcessError(1, [\"cmd\"], b\"stdout\", b\"stderr\"),\n            [\"Additional info\"],\n            [\n                \"Subprocess error\",\n                \"<warning><b>Exception:</>\\n\"\n                \"    | Command '['cmd']' returned non-zero exit status 1.</>\",\n                \"<warning><b>Output:</>\\n    | stdout</>\",\n                \"<warning><b>Errors:</>\\n    | stderr</>\",\n                \"<info>Additional info</>\",\n                \"You can test the failed command by executing:\\n\\n    <c1>cmd</c1>\",\n            ],\n        ),\n    ],\n)\ndef test_poetry_runtime_error_create(\n    reason: str,\n    exception: Exception,\n    info: list[str],\n    expected_message_texts: list[str],\n) -> None:\n    \"\"\"Test the create class method of PoetryRuntimeError.\"\"\"\n    error = PoetryRuntimeError.create(reason, exception, info)\n\n    assert isinstance(error, PoetryRuntimeError)\n    assert all(isinstance(msg, ConsoleMessage) for msg in error._messages)\n\n    actual_texts = [msg.text for msg in error._messages]\n    assert actual_texts == expected_message_texts\n\n\ndef test_poetry_runtime_error_append() -> None:\n    \"\"\"Test the append method of PoetryRuntimeError.\"\"\"\n    error = PoetryRuntimeError.create(\"Error\", info=[\"Hello\"]).append(\"World\")\n    actual_texts = [msg.text for msg in error._messages]\n    assert actual_texts == [\"Error\", \"<info>Hello</>\", \"World\"]\n"
  },
  {
    "path": "tests/fixtures/bad_scripts_project/no_colon/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/bad_scripts_project/no_colon/pyproject.toml",
    "content": "[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = [\"README.rst\"]\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[tool.poetry.scripts]\nfoo = \"bar.bin.foo\"\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/bad_scripts_project/no_colon/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/bad_scripts_project/too_many_colon/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/bad_scripts_project/too_many_colon/pyproject.toml",
    "content": "[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = [\"README.rst\"]\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[tool.poetry.scripts]\nfoo = \"foo::bar\"\n\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/bad_scripts_project/too_many_colon/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_constraints/pyproject.toml",
    "content": "[project]\nname = \"build-constraints\"\nversion = \"0.1.0\"\n\n[tool.poetry.build-constraints]\nLegacy-Lib = { setuptools = \"<75\" }\nno-constraints = {}\n\n[tool.poetry.build-constraints.c-ext-lib]\nCython = { version = \"<3.1\", source = \"pypi\" }\nsetuptools = [\n    { version = \">=60,<75\", python = \"<3.9\" },\n    { version = \">=75\", python = \">=3.8\" }\n]\n"
  },
  {
    "path": "tests/fixtures/build_constraints_empty/pyproject.toml",
    "content": "[project]\nname = \"build-constraints\"\nversion = \"0.1.0\"\n\n[tool.poetry.build-constraints]\n"
  },
  {
    "path": "tests/fixtures/build_system_requires_not_available/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_system_requires_not_available/pyproject.toml",
    "content": "[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = [\"README.rst\"]\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core==0.999\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_system_requires_not_available/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/core_from_git/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_from_git/pyproject.toml",
    "content": "[project]\nname = \"core-from-git\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[build-system]\nrequires = [\"poetry-core @ git+https://github.com/python-poetry/poetry-core.git\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_from_git/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/core_in_range/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_in_range/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_in_range/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/core_not_in_range/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_not_in_range/pyproject.toml",
    "content": "[project]\nname = \"simple-prject\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[build-system]\nrequires = [\"poetry-core<0.1\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/core_not_in_range/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/has_build_script/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/has_build_script/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[tool.poetry.build]\nscript = \"build.py\"\ngenerate-setup-file = true\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/has_build_script/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/multiple_build_deps/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/multiple_build_deps/pyproject.toml",
    "content": "[project]\nname = \"simple-prject\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nrequires-python = \">=3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\", \"setuptools\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/multiple_build_deps/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/no_build_backend/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_build_backend/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_build_backend/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/no_build_system/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_build_system/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_build_system/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/build_systems/no_core/README.md",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_core/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Poetry Contributors\", email = \"no-reply@python-poetry.org\" }\n]\nlicense = { text = \"MIT\" }\nreadme = \"README.md\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\nrequires-python = \">=3.4\"\n\n[tool.maturin]\nmanylinux = \"off\"\nsdist-include = [\"Cargo.lock\", \"json/**/*\"]\nstrip = \"on\"\n\n[build-system]\nbuild-backend = \"maturin\"\nrequires = [\"maturin>=0.8.1,<0.9\"]\n"
  },
  {
    "path": "tests/fixtures/build_systems/no_core/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/complete.toml",
    "content": "[tool.poetry]\nname = \"poetry\"\nversion = \"0.5.0\"\ndescription = \"Python dependency management and packaging made easy.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org/\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.2\"  # Compatible python versions must be declared here\ntoml = \"^0.9\"\n# Dependencies with extras\nrequests = { version = \"^2.13\", extras = [ \"security\" ] }\n# Python specific dependencies with prereleases allowed\npathlib2 = { version = \"^2.2\", python = \"~2.7\", allow-prereleases = true }\n# Git dependencies\ncleo = { git = \"https://github.com/sdispater/cleo.git\", branch = \"master\" }\n\n# Optional dependencies (extras)\npendulum = { version = \"^1.4\", optional = true }\n\n[tool.poetry.extras]\ntime = [ \"pendulum\" ]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"^3.0\"\npytest-cov = \"^2.4\"\n\n[tool.poetry.scripts]\nmy-script = 'my_package:main'\n\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://bar.com\"\n"
  },
  {
    "path": "tests/fixtures/deleted_directory_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-missing-directory-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\npackages = []\n\n[tool.poetry.dependencies]\npython = \"*\"\n"
  },
  {
    "path": "tests/fixtures/deleted_file_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-missing-directory-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\npackages = []\n\n[tool.poetry.dependencies]\npython = \"*\"\n"
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_directory_dependencies/project_with_transitive_directory_dependencies/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_directory_dependencies/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-transitive-directory-dependencies\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"*\"\nproject-with-extras = {path = \"../../project_with_extras/\"}\nproject-with-transitive-file-dependencies = {path = \"../project_with_transitive_file_dependencies/\"}\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_directory_dependencies/setup.py",
    "content": "from __future__ import annotations\n\nfrom distutils.core import setup\n\n\npackages = [\"project_with_extras\"]\n\npackage_data = {\"\": [\"*\"]}\n\nextras_require = {\"extras_a\": [\"pendulum>=1.4.4\"], \"extras_b\": [\"cachy>=0.2.0\"]}\n\nsetup_kwargs = {\n    \"name\": \"project-with-extras\",\n    \"version\": \"1.2.3\",\n    \"description\": \"This is a description\",\n    \"long_description\": None,\n    \"author\": \"Your Name\",\n    \"author_email\": \"you@example.com\",\n    \"url\": None,\n    \"packages\": packages,\n    \"package_data\": package_data,\n    \"extras_require\": extras_require,\n}\n\n\nsetup(**setup_kwargs)\n"
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_file_dependencies/inner-directory-project/pyproject.toml",
    "content": "[tool.poetry]\nname = \"inner-directory-project\"\nversion = \"1.2.4\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"*\"\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_file_dependencies/project_with_transitive_file_dependencies/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/directory/project_with_transitive_file_dependencies/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-transitive-file-dependencies\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"*\"\ndemo = {path = \"../../distributions/demo-0.1.0-py2.py3-none-any.whl\"}\ninner-directory-project = {path = \"./inner-directory-project\"}\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/excluded_subpackage/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/excluded_subpackage/example/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"0.1.0\"\n"
  },
  {
    "path": "tests/fixtures/excluded_subpackage/example/test/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/excluded_subpackage/example/test/excluded.py",
    "content": "from __future__ import annotations\n\nfrom tests.fixtures.excluded_subpackage.example import __version__\n\n\ndef test_version():\n    assert __version__ == \"0.1.0\"\n"
  },
  {
    "path": "tests/fixtures/excluded_subpackage/pyproject.toml",
    "content": "[tool.poetry]\nname = \"example\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Sébastien Eustace <sebastien@eustace.io>\"]\nexclude = [\n    \"**/test/**/*\",\n]\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\npytest = \"^3.0\"\n\n[build-system]\nrequires = [\"poetry>=0.12\"]\nbuild-backend = \"poetry.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/extended_project/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/extended_project/build.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import Any\n\n\ndef build(setup_kwargs: dict[str, Any]):\n    assert setup_kwargs[\"name\"] == \"extended-project\"\n    assert setup_kwargs[\"version\"] == \"1.2.3\"\n\n    dynamic_module = Path(__file__).parent / \"extended_project\" / \"built.py\"\n    dynamic_module.write_text(\"# Generated by build.py\")\n"
  },
  {
    "path": "tests/fixtures/extended_project/extended_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/extended_project/pyproject.toml",
    "content": "[tool.poetry]\nname = \"extended-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n[tool.poetry.build]\nscript = \"build.py\"\ngenerate-setup-file = true\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.7\"\n"
  },
  {
    "path": "tests/fixtures/extended_project_without_setup/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/extended_project_without_setup/build.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/extended_project_without_setup/extended_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/extended_project_without_setup/pyproject.toml",
    "content": "[tool.poetry]\nname = \"extended-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n[tool.poetry.build]\nscript = \"build.py\"\ngenerate-setup-file = false\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[build-system]\nrequires = [\"poetry-core\", \"cython\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/extended_with_no_setup/README.md",
    "content": "Module 1\n========\n"
  },
  {
    "path": "tests/fixtures/extended_with_no_setup/build.py",
    "content": "from __future__ import annotations\n\nimport os\nimport shutil\n\nfrom setuptools import Distribution\nfrom setuptools import Extension\nfrom setuptools.command.build_ext import build_ext\n\n\nextensions = [Extension(\"extended.extended\", [\"extended/extended.c\"])]\n\n\ndef build():\n    distribution = Distribution({\"name\": \"extended\", \"ext_modules\": extensions})\n    distribution.package_dir = {\"extended\": \"extended\"}\n\n    cmd = build_ext(distribution)\n    cmd.ensure_finalized()\n    cmd.run()\n\n    # Copy built extensions back to the project\n    for output in cmd.get_outputs():\n        relative_extension = os.path.relpath(output, cmd.build_lib)\n        shutil.copyfile(output, relative_extension)\n        mode = os.stat(relative_extension).st_mode\n        mode |= (mode & 0o444) >> 2\n        os.chmod(relative_extension, mode)\n\n\nif __name__ == \"__main__\":\n    build()\n"
  },
  {
    "path": "tests/fixtures/extended_with_no_setup/extended/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/extended_with_no_setup/extended/extended.c",
    "content": "#include <Python.h>\n\n\nstatic PyObject *hello(PyObject *self) {\n    return PyUnicode_FromString(\"Hello\");\n}\n\n\nstatic PyMethodDef module_methods[] = {\n    {\n        \"hello\",\n        (PyCFunction) hello,\n        METH_NOARGS,\n        PyDoc_STR(\"Say hello.\")\n    },\n    {NULL}\n};\n\n#if PY_MAJOR_VERSION >= 3\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"extended\",\n    NULL,\n    -1,\n    module_methods,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n};\n#endif\n\nPyMODINIT_FUNC\n#if PY_MAJOR_VERSION >= 3\nPyInit_extended(void)\n#else\ninit_extended(void)\n#endif\n{\n    PyObject *module;\n\n#if PY_MAJOR_VERSION >= 3\n    module = PyModule_Create(&moduledef);\n#else\n    module = Py_InitModule3(\"extended\", module_methods, NULL);\n#endif\n\n    if (module == NULL)\n#if PY_MAJOR_VERSION >= 3\n        return NULL;\n#else\n        return;\n#endif\n\n#if PY_MAJOR_VERSION >= 3\n    return module;\n#endif\n}\n"
  },
  {
    "path": "tests/fixtures/extended_with_no_setup/pyproject.toml",
    "content": "[tool.poetry]\nname = \"extended\"\nversion = \"0.1\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.md\"\n\nhomepage = \"https://python-poetry.org/\"\n\ninclude = [\n    # C extensions must be included in the wheel distributions\n    {path = \"extended/*.so\", format = \"wheel\"},\n    {path = \"extended/*.pyd\", format = \"wheel\"},\n]\n\n[tool.poetry.build]\nscript = \"build.py\"\ngenerate-setup-file = false\n\n[build-system]\nrequires = [\"poetry-core>=1.5.0\", \"setuptools>=67.6.1\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/demo/demo/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/demo/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"0.1.2\"\ndescription = \"Demo package\"\nauthors = [\"Poetry Team <noreply@python-poetry.org>\"]\nlicense = \"MIT\"\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \"*\"\npendulum = \">=1.4.4\"\ncleo = {version=\"*\", optional = true}\ntomlkit = {version=\"*\", optional = true}\n\n[tool.poetry.extras]\nfoo = [\"cleo\"]\nbar = [\"tomlkit\"]\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/namespace-package-one/namespace_package/__init__.py",
    "content": "from __future__ import annotations\n\n\n__import__(\"pkg_resources\").declare_namespace(__name__)\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/namespace-package-one/namespace_package/one/__init__.py",
    "content": "from __future__ import annotations\n\n\nname = \"one\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/namespace-package-one/setup.py",
    "content": "from __future__ import annotations\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\n\nsetup(\n    name=\"namespace_package_one\",\n    version=\"1.0.0\",\n    description=\"\",\n    long_description=\"\",\n    author=\"Python Poetry\",\n    author_email=\"noreply@python-poetry.org\",\n    license=\"MIT\",\n    packages=find_packages(),\n    namespace_packages=[\"namespace_package\"],\n    zip_safe=False,\n)\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/no-dependencies/demo/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/no-dependencies/setup.py",
    "content": "from __future__ import annotations\n\nfrom setuptools import setup\n\n\nkwargs = dict(\n    name=\"demo\",\n    license=\"MIT\",\n    version=\"0.1.2\",\n    description=\"Demo project.\",\n    author=\"Sébastien Eustace\",\n    author_email=\"sebastien@eustace.io\",\n    url=\"https://github.com/demo/demo\",\n    packages=[\"demo\"],\n)\n\n\nsetup(**kwargs)\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/no-version/demo/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/no-version/setup.py",
    "content": "from __future__ import annotations\n\nimport ast\nimport os\n\nfrom setuptools import setup\n\n\ndef read_version():\n    with open(os.path.join(os.path.dirname(__file__), \"demo\", \"__init__.py\")) as f:\n        for line in f:\n            if line.startswith(\"__version__ = \"):\n                return ast.literal_eval(line[len(\"__version__ = \") :].strip())\n\n\nkwargs = dict(\n    name=\"demo\",\n    license=\"MIT\",\n    version=read_version(),\n    description=\"Demo project.\",\n    author=\"Sébastien Eustace\",\n    author_email=\"sebastien@eustace.io\",\n    url=\"https://github.com/demo/demo\",\n    packages=[\"demo\"],\n    install_requires=[\"pendulum>=1.4.4\"],\n    extras_require={\"foo\": [\"cleo\"]},\n)\n\n\nsetup(**kwargs)\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/non-canonical-name/demo/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/non-canonical-name/setup.py",
    "content": "from __future__ import annotations\n\nfrom setuptools import setup\n\n\nkwargs = dict(\n    name=\"Demo\",\n    license=\"MIT\",\n    version=\"0.1.2\",\n    description=\"Demo project.\",\n    author=\"Sébastien Eustace\",\n    author_email=\"sebastien@eustace.io\",\n    url=\"https://github.com/demo/demo\",\n    packages=[\"demo\"],\n    install_requires=[\"pendulum>=1.4.4\"],\n    extras_require={\"foo\": [\"cleo\"], \"bar\": [\"tomlkit\"]},\n)\n\n\nsetup(**kwargs)\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/poetry-plugin/poetry_plugin/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/poetry-plugin/pyproject.toml",
    "content": "[tool.poetry]\nname = \"poetry-plugin\"\nversion = \"0.1.2\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\npendulum = \"^2.0\"\ntomlkit = {version = \"^0.7.0\", optional = true}\n\n[tool.poetry.extras]\nfoo = [\"tomlkit\"]\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/poetry-plugin2/subdir/poetry_plugin/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/poetry-plugin2/subdir/pyproject.toml",
    "content": "[tool.poetry]\nname = \"poetry-plugin\"\nversion = \"0.1.2\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\npendulum = \"^2.0\"\ntomlkit = {version = \"^0.7.0\", optional = true}\n\n[tool.poetry.extras]\nfoo = [\"tomlkit\"]\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/prerelease/prerelease/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/prerelease/pyproject.toml",
    "content": "[tool.poetry]\nname = \"prerelease\"\nversion = \"1.0.0.dev0\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/pyproject-demo/demo/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/pyproject-demo/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"0.1.2\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\npendulum = '^1.4'\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/one/one/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/one/pyproject.toml",
    "content": "[tool.poetry]\nname = \"one\"\nversion = \"1.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/one-copy/one/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/one-copy/pyproject.toml",
    "content": "[tool.poetry]\nname = \"one\"\nversion = \"1.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/two/pyproject.toml",
    "content": "[tool.poetry]\nname = \"two\"\nversion = \"2.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/demo/subdirectories/two/two/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/one/one/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/one/pyproject.toml",
    "content": "[tool.poetry]\nname = \"one\"\nversion = \"1.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/one-copy/one/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/one-copy/pyproject.toml",
    "content": "[tool.poetry]\nname = \"one\"\nversion = \"1.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/two/pyproject.toml",
    "content": "[tool.poetry]\nname = \"two\"\nversion = \"2.0.0\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/git/github.com/forked_demo/subdirectories/two/two/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/incompatible_lock/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Poetry Developer <developer@python-poetry.org>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\nsampleproject = \">=1.3.1\"\n\n[tool.poetry.group.dev.dependencies]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/inspection/demo/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Sébastien Eustace <sebastien@eustace.io>\"]\n\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\npendulum = \">=1.4.4\"\ncleo = {version = \"*\", optional = true}\ntomlkit = {version = \"*\", optional = true}\n\n[tool.poetry.extras]\nfoo = [\"cleo\"]\nbar = [\"tomlkit\"]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"^3.0\"\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps/PKG-INFO",
    "content": "Metadata-Version: 1.0\nName: demo\nVersion: 0.1.0\nSummary: Demo project.\nHome-page: https://github.com/demo/demo\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDescription: UNKNOWN\nPlatform: UNKNOWN\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps/pyproject.toml",
    "content": "# this was copied over and modified from orjson project's pyproject.toml\n# https://github.com/ijl/orjson/blob/master/pyproject.toml\n[project]\nname = \"demo\"\nrepository = \"https://github.com/demo/demo\"\n\n[build-system]\nbuild-backend = \"maturin\"\nrequires = [\"maturin>=0.8.1,<0.9\"]\n\n[tool.maturin]\nmanylinux = \"off\"\nsdist-include = [\"Cargo.lock\", \"json/**/*\"]\nstrip = \"on\"\n\n[tool.black]\nline-length = 88\ntarget-version = ['py36', 'py37', 'py38']\ninclude = '\\.pyi?$'\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/PKG-INFO",
    "content": "Metadata-Version: 2.2\nName: demo\nVersion: 0.1.0\nSummary: Demo project.\nHome-page: https://github.com/demo/demo\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDescription: UNKNOWN\nPlatform: UNKNOWN\nDynamic: Requires-Dist\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_dynamic/pyproject.toml",
    "content": "# this was copied over and modified from orjson project's pyproject.toml\n# https://github.com/ijl/orjson/blob/master/pyproject.toml\n[project]\nname = \"demo\"\nrepository = \"https://github.com/demo/demo\"\n\n[build-system]\nbuild-backend = \"maturin\"\nrequires = [\"maturin>=0.8.1,<0.9\"]\n\n[tool.maturin]\nmanylinux = \"off\"\nsdist-include = [\"Cargo.lock\", \"json/**/*\"]\nstrip = \"on\"\n\n[tool.black]\nline-length = 88\ntarget-version = ['py36', 'py37', 'py38']\ninclude = '\\.pyi?$'\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/PKG-INFO",
    "content": "Metadata-Version: 2.3\nName: demo\nVersion: 0.1.0\nSummary: Demo project.\nHome-page: https://github.com/demo/demo\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDescription: UNKNOWN\nPlatform: UNKNOWN\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_no_setup_pkg_info_no_deps_for_sure/pyproject.toml",
    "content": "# this was copied over and modified from orjson project's pyproject.toml\n# https://github.com/ijl/orjson/blob/master/pyproject.toml\n[project]\nname = \"demo\"\nrepository = \"https://github.com/demo/demo\"\n\n[build-system]\nbuild-backend = \"maturin\"\nrequires = [\"maturin>=0.8.1,<0.9\"]\n\n[tool.maturin]\nmanylinux = \"off\"\nsdist-include = [\"Cargo.lock\", \"json/**/*\"]\nstrip = \"on\"\n\n[tool.black]\nline-length = 88\ntarget-version = ['py36', 'py37', 'py38']\ninclude = '\\.pyi?$'\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_only_requires_txt.egg-info/PKG-INFO",
    "content": "Metadata-Version: 1.0\nName: demo\nVersion: 0.1.0\nSummary: Demo project.\nHome-page: https://github.com/demo/demo\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDescription: UNKNOWN\nPlatform: UNKNOWN\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_only_requires_txt.egg-info/requires.txt",
    "content": "cleo; extra == \"foo\"\npendulum (>=1.4.4)\ntomlkit; extra == \"bar\"\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_poetry_package/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo-poetry\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"John Doe <john@example.com.com>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\npendulum = \"*\"\n\n[tool.poetry.group.dev.dependencies]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_with_obsolete_egg_info/demo-0.1.0.egg-info/PKG-INFO",
    "content": "Metadata-Version: 1.0\nName: demo\nVersion: 0.1.0\nSummary: Demo project.\nHome-page: https://github.com/demo/demo\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDescription: UNKNOWN\nPlatform: UNKNOWN\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_with_obsolete_egg_info/demo-0.1.0.egg-info/requires.txt",
    "content": "cleo; extra == \"foo\"\npendulum (>=1.0.0)\ntomlkit; extra == \"bar\"\n"
  },
  {
    "path": "tests/fixtures/inspection/demo_with_obsolete_egg_info/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Sébastien Eustace <sebastien@eustace.io>\"]\n\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\npendulum = \">=1.4.4\"\ncleo = {version = \"*\", optional = true}\ntomlkit = {version = \"*\", optional = true}\n\n[tool.poetry.extras]\nfoo = [\"cleo\"]\nbar = [\"tomlkit\"]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"^3.0\"\n"
  },
  {
    "path": "tests/fixtures/invalid_lock/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Poetry Developer <developer@python-poetry.org>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\nsampleproject = \">=1.3.1\"\n\n[tool.poetry.group.dev.dependencies]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/invalid_pyproject/pyproject.toml",
    "content": "[project]\nname = \"invalid\"\nversion = \"1.0.0\"\nlicense = \"INVALID\"\nclassifiers = [\n    \"Environment :: Console\",\n    \"Intended Audience :: Clowns\",\n    \"Natural Language :: Ukranian\",\n    \"Topic :: Communications :: Chat :: AOL Instant Messenger\",\n]\ndynamic = [ \"readme\", \"dependencies\", \"requires-python\" ]\n\n[tool.poetry]\nreadme = \"never/exists.md\"\n\n[tool.poetry.dependencies]\npython = \"*\"\npendulum = {\"version\" = \"^2.0.5\", allows-prereleases = true}\ninvalid_dep = \"1.0\"\ninvalid_source = { \"version\" = \"*\", source = \"not-exists\" }\ninvalid_source_multi = [\n    { \"version\" = \"*\", platform = \"linux\", source = \"exists\" },\n    { \"version\" = \"*\", platform = \"win32\", source = \"not-exists2\" },\n]\n\n[[tool.poetry.source]]\nname = \"exists\"\npriority = \"explicit\"\nurl = \"https://example.com\"\n"
  },
  {
    "path": "tests/fixtures/invalid_pyproject_dep_name/pyproject.toml",
    "content": "[project]\nname = \"invalid\"\nversion = \"1.0.0\"\ndynamic = [\"dependencies\"]\n\n[tool.poetry.dependencies]\ninvalid = \"1.0\"\n"
  },
  {
    "path": "tests/fixtures/missing_directory_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-missing-directory-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\npackages = []\n\n[tool.poetry.dependencies]\npython = \"*\"\n\n[tool.poetry.group.dev.dependencies]\nmissing = { path = \"./missing\" }\n"
  },
  {
    "path": "tests/fixtures/missing_extra_directory_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-missing-extra-directory-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\npackages = []\n\n[tool.poetry.dependencies]\npython = \"*\"\nmissing = { path = \"./missing\", optional = true }\n\n[tool.poetry.extras]\nnotinstallable = [\"missing\"]\n"
  },
  {
    "path": "tests/fixtures/missing_file_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-missing-directory-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\npackages = []\n\n[tool.poetry.dependencies]\npython = \"*\"\n\n[tool.poetry.group.dev.dependencies]\nmissing = { file = \"missing-0.1.0-py2.py3-none-any.whl\" }\n"
  },
  {
    "path": "tests/fixtures/nameless_pyproject/pyproject.toml",
    "content": "[tool.poetry]\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Foo <foo@bar.com>\"]\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\n"
  },
  {
    "path": "tests/fixtures/no_name_project/README.rst",
    "content": "No name project\n===============\n"
  },
  {
    "path": "tests/fixtures/no_name_project/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\nversion = \"1.2.3\"\ndescription = \"This project has no name\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.6\"\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n"
  },
  {
    "path": "tests/fixtures/non_package_mode/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\ncleo = \"^0.6\"\npendulum = { git = \"https://github.com/sdispater/pendulum.git\", branch = \"2.0\" }\n"
  },
  {
    "path": "tests/fixtures/old_lock/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Poetry Developer <developer@python-poetry.org>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\nsampleproject = \">=1.3.1\"\n\n[tool.poetry.group.dev.dependencies]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/old_lock_path_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Poetry Developer <developer@python-poetry.org>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\nquix = { path = \"./quix\", develop = true}\n\n[tool.poetry.group.dev.dependencies]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/old_lock_path_dependency/quix/pyproject.toml",
    "content": "[tool.poetry]\nname = \"quix\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\"Poetry Maintainer <tests@python-poetry.org>\"]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\nsampleproject = \">=1.3.1\"\n"
  },
  {
    "path": "tests/fixtures/outdated_lock/pyproject.toml",
    "content": "[project]\nname = \"foobar\"\nversion = \"0.1.0\"\nrequires-python = \">=3.8,<4.0\"\ndependencies = [\n    \"docker>=4.3.1\",\n]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/private_pyproject/pyproject.toml",
    "content": "[project]\nname = \"private\"\nversion = \"0.1.0\"\nrequires-python = \">=3.7,<4.0\"\nclassifiers = [\n    \"Private :: Do Not Upload\",\n]\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_application_plugin-1.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: my-application-plugin\nVersion: 1.0\nSummary: description\nRequires-Python: >=3.8,<4.0\nRequires-Dist: poetry (>=1.8.0,<3.0.0)\nRequires-Dist: some-lib (>=1.7.0,<3.0.0)\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_application_plugin-1.0.dist-info/entry_points.txt",
    "content": "[poetry.application.plugin]\nmy-command=my_application_plugin.plugins:MyApplicationPlugin\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_application_plugin-2.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: my-application-plugin\nVersion: 2.0\nSummary: description\nRequires-Python: >=3.8,<4.0\nRequires-Dist: poetry (>=1.8.0,<3.0.0)\nRequires-Dist: some-lib (>=1.7.0,<3.0.0)\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_application_plugin-2.0.dist-info/entry_points.txt",
    "content": "[poetry.application.plugin]\nmy-command=my_application_plugin.plugins:MyApplicationPlugin\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_other_plugin-1.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: my-other-plugin\nVersion: 1.0\nSummary: description\nRequires-Python: >=3.8,<4.0\nRequires-Dist: poetry (>=1.8.0,<3.0.0)\nRequires-Dist: some-lib (>=1.7.0,<3.0.0)\n"
  },
  {
    "path": "tests/fixtures/project_plugins/my_other_plugin-1.0.dist-info/entry_points.txt",
    "content": "[poetry.plugin]\nother-plugin=my_application_plugin.plugins:MyOtherPlugin\n"
  },
  {
    "path": "tests/fixtures/project_plugins/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\n\n[tool.poetry.requires-plugins]\nmy-application-plugin = \">=2.0\"\nmy-other-plugin = \">=1.0\"\n"
  },
  {
    "path": "tests/fixtures/project_plugins/some_lib-1.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: some-lib\nVersion: 1.0\nSummary: description\nRequires-Python: >=3.8,<4.0\n"
  },
  {
    "path": "tests/fixtures/project_plugins/some_lib-2.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: some-lib\nVersion: 2.0\nSummary: description\nRequires-Python: >=3.8,<4.0\n"
  },
  {
    "path": "tests/fixtures/project_with_extras/project_with_extras/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/project_with_extras/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-extras\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"*\"\npendulum = { version = \">=1.4.4\", optional = true }\ncachy = { version = \">=0.2.0\", optional = true }\n\n[tool.poetry.extras]\nextras_a = [ \"pendulum\" ]\nextras_b = [ \"cachy\" ]\n"
  },
  {
    "path": "tests/fixtures/project_with_git_dev_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\ncachy = \"^0.1.0\"\npendulum = \"^2.0.0\"\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\ndemo = { git = \"https://github.com/demo/demo.git\", rev = \"9cf87a285a2d3fbb0b9fa621997b3acc3631ed24\" }\n\n\n[tool.poetry.scripts]\nmy-script = \"my_package:main\"\n\n\n[tool.poetry.plugins.\"blogtool.parsers\"]\n\".rst\" = \"some_module::SomeClass\"\n"
  },
  {
    "path": "tests/fixtures/project_with_local_dependencies/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n# File dependency\ndemo = { path = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\n\n# Dir dependency with setup.py\nproject-with-setup = { path = \"../project_with_setup/\" }\n\n\n[tool.poetry.scripts]\nmy-script = \"my_package:main\"\n\n\n[tool.poetry.plugins.\"blogtool.parsers\"]\n\".rst\" = \"some_module::SomeClass\"\n"
  },
  {
    "path": "tests/fixtures/project_with_multi_constraints_dependency/project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/project_with_multi_constraints_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-multi-constraints-dependency\"\nversion = \"1.2.3\"\ndescription = \"This is a description\"\nauthors = [\"Your Name <you@example.com>\"]\nlicense = \"MIT\"\n\npackages = [\n    {include = \"project\"}\n]\n\n[tool.poetry.dependencies]\npython = \"*\"\npendulum = [\n    { version = \"^1.5\", python = \"<3.4\" },\n    { version = \"^2.0\", python = \"^3.4\" }\n]\n\n[tool.poetry.group.dev.dependencies]\n"
  },
  {
    "path": "tests/fixtures/project_with_nested_local/bar/pyproject.toml",
    "content": "[tool.poetry]\nname = \"bar\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\"Poetry Maintainer <tests@python-poetry.org>\"]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\nquix = { path = \"../quix\", develop = true }\n"
  },
  {
    "path": "tests/fixtures/project_with_nested_local/foo/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foo\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\"Poetry Maintainer <tests@python-poetry.org>\"]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\nbar = { path = \"../bar\", develop = true }\n"
  },
  {
    "path": "tests/fixtures/project_with_nested_local/pyproject.toml",
    "content": "[tool.poetry]\nname = \"project-with-nested-local\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\"Poetry Maintainer <tests@python-poetry.org>\"]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\nfoo = { path = \"./foo\", develop = true }\nbar = { path = \"./bar\", develop = true }\n"
  },
  {
    "path": "tests/fixtures/project_with_nested_local/quix/pyproject.toml",
    "content": "[tool.poetry]\nname = \"quix\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\"Poetry Maintainer <tests@python-poetry.org>\"]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n"
  },
  {
    "path": "tests/fixtures/project_with_setup/my_package/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/project_with_setup/setup.py",
    "content": "from __future__ import annotations\n\nfrom setuptools import setup\n\n\nkwargs = dict(\n    name=\"project-with-setup\",\n    license=\"MIT\",\n    version=\"0.1.2\",\n    description=\"Demo project.\",\n    author=\"Sébastien Eustace\",\n    author_email=\"sebastien@eustace.io\",\n    url=\"https://github.com/demo/demo\",\n    packages=[\"my_package\"],\n    install_requires=[\"pendulum>=1.4.4\", \"cachy[msgpack]>=0.2.0\"],\n)\n\n\nsetup(**kwargs)\n"
  },
  {
    "path": "tests/fixtures/project_with_setup_calls_script/my_package/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/project_with_setup_calls_script/pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools\", \"<scripts>\"]\nbuild-backend = \"setuptools.build_meta:__legacy__\"\n"
  },
  {
    "path": "tests/fixtures/project_with_setup_calls_script/setup.py",
    "content": "from __future__ import annotations\n\nimport subprocess\n\nfrom setuptools import setup\n\nif subprocess.call([\"exit-code\"]) != 42:\n    raise RuntimeError(\"Wrong exit code.\")\n\nkwargs = dict(\n    name=\"project-with-setup-calls-script\",\n    license=\"MIT\",\n    version=\"0.1.2\",\n    description=\"Demo project.\",\n    author=\"Sébastien Eustace\",\n    author_email=\"sebastien@eustace.io\",\n    url=\"https://github.com/demo/demo\",\n    packages=[\"my_package\"],\n    install_requires=[\"pendulum>=1.4.4\", \"cachy[msgpack]>=0.2.0\"],\n)\n\n\nsetup(**kwargs)\n"
  },
  {
    "path": "tests/fixtures/pypi_reference/pyproject.toml",
    "content": "[project]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\n    { name = \"Poetry Developer\", email =  \"<developer@python-poetry.org>\" }\n]\ndynamic = [\"dependencies\", \"requires-python\"]\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\ndocker = { version = \">=4.3.1\", source = \"PyPI\" }\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/sample_project/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/sample_project/pyproject.toml",
    "content": "[tool.poetry]\nname = \"sample-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.6\"\ncleo = \"^0.6\"\npendulum = { git = \"https://github.com/sdispater/pendulum.git\", branch = \"2.0\" }\nrequests = { version = \"^2.18\", optional = true, extras=[ \"security\" ] }\npathlib2 = { version = \"^2.2\", python = \"~2.7\" }\n\norator = { version = \"^0.9\", optional = true }\n\n# File dependency\ndemo = { path = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\n\n# Dir dependency with setup.py\nmy-package = { path = \"../project_with_setup/\" }\n\n# Dir dependency with pyproject.toml\nsimple-project = { path = \"../simple_project/\" }\n\n# Dependency with markers\nfunctools32 = { version = \"^3.2.3\", markers = \"python_version ~= '2.7' and sys_platform == 'win32' or python_version in '3.4 3.5'\" }\n\n\n[tool.poetry.extras]\ndb = [ \"orator\" ]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n\n\n[tool.poetry.scripts]\nmy-script = \"sample_project:main\"\n\n\n[tool.poetry.plugins.\"blogtool.parsers\"]\n\".rst\" = \"some_module::SomeClass\"\n"
  },
  {
    "path": "tests/fixtures/scripts/README.md",
    "content": ""
  },
  {
    "path": "tests/fixtures/scripts/pyproject.toml",
    "content": "[tool.poetry]\nname = \"scripts\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[tool.poetry.scripts]\ncheck-argv0 = \"scripts.check_argv0:main\"\nexit-code = \"scripts.exit_code:main\"\nreturn-code = \"scripts.return_code:main\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/scripts/scripts/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/scripts/scripts/check_argv0.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\n\n\ndef main() -> int:\n    path = Path(sys.argv[0])\n    if sys.argv[1] == \"absolute\":\n        if not path.is_absolute():\n            raise RuntimeError(f\"sys.argv[0] is not an absolute path: {path}\")\n        if not path.exists():\n            raise RuntimeError(f\"sys.argv[0] does not exist: {path}\")\n    else:\n        if path.is_absolute():\n            raise RuntimeError(f\"sys.argv[0] is an absolute path: {path}\")\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    raise sys.exit(main())\n"
  },
  {
    "path": "tests/fixtures/scripts/scripts/exit_code.py",
    "content": "from __future__ import annotations\n\n\ndef main() -> None:\n    raise SystemExit(42)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/fixtures/scripts/scripts/return_code.py",
    "content": "from __future__ import annotations\n\n\ndef main() -> int:\n    return 42\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n"
  },
  {
    "path": "tests/fixtures/self_version_not_ok/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\nrequires-poetry = \"<1.2\"\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\n"
  },
  {
    "path": "tests/fixtures/self_version_not_ok_invalid_config/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\nrequires-poetry = \"<1.2\"\ninvalid_option = 42\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\n"
  },
  {
    "path": "tests/fixtures/self_version_ok/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\nrequires-poetry = \">=1.2\"\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\n"
  },
  {
    "path": "tests/fixtures/simple_project/LICENSE",
    "content": "license ...\n"
  },
  {
    "path": "tests/fixtures/simple_project/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/simple_project/pyproject.toml",
    "content": "[project]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    { name = \"Sébastien Eustace\", email = \"sebastien@eustace.io\" }\n]\nlicense = \"MIT\"\nreadme = \"README.rst\"\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\ndynamic = [ \"classifiers\", \"dependencies\", \"requires-python\" ]\n\n[project.urls]\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\n[project.scripts]\nfoo = \"foo:bar\"\nbaz = \"bar:baz.boom.bim\"\nfox = \"fuz.foo:bar.baz\"\n\n[tool.poetry]\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/simple_project/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/simple_project_legacy/LICENSE",
    "content": "license ...\n"
  },
  {
    "path": "tests/fixtures/simple_project_legacy/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/simple_project_legacy/pyproject.toml",
    "content": "[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = [\"README.rst\"]\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[tool.poetry.scripts]\nfoo = \"foo:bar\"\nbaz = \"bar:baz.boom.bim\"\nfox = \"fuz.foo:bar.baz\"\n\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/simple_project_legacy/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/up_to_date_lock/pyproject.toml",
    "content": "[project]\nname = \"foobar\"\nversion = \"0.1.0\"\nrequires-python = \">=3.8,<4.0\"\ndependencies = [\n    \"docker>=4.3.1\",\n]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/up_to_date_lock_non_package/pyproject.toml",
    "content": "[tool.poetry]\npackage-mode = false\n\n[tool.poetry.dependencies]\npython = \"^3.8\"\ndocker = \">=4.3.1\"\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/with-include/LICENSE",
    "content": "Copyright (c) 2018 Sébastien Eustace\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "tests/fixtures/with-include/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/with-include/extra_dir/README.md",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/extra_dir/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/extra_dir/sub_pkg/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/extra_dir/sub_pkg/vcs_excluded.txt",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/extra_dir/vcs_excluded.txt",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/for_wheel_only/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/my_module.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/notes.txt",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/package_with_include/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "tests/fixtures/with-include/pyproject.toml",
    "content": "[tool.poetry]\nname = \"with-include\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org/\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\npackages = [\n    { include = \"extra_dir/**/*.py\" },\n    { include = \"extra_dir/**/*.py\" },\n    { include = \"my_module.py\" },\n    { include = \"package_with_include\" },\n    { include = \"tests\", format = \"sdist\" },\n    { include = \"for_wheel_only\", format = [\"wheel\"] },\n    { include = \"src_package\", from = \"src\"},\n]\n\ninclude = [\n    \"extra_dir/vcs_excluded.txt\",\n    \"notes.txt\"\n]\n\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\ncleo = \"^0.6\"\ncachy = { version = \"^0.2.0\", extras = [\"msgpack\"] }\n\npendulum = { version = \"^1.4\", optional = true }\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n\n[tool.poetry.extras]\ntime = [\"pendulum\"]\n\n[tool.poetry.scripts]\nmy-script = \"my_package:main\"\nmy-2nd-script = \"my_package:main2\"\n"
  },
  {
    "path": "tests/fixtures/with-include/src/src_package/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with-include/tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with_conditional_path_deps/demo_one/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n"
  },
  {
    "path": "tests/fixtures/with_conditional_path_deps/demo_two/pyproject.toml",
    "content": "[tool.poetry]\nname = \"demo\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n"
  },
  {
    "path": "tests/fixtures/with_conditional_path_deps/pyproject.toml",
    "content": "[tool.poetry]\nname = \"sample\"\nversion = \"1.0.0\"\ndescription = \"Sample Project\"\nauthors = []\nlicense = \"MIT\"\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\ndemo = [\n    { path = \"demo_one\", platform = \"linux\" },\n    { path = \"demo_two\", platform = \"win32\" },\n]\n"
  },
  {
    "path": "tests/fixtures/with_explicit_pypi_and_other/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\n\n[[tool.poetry.source]]\nname = \"PyPI\"\npriority = \"explicit\"\n"
  },
  {
    "path": "tests/fixtures/with_explicit_pypi_and_other_explicit/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"explicit\"\nurl = \"https://explicit.com/simple/\"\npriority = \"explicit\"\n\n[[tool.poetry.source]]\nname = \"PyPI\"\npriority = \"explicit\"\n"
  },
  {
    "path": "tests/fixtures/with_explicit_pypi_no_other/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"PyPI\"\npriority = \"explicit\"\n"
  },
  {
    "path": "tests/fixtures/with_explicit_source/pyproject.toml",
    "content": "[tool.poetry]\nname = \"with-explicit-source\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"explicit\"\nurl = \"https://explicit.com/simple/\"\npriority = \"explicit\"\n"
  },
  {
    "path": "tests/fixtures/with_local_config/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/with_local_config/poetry.toml",
    "content": "[virtualenvs]\nin-project = false\ncreate = false\n"
  },
  {
    "path": "tests/fixtures/with_local_config/pyproject.toml",
    "content": "[tool.poetry]\nname = \"local-config\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.6\"\ncleo = \"^0.6\"\npendulum = { git = \"https://github.com/sdispater/pendulum.git\", branch = \"2.0\" }\nrequests = { version = \"^2.18\", optional = true, extras=[ \"security\" ] }\npathlib2 = { version = \"^2.2\", python = \"~2.7\" }\n\norator = { version = \"^0.9\", optional = true }\n\n# File dependency\ndemo = { path = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\n\n# Dir dependency with setup.py\nmy-package = { path = \"../project_with_setup/\" }\n\n# Dir dependency with pyproject.toml\nsimple-project = { path = \"../simple_project/\" }\n\n\n[tool.poetry.extras]\ndb = [ \"orator\" ]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n\n\n[tool.poetry.scripts]\nmy-script = \"local_config:main\"\n\n\n[tool.poetry.plugins.\"blogtool.parsers\"]\n\".rst\" = \"some_module::SomeClass\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_dist_dir/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/with_multiple_dist_dir/pyproject.toml",
    "content": "[tool.poetry]\nname = \"simple-project\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = [\"README.rst\"]\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"~2.7 || ^3.4\"\n\n[tool.poetry.scripts]\nfoo = \"foo:bar\"\nbaz = \"bar:baz.boom.bim\"\nfox = \"fuz.foo:bar.baz\"\n\n\n[build-system]\nrequires = [\"poetry-core>=1.1.0a7\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_dist_dir/simple_project/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fixtures/with_multiple_readme_files/README-1.rst",
    "content": "Single Python\n=============\n"
  },
  {
    "path": "tests/fixtures/with_multiple_readme_files/README-2.rst",
    "content": "Changelog\n=========\n"
  },
  {
    "path": "tests/fixtures/with_multiple_readme_files/my_package/__init__.py",
    "content": "\"\"\"Example module\"\"\"\n\nfrom __future__ import annotations\n\n\n__version__ = \"0.1\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_readme_files/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"0.1\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\nreadme = [\n    \"README-1.rst\",\n    \"README-2.rst\"\n]\n\nhomepage = \"https://python-poetry.org\"\n\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_sources/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"supplemental\"\n\n[[tool.poetry.source]]\nname = \"bar\"\nurl = \"https://bar.baz/simple/\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_sources_pypi/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"supplemental\"\n\n[[tool.poetry.source]]\nname = \"bar\"\nurl = \"https://bar.baz/simple/\"\n\n[[tool.poetry.source]]\nname = \"PyPI\"\n\n[[tool.poetry.source]]\nname = \"baz\"\nurl = \"https://baz.bar/simple/\"\n"
  },
  {
    "path": "tests/fixtures/with_multiple_supplemental_sources/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"supplemental\"\n\n[[tool.poetry.source]]\nname = \"bar\"\nurl = \"https://bar.baz/simple/\"\npriority = \"supplemental\"\n"
  },
  {
    "path": "tests/fixtures/with_path_dependency/bazz/pyproject.toml",
    "content": "[tool.poetry]\nname = \"bazz\"\nversion = \"1\"\ndescription = \"Demo package\"\nauthors = [\"Poetry Team <noreply@python-poetry.org>\"]\nlicense = \"MIT\"\nreadme = \"README.md\"\npackages = [\n    { include = \"demo\", from = \"src\" }\n]\n\n[tool.poetry.dependencies]\npython = \"*\"\nrequests = \"~=2.25.1\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/with_path_dependency/pyproject.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = []\nreadme = \"README.md\"\npackages = [{include = \"foobar\"}]\n\n[tool.poetry.dependencies]\npython = \"^3.9\"\nbazz = { path = \"./bazz\", develop = true }\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/fixtures/with_primary_source_explicit/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\npriority = \"primary\"\n"
  },
  {
    "path": "tests/fixtures/with_primary_source_implicit/pyproject.toml",
    "content": "[tool.poetry]\nname = \"my-package\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\n"
  },
  {
    "path": "tests/fixtures/with_source/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/fixtures/with_source/pyproject.toml",
    "content": "[tool.poetry]\nname = \"with-source\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.rst\"\n\nhomepage = \"https://python-poetry.org\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\nclassifiers = [\n    \"Topic :: Software Development :: Build Tools\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\ncleo = \"^0.6\"\npendulum = { git = \"https://github.com/sdispater/pendulum.git\", branch = \"2.0\" }\nrequests = { version = \"^2.18\", optional = true, extras=[ \"security\" ] }\npathlib2 = { version = \"^2.2\", python = \"~3.6\" }\n\norator = { version = \"^0.9\", optional = true }\n\n# File dependency\ndemo = { path = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\n\n# Dir dependency with setup.py\nmy-package = { path = \"../project_with_setup/\" }\n\n# Dir dependency with pyproject.toml\nsimple-project = { path = \"../simple_project/\" }\n\n\n[tool.poetry.extras]\ndb = [ \"orator\" ]\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n\n\n[tool.poetry.scripts]\nmy-script = \"with_default_source:main\"\n\n\n[tool.poetry.plugins.\"blogtool.parsers\"]\n\".rst\" = \"some_module::SomeClass\"\n\n\n[[tool.poetry.source]]\nname = \"foo\"\nurl = \"https://foo.bar/simple/\"\n"
  },
  {
    "path": "tests/fixtures/with_supplemental_source/pyproject.toml",
    "content": "[tool.poetry]\nname = \"with-explicit-source\"\nversion = \"1.2.3\"\ndescription = \"Some description.\"\nauthors = [\n    \"Your Name <you@example.com>\"\n]\nlicense = \"MIT\"\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\n\n[[tool.poetry.source]]\nname = \"supplemental\"\nurl = \"https://supplemental.com/simple/\"\npriority = \"supplemental\"\n"
  },
  {
    "path": "tests/helpers.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport re\nimport shutil\n\nfrom importlib import metadata\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport keyring\n\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.utils.link import Link\nfrom poetry.core.vcs.git import ParsedUrl\n\nfrom poetry.config.config import Config\nfrom poetry.console.application import Application\nfrom poetry.factory import Factory\nfrom poetry.installation.executor import Executor\nfrom poetry.packages import Locker\nfrom poetry.repositories import Repository\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.utils.password_manager import PoetryKeyring\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from collections.abc import Mapping\n    from typing import Any\n\n    import responses\n\n    from keyring.backend import KeyringBackend\n    from poetry.core.constraints.version import Version\n    from poetry.core.packages.dependency import Dependency\n    from pytest_mock import MockerFixture\n    from requests import PreparedRequest\n    from tomlkit.toml_document import TOMLDocument\n\n    from poetry.installation.operations.operation import Operation\n    from poetry.poetry import Poetry\n    from tests.types import HttpResponse\n\nFIXTURE_PATH = Path(__file__).parent / \"fixtures\"\nFIXTURE_PATH_INSTALLATION = Path(__file__).parent / \"installation\" / \"fixtures\"\nFIXTURE_PATH_DISTRIBUTIONS = FIXTURE_PATH / \"distributions\"\nFIXTURE_PATH_REPOSITORIES = Path(__file__).parent / \"repositories\" / \"fixtures\"\nFIXTURE_PATH_REPOSITORIES_LEGACY = FIXTURE_PATH_REPOSITORIES / \"legacy\"\nFIXTURE_PATH_REPOSITORIES_PYPI = FIXTURE_PATH_REPOSITORIES / \"pypi.org\"\n\n# Used as a mock for latest git revision.\nMOCK_DEFAULT_GIT_REVISION = \"9cf87a285a2d3fbb0b9fa621997b3acc3631ed24\"\n\n\ndef get_package(\n    name: str, version: str | Version, yanked: str | bool = False\n) -> Package:\n    return Package(name, version, yanked=yanked)\n\n\ndef get_dependency(\n    name: str,\n    constraint: str | dict[str, Any] | None = None,\n    groups: list[str] | None = None,\n    optional: bool = False,\n    allows_prereleases: bool = False,\n) -> Dependency:\n    if constraint is None:\n        constraint = \"*\"\n\n    if isinstance(constraint, str):\n        constraint = {\"version\": constraint}\n\n    constraint[\"optional\"] = optional\n    constraint[\"allow-prereleases\"] = allows_prereleases\n\n    return Factory.create_dependency(name, constraint or \"*\", groups=groups)\n\n\ndef copy_path(source: Path, dest: Path) -> None:\n    if dest.is_dir():\n        shutil.rmtree(dest)\n    else:\n        dest.unlink(missing_ok=True)\n\n    if source.is_dir():\n        shutil.copytree(source, dest)\n    else:\n        shutil.copyfile(source, dest)\n\n\nclass MockDulwichRepo:\n    def __init__(self, root: Path | str, **__: Any) -> None:\n        self.path = str(root)\n\n    def head(self) -> bytes:\n        return MOCK_DEFAULT_GIT_REVISION.encode()\n\n\ndef mock_clone(\n    url: str,\n    *_: Any,\n    source_root: Path | None = None,\n    **__: Any,\n) -> MockDulwichRepo:\n    # Checking source to determine which folder we need to copy\n    parsed = ParsedUrl.parse(url)\n    assert parsed.pathname is not None\n    path = re.sub(r\"(.git)?$\", \"\", parsed.pathname.lstrip(\"/\"))\n\n    assert parsed.resource is not None\n    folder = FIXTURE_PATH / \"git\" / parsed.resource / path\n    assert folder.is_dir()\n\n    if not source_root:\n        source_root = Path(Config.create().get(\"cache-dir\")) / \"src\"\n\n    assert parsed.name is not None\n    dest = source_root / parsed.name\n    dest.mkdir(parents=True, exist_ok=True)\n\n    copy_path(folder, dest)\n    return MockDulwichRepo(dest)\n\n\nclass TestExecutor(Executor):\n    # class name begins 'Test': tell pytest that it does not contain testcases.\n    __test__ = False\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        super().__init__(*args, **kwargs)\n\n        self._installs: list[Package] = []\n        self._updates: list[Package] = []\n        self._uninstalls: list[Package] = []\n\n    @property\n    def installations(self) -> list[Package]:\n        return self._installs\n\n    @property\n    def updates(self) -> list[Package]:\n        return self._updates\n\n    @property\n    def removals(self) -> list[Package]:\n        return self._uninstalls\n\n    def _do_execute_operation(self, operation: Operation) -> int:\n        rc = super()._do_execute_operation(operation)\n\n        if not operation.skipped:\n            getattr(self, f\"_{operation.job_type}s\").append(operation.package)\n\n        return rc\n\n    def _execute_install(self, operation: Operation) -> int:\n        return 0\n\n    def _execute_update(self, operation: Operation) -> int:\n        return 0\n\n    def _execute_remove(self, operation: Operation) -> int:\n        return 0\n\n\nclass PoetryTestApplication(Application):\n    def __init__(self, poetry: Poetry) -> None:\n        super().__init__()\n        self._poetry = poetry\n\n    def reset_poetry(self) -> None:\n        assert self._poetry is not None\n        poetry = self._poetry\n        self._poetry = Factory().create_poetry(self._poetry.file.path.parent)\n        self._poetry.set_pool(poetry.pool)\n        self._poetry.set_config(poetry.config)\n        self._poetry.set_locker(\n            TestLocker(poetry.locker.lock, self._poetry.pyproject.data)\n        )\n\n\nclass TestLocker(Locker):\n    # class name begins 'Test': tell pytest that it does not contain testcases.\n    __test__ = False\n\n    def __init__(self, lock: Path, pyproject_data: dict[str, Any]) -> None:\n        super().__init__(lock, pyproject_data)\n        self._locked = False\n        self._write = False\n\n    def write(self, write: bool = True) -> None:\n        self._write = write\n\n    def is_locked(self) -> bool:\n        return self._locked\n\n    def locked(self, is_locked: bool = True) -> TestLocker:\n        self._locked = is_locked\n\n        return self\n\n    def mock_lock_data(self, data: dict[str, Any]) -> None:\n        self.locked()\n\n        self._lock_data = data\n\n    def is_fresh(self) -> bool:\n        return True\n\n    def _write_lock_data(self, data: TOMLDocument) -> None:\n        if self._write:\n            super()._write_lock_data(data)\n            self._locked = True\n            return\n\n        self._lock_data = data\n\n\nclass TestRepository(Repository):\n    def find_packages(self, dependency: Dependency) -> list[Package]:\n        packages = super().find_packages(dependency)\n        if len(packages) == 0:\n            raise PackageNotFoundError(f\"Package [{dependency.name}] not found.\")\n\n        return packages\n\n    def find_links_for_package(self, package: Package) -> list[Link]:\n        return [\n            Link(\n                f\"https://foo.bar/files/{package.name.replace('-', '_')}\"\n                f\"-{package.version.to_string()}-py2.py3-none-any.whl\"\n            )\n        ]\n\n\n@contextlib.contextmanager\ndef isolated_environment(\n    environ: dict[str, Any] | None = None, clear: bool = False\n) -> Iterator[None]:\n    original_environ = dict(os.environ)\n\n    if clear:\n        os.environ.clear()\n\n    if environ:\n        os.environ.update(environ)\n\n    yield\n\n    os.environ.clear()\n    os.environ.update(original_environ)\n\n\ndef make_entry_point_from_plugin(\n    name: str, cls: type[Any], dist: metadata.Distribution | None = None\n) -> metadata.EntryPoint:\n    group: str | None = getattr(cls, \"group\", None)\n    ep: metadata.EntryPoint = metadata.EntryPoint(\n        name=name,\n        group=group,  # type: ignore[arg-type]\n        value=f\"{cls.__module__}:{cls.__name__}\",\n    )\n\n    if dist:\n        ep = ep._for(dist)  # type: ignore[attr-defined,no-untyped-call]\n        return ep\n\n    return ep\n\n\ndef mock_metadata_entry_points(\n    mocker: MockerFixture,\n    cls: type[Any],\n    name: str = \"my-plugin\",\n    dist: metadata.Distribution | None = None,\n) -> None:\n    def patched_entry_points(*args: Any, **kwargs: Any) -> list[metadata.EntryPoint]:\n        if \"group\" in kwargs and kwargs[\"group\"] != getattr(cls, \"group\", None):\n            return []\n        return [make_entry_point_from_plugin(name, cls, dist)]\n\n    mocker.patch.object(\n        metadata,\n        \"entry_points\",\n        side_effect=patched_entry_points,\n    )\n\n\ndef flatten_dict(obj: Mapping[str, Any], delimiter: str = \".\") -> Mapping[str, Any]:\n    \"\"\"\n    Flatten a nested dict.\n\n    A flatdict replacement.\n\n    :param obj: A nested dict to be flattened\n    :delimiter str: A delimiter used in the key path\n    :return: Flattened dict\n    \"\"\"\n\n    def recurse_keys(obj: Mapping[str, Any]) -> Iterator[tuple[list[str], Any]]:\n        \"\"\"\n        A recursive generator to yield key paths and their values\n\n        :param obj: A nested dict to be flattened\n        :return:  dict\n        \"\"\"\n        if isinstance(obj, dict):\n            for key in obj:\n                for leaf in recurse_keys(obj[key]):\n                    leaf_path, leaf_value = leaf\n                    leaf_path.insert(0, key)\n                    yield (leaf_path, leaf_value)\n        else:\n            yield ([], obj)\n\n    return {delimiter.join(path): value for path, value in recurse_keys(obj)}\n\n\ndef http_setup_redirect(\n    http: responses.RequestsMock, *methods: str, status_code: int = 301\n) -> None:\n    redirect_uri_regex = re.compile(r\"^(?P<protocol>https?)://redirect\\.(?P<uri>.*)$\")\n\n    def redirect_request_callback(request: PreparedRequest) -> HttpResponse:\n        assert request.url\n        redirect_uri_match = redirect_uri_regex.match(request.url)\n        assert redirect_uri_match is not None\n        redirect_uri = f\"{redirect_uri_match.group('protocol')}://{redirect_uri_match.group('uri')}\"\n        return status_code, {\"Location\": redirect_uri}, b\"\"\n\n    for method in methods:\n        http.add_callback(\n            method,\n            redirect_uri_regex,\n            callback=redirect_request_callback,\n        )\n\n\n@contextlib.contextmanager\ndef switch_working_directory(path: Path, remove: bool = False) -> Iterator[Path]:\n    original_cwd = Path.cwd()\n    os.chdir(path)\n\n    try:\n        yield path\n    finally:\n        os.chdir(original_cwd)\n\n        if remove:\n            shutil.rmtree(path, ignore_errors=True)\n\n\n@contextlib.contextmanager\ndef with_working_directory(source: Path, target: Path | None = None) -> Iterator[Path]:\n    use_copy = target is not None\n\n    if use_copy:\n        assert target is not None\n        shutil.copytree(source, target)\n\n    with switch_working_directory(target or source, remove=use_copy) as path:\n        yield path\n\n\ndef set_keyring_backend(backend: KeyringBackend) -> None:\n    \"\"\"Clears availability cache and sets the specified keyring backend.\"\"\"\n    PoetryKeyring.is_available.cache_clear()\n    keyring.set_keyring(backend)\n\n\ndef pbs_installer_supported_arch(architecture: str) -> bool:\n    # Based on pbs_installer._versions and pbs_installer._utils.ARCH_MAPPING\n    supported_archs = [\"arm64\", \"aarch64\", \"amd64\", \"x86_64\", \"i686\", \"x86\"]\n    return architecture.lower() in supported_archs\n"
  },
  {
    "path": "tests/inspection/__init__.py",
    "content": ""
  },
  {
    "path": "tests/inspection/test_info.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport shutil\nimport uuid\n\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom zipfile import ZipFile\n\nimport pytest\n\nfrom build import BuildBackendException\nfrom build import ProjectBuilder\nfrom packaging.metadata import parse_email\nfrom pkginfo.distribution import NewMetadataVersion\n\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.inspection.info import PackageInfoError\nfrom poetry.utils.env import VirtualEnv\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n    from pathlib import Path\n\n    from packaging.metadata import RawMetadata\n    from pytest_mock import MockerFixture\n\n    from tests.types import FixtureDirGetter\n    from tests.types import SetProjectContext\n\n\n@pytest.fixture\ndef demo_sdist(fixture_dir: FixtureDirGetter) -> Path:\n    return fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n\n\n@pytest.fixture\ndef demo_wheel(fixture_dir: FixtureDirGetter) -> Path:\n    return fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n\n\n@pytest.fixture\ndef demo_wheel_metadata(demo_wheel: Path) -> RawMetadata:\n    with ZipFile(demo_wheel) as zf:\n        metadata, _ = parse_email(zf.read(\"demo-0.1.0.dist-info/METADATA\"))\n    return metadata\n\n\n@pytest.fixture\ndef source_dir(tmp_path: Path) -> Path:\n    path = tmp_path / \"source\"\n    path.mkdir()\n    return path\n\n\n@pytest.fixture\ndef demo_setup(source_dir: Path) -> Path:\n    setup_py = source_dir / \"setup.py\"\n    setup_py.write_text(\n        \"from setuptools import setup; \"\n        'setup(name=\"demo\", '\n        'version=\"0.1.0\", '\n        'install_requires=[\"package\"])',\n        encoding=\"utf-8\",\n    )\n    return source_dir\n\n\n@pytest.fixture\ndef demo_setup_cfg(source_dir: Path) -> Path:\n    setup_cfg = source_dir / \"setup.cfg\"\n    setup_cfg.write_text(\n        \"\\n\".join(\n            [\n                \"[metadata]\",\n                \"name = demo\",\n                \"version = 0.1.0\",\n                \"[options]\",\n                \"install_requires = package\",\n            ]\n        ),\n        encoding=\"utf-8\",\n    )\n    return source_dir\n\n\n@pytest.fixture\ndef demo_setup_complex(source_dir: Path) -> Path:\n    setup_py = source_dir / \"setup.py\"\n    setup_py.write_text(\n        \"from setuptools import setup; \"\n        'setup(name=\"demo\", '\n        'version=\"0.1.0\", '\n        'install_requires=[i for i in [\"package\"]])',\n        encoding=\"utf-8\",\n    )\n    return source_dir\n\n\n@pytest.fixture\ndef demo_setup_complex_pep517_legacy(demo_setup_complex: Path) -> Path:\n    pyproject_toml = demo_setup_complex / \"pyproject.toml\"\n    pyproject_toml.write_text(\n        '[build-system]\\nrequires = [\"setuptools\", \"wheel\"]', encoding=\"utf-8\"\n    )\n    return demo_setup_complex\n\n\n@pytest.fixture\ndef demo_setup_complex_calls_script(\n    fixture_dir: FixtureDirGetter, source_dir: Path, tmp_path: Path\n) -> Path:\n    # make sure the scripts project is on the same drive (for Windows tests in CI)\n    scripts_dir = tmp_path / \"scripts\"\n    shutil.copytree(fixture_dir(\"scripts\"), scripts_dir)\n\n    pyproject = source_dir / \"pyproject.toml\"\n    pyproject.write_text(\n        f\"\"\"\\\n    [build-system]\n    requires = [\"setuptools\", \"scripts @ {scripts_dir.as_uri()}\"]\n    build-backend = \"setuptools.build_meta:__legacy__\"\n\"\"\",\n        encoding=\"utf-8\",\n    )\n\n    setup_py = source_dir / \"setup.py\"\n    setup_py.write_text(\n        \"\"\"\\\nimport subprocess\nfrom setuptools import setup\nif subprocess.call([\"exit-code\"]) != 42:\n    raise RuntimeError(\"Wrong exit code.\")\nsetup(name=\"demo\", version=\"0.1.0\", install_requires=[i for i in [\"package\"]])\n\"\"\",\n        encoding=\"utf-8\",\n    )\n\n    return source_dir\n\n\n@pytest.fixture(autouse=True)\ndef use_project_context(set_project_context: SetProjectContext) -> Iterator[None]:\n    with set_project_context(\"sample_project\"):\n        yield\n\n\ndef demo_check_info(info: PackageInfo, requires_dist: set[str] | None = None) -> None:\n    assert info.name == \"demo\"\n    assert info.version == \"0.1.0\"\n    assert info.requires_dist\n\n    if requires_dist:\n        assert set(info.requires_dist) == requires_dist\n    else:\n        # Exact formatting various according to the exact mechanism used to extract the\n        # metadata.\n        assert set(info.requires_dist) in (\n            {\n                'cleo; extra == \"foo\"',\n                \"pendulum (>=1.4.4)\",\n                'tomlkit; extra == \"bar\"',\n            },\n            {\n                'cleo ; extra == \"foo\"',\n                \"pendulum (>=1.4.4)\",\n                'tomlkit ; extra == \"bar\"',\n            },\n            {\n                'cleo ; extra == \"foo\"',\n                \"pendulum>=1.4.4\",\n                'tomlkit ; extra == \"bar\"',\n            },\n            {\n                \"cleo ; extra == 'foo'\",\n                \"pendulum (>=1.4.4)\",\n                \"tomlkit ; extra == 'bar'\",\n            },\n        )\n\n\ndef test_info_from_sdist(demo_sdist: Path) -> None:\n    info = PackageInfo.from_sdist(demo_sdist)\n    demo_check_info(info)\n    assert info._source_type == \"file\"\n    assert info._source_url == demo_sdist.resolve().as_posix()\n\n\ndef test_info_from_sdist_no_pkg_info(fixture_dir: FixtureDirGetter) -> None:\n    path = fixture_dir(\"distributions\") / \"demo_no_pkg_info-0.1.0.tar.gz\"\n    info = PackageInfo.from_sdist(path)\n    demo_check_info(info)\n    assert info._source_type == \"file\"\n    assert info._source_url == path.resolve().as_posix()\n\n\ndef test_info_from_wheel(demo_wheel: Path) -> None:\n    info = PackageInfo.from_wheel(demo_wheel)\n    demo_check_info(info)\n    assert info._source_type == \"file\"\n    assert info._source_url == demo_wheel.resolve().as_posix()\n\n\n@pytest.mark.parametrize(\"version\", [\"23\", \"24\", \"299\"])\ndef test_info_from_wheel_metadata_versions(\n    version: str, fixture_dir: FixtureDirGetter\n) -> None:\n    path = (\n        fixture_dir(\"distributions\")\n        / f\"demo_metadata_version_{version}-0.1.0-py2.py3-none-any.whl\"\n    )\n    with (\n        pytest.warns(NewMetadataVersion)\n        if version == \"299\"\n        else contextlib.nullcontext()\n    ):\n        info = PackageInfo.from_wheel(path)\n    demo_check_info(info)\n    assert info._source_type == \"file\"\n    assert info._source_url == path.resolve().as_posix()\n\n\ndef test_info_from_wheel_metadata_version_unknown(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    path = (\n        fixture_dir(\"distributions\")\n        / \"demo_metadata_version_unknown-0.1.0-py2.py3-none-any.whl\"\n    )\n\n    with pytest.warns(NewMetadataVersion), pytest.raises(PackageInfoError) as e:\n        PackageInfo.from_wheel(path)\n\n    assert \"Unknown metadata version: 999.3\" in str(e.value)\n\n\ndef test_info_from_wheel_metadata(demo_wheel_metadata: RawMetadata) -> None:\n    info = PackageInfo.from_metadata(demo_wheel_metadata)\n    demo_check_info(info)\n    assert info.requires_python == \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\n    assert info._source_type is None\n    assert info._source_url is None\n\n\ndef test_info_from_wheel_metadata_incomplete() -> None:\n    \"\"\"\n    To avoid differences in cached metadata,\n    it is important that the representation of missing fields does not change!\n    \"\"\"\n    metadata, _ = parse_email(b\"Metadata-Version: 2.1\\nName: demo\\nVersion: 0.1.0\\n\")\n    info = PackageInfo.from_metadata(metadata)\n    assert info.name == \"demo\"\n    assert info.version == \"0.1.0\"\n    assert info.summary is None\n    assert info.requires_dist is None\n    assert info.requires_python is None\n\n\ndef test_info_from_bdist(demo_wheel: Path) -> None:\n    info = PackageInfo.from_bdist(demo_wheel)\n    demo_check_info(info)\n    assert info._source_type == \"file\"\n    assert info._source_url == demo_wheel.resolve().as_posix()\n\n\ndef test_info_from_poetry_directory(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_directory(fixture_dir(\"inspection\") / \"demo\")\n    demo_check_info(info)\n\n\ndef test_info_from_poetry_directory_fallback_on_poetry_create_error(\n    mocker: MockerFixture, fixture_dir: FixtureDirGetter\n) -> None:\n    mock_create_poetry = mocker.patch(\n        \"poetry.inspection.info.Factory.create_poetry\", side_effect=RuntimeError\n    )\n    mock_get_poetry_package = mocker.spy(PackageInfo, \"_get_poetry_package\")\n    mock_get_pep517_metadata = mocker.patch(\n        \"poetry.inspection.info.get_pep517_metadata\"\n    )\n\n    PackageInfo.from_directory(fixture_dir(\"inspection\") / \"demo_poetry_package\")\n\n    assert mock_create_poetry.call_count == 1\n    assert mock_get_poetry_package.call_count == 1\n    assert mock_get_pep517_metadata.call_count == 1\n\n\ndef test_info_from_requires_txt(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_metadata_directory(\n        fixture_dir(\"inspection\") / \"demo_only_requires_txt.egg-info\"\n    )\n    assert info is not None\n    demo_check_info(info)\n\n\ndef test_info_no_setup_pkg_info_no_deps(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_metadata_directory(\n        fixture_dir(\"inspection\") / \"demo_no_setup_pkg_info_no_deps\"\n    )\n    assert info is not None\n    assert info.name == \"demo\"\n    assert info.version == \"0.1.0\"\n    assert info.requires_dist is None\n\n\ndef test_info_no_setup_pkg_info_no_deps_for_sure(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_metadata_directory(\n        fixture_dir(\"inspection\") / \"demo_no_setup_pkg_info_no_deps_for_sure\",\n    )\n    assert info is not None\n    assert info.name == \"demo\"\n    assert info.version == \"0.1.0\"\n    assert info.requires_dist == []\n\n\ndef test_info_no_setup_pkg_info_no_deps_dynamic(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_metadata_directory(\n        fixture_dir(\"inspection\") / \"demo_no_setup_pkg_info_no_deps_dynamic\",\n    )\n    assert info is not None\n    assert info.name == \"demo\"\n    assert info.version == \"0.1.0\"\n    assert info.requires_dist is None\n\n\ndef test_info_setup_simple(mocker: MockerFixture, demo_setup: Path) -> None:\n    spy = mocker.spy(VirtualEnv, \"run\")\n    info = PackageInfo.from_directory(demo_setup)\n\n    assert spy.call_count == 6\n    demo_check_info(info, requires_dist={\"package\"})\n\n\ndef test_info_setup_complex(demo_setup_complex: Path) -> None:\n    info = PackageInfo.from_directory(demo_setup_complex)\n    demo_check_info(info, requires_dist={\"package\"})\n\n\ndef test_info_setup_complex_pep517_error(\n    mocker: MockerFixture, demo_setup_complex: Path\n) -> None:\n    output = uuid.uuid4().hex\n    mocker.patch(\n        \"build.ProjectBuilder.from_isolated_env\",\n        autospec=True,\n        side_effect=BuildBackendException(CalledProcessError(1, \"mock\", output=output)),\n    )\n\n    with pytest.raises(PackageInfoError) as exc:\n        PackageInfo.from_directory(demo_setup_complex)\n\n    text = str(exc.value)\n    assert \"Command 'mock' returned non-zero exit status 1.\" in text\n    assert output in text\n    assert (\n        \"This error originates from the build backend, and is likely not a problem with poetry\"\n        in text\n    )\n\n\ndef test_info_setup_complex_pep517_legacy(\n    demo_setup_complex_pep517_legacy: Path,\n) -> None:\n    info = PackageInfo.from_directory(demo_setup_complex_pep517_legacy)\n    demo_check_info(info, requires_dist={\"package\"})\n\n\ndef test_info_setup_complex_calls_script(demo_setup_complex_calls_script: Path) -> None:\n    \"\"\"Building the project requires calling a script from its build_requires.\"\"\"\n    info = PackageInfo.from_directory(demo_setup_complex_calls_script)\n    demo_check_info(info, requires_dist={\"package\"})\n\n\n@pytest.mark.parametrize(\"missing\", [\"version\", \"name\"])\ndef test_info_setup_missing_mandatory_should_trigger_pep517(\n    mocker: MockerFixture, source_dir: Path, missing: str\n) -> None:\n    setup = \"from setuptools import setup; \"\n    setup += \"setup(\"\n    setup += 'name=\"demo\", ' if missing != \"name\" else \"\"\n    setup += 'version=\"0.1.0\", ' if missing != \"version\" else \"\"\n    setup += 'install_requires=[\"package\"]'\n    setup += \")\"\n\n    setup_py = source_dir / \"setup.py\"\n    setup_py.write_text(setup, encoding=\"utf-8\")\n\n    spy = mocker.spy(ProjectBuilder, \"from_isolated_env\")\n    _ = PackageInfo.from_directory(source_dir)\n    assert spy.call_count == 1\n\n\ndef test_info_prefer_poetry_config_over_egg_info(fixture_dir: FixtureDirGetter) -> None:\n    info = PackageInfo.from_directory(\n        fixture_dir(\"inspection\") / \"demo_with_obsolete_egg_info\"\n    )\n    demo_check_info(info)\n"
  },
  {
    "path": "tests/inspection/test_lazy_wheel.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom enum import IntEnum\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Protocol\nfrom urllib.parse import urlparse\n\nimport pytest\nimport requests\nimport responses\n\nfrom requests import codes\n\nfrom poetry.inspection.lazy_wheel import HTTPRangeRequestNotRespectedError\nfrom poetry.inspection.lazy_wheel import HTTPRangeRequestUnsupportedError\nfrom poetry.inspection.lazy_wheel import InvalidWheelError\nfrom poetry.inspection.lazy_wheel import LazyWheelUnsupportedError\nfrom poetry.inspection.lazy_wheel import metadata_from_wheel_url\nfrom tests.helpers import http_setup_redirect\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n    from requests import PreparedRequest\n\n    from tests.types import FixtureDirGetter\n    from tests.types import HttpRequestCallback\n    from tests.types import HttpRequestCallbackWrapper\n    from tests.types import HttpResponse\n    from tests.types import PackageDistributionLookup\n\n    class RequestCallbackFactory(Protocol):\n        def __call__(\n            self,\n            *,\n            accept_ranges: str | None = \"bytes\",\n            negative_offset_error: tuple[int, bytes] | None = None,\n            ignore_accept_ranges: bool = False,\n        ) -> HttpRequestCallback: ...\n\n    class AssertMetadataFromWheelUrl(Protocol):\n        def __call__(\n            self,\n            *,\n            accept_ranges: str | None = \"bytes\",\n            negative_offset_error: tuple[int, bytes] | None = None,\n            expected_requests: int = 3,\n            request_callback_wrapper: HttpRequestCallbackWrapper | None = None,\n            redirect: bool = True,\n        ) -> None: ...\n\n\nclass NegativeOffsetFailure(IntEnum):\n    # numbers must be negative to avoid conflicts with HTTP status codes\n    as_positive = -1  # JFrog Artifactory bug (RTDEV-38572)\n    one_more = -2  # JFrog Artifactory bug (one more byte than requested)\n\n\ndef build_head_response(\n    accept_ranges: str | None, content_length: int, response_headers: dict[str, str]\n) -> HttpResponse:\n    response_headers[\"Content-Length\"] = str(content_length)\n    if accept_ranges:\n        response_headers[\"Accept-Ranges\"] = accept_ranges\n    return 200, response_headers, b\"\"\n\n\ndef build_partial_response(\n    rng: str,\n    wheel_bytes: bytes,\n    response_headers: dict[str, str],\n    *,\n    negative_offset_failure: NegativeOffsetFailure | None = None,\n) -> HttpResponse:\n    status_code = 206\n    response_headers[\"Accept-Ranges\"] = \"bytes\"\n    total_length = len(wheel_bytes)\n    if rng.startswith(\"-\"):\n        # negative offset\n        offset = int(rng)\n        if negative_offset_failure == NegativeOffsetFailure.as_positive:\n            # some servers interpret a negative offset like \"-10\" as \"0-10\"\n            start = 0\n            end = min(-offset, total_length - 1)\n            body = wheel_bytes[start : end + 1]\n        elif negative_offset_failure == NegativeOffsetFailure.one_more:\n            # https://github.com/python-poetry/poetry/issues/9056#issuecomment-1973273721\n            offset -= 1  # one more byte\n            start = total_length + offset  # negative start of content range possible!\n            end = total_length - 1\n            body = wheel_bytes[offset:]\n            response_headers[\"Content-Length\"] = str(-offset)  # just wrong...\n        else:\n            start = total_length + offset\n            if start < 0:\n                # wheel is smaller than initial chunk size\n                response_headers[\"Content-Length\"] = str(len(wheel_bytes))\n                return 200, response_headers, wheel_bytes\n            end = total_length - 1\n            body = wheel_bytes[offset:]\n    else:\n        # range with start and end\n        start, end = map(int, rng.split(\"-\"))\n        body = wheel_bytes[start : end + 1]\n    response_headers[\"Content-Range\"] = f\"bytes {start}-{end}/{total_length}\"\n    if \"Content-Length\" not in response_headers:\n        response_headers[\"Content-Length\"] = str(len(body))\n    return status_code, response_headers, body\n\n\n@pytest.fixture\ndef handle_request_factory(\n    fixture_dir: FixtureDirGetter,\n    package_distribution_lookup: PackageDistributionLookup,\n) -> RequestCallbackFactory:\n    def _factory(\n        *,\n        accept_ranges: str | None = \"bytes\",\n        negative_offset_error: tuple[int, bytes] | None = None,\n        ignore_accept_ranges: bool = False,\n    ) -> HttpRequestCallback:\n        def handle_request(request: PreparedRequest) -> HttpResponse:\n            assert request.url\n            name = Path(urlparse(request.url).path).name\n\n            wheel = package_distribution_lookup(name) or package_distribution_lookup(\n                \"demo-0.1.0-py2.py3-none-any.whl\"\n            )\n\n            if not wheel:\n                return 404, {}, b\"Not Found\"\n\n            wheel_bytes = wheel.read_bytes()\n\n            response_headers: dict[str, str] = {}\n\n            if request.method == \"HEAD\":\n                return build_head_response(\n                    accept_ranges, len(wheel_bytes), response_headers\n                )\n\n            rng = request.headers.get(\"Range\", \"=\").split(\"=\")[1]\n\n            negative_offset_failure = None\n            if negative_offset_error and rng.startswith(\"-\"):\n                if negative_offset_error[0] == codes.requested_range_not_satisfiable:\n                    response_headers[\"Content-Range\"] = f\"bytes */{len(wheel_bytes)}\"\n                if negative_offset_error[0] == NegativeOffsetFailure.as_positive:\n                    negative_offset_failure = NegativeOffsetFailure.as_positive\n                elif negative_offset_error[0] == NegativeOffsetFailure.one_more:\n                    negative_offset_failure = NegativeOffsetFailure.one_more\n                else:\n                    response_headers[\"Content-Length\"] = str(\n                        len(negative_offset_error[1])\n                    )\n                    return (\n                        negative_offset_error[0],\n                        response_headers,\n                        negative_offset_error[1],\n                    )\n            if accept_ranges == \"bytes\" and rng and not ignore_accept_ranges:\n                return build_partial_response(\n                    rng,\n                    wheel_bytes,\n                    response_headers,\n                    negative_offset_failure=negative_offset_failure,\n                )\n\n            status_code = 200\n            body = wheel_bytes\n            response_headers[\"Content-Length\"] = str(len(body))\n\n            return status_code, response_headers, body\n\n        return handle_request\n\n    return _factory\n\n\n@pytest.fixture\ndef assert_metadata_from_wheel_url(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n) -> AssertMetadataFromWheelUrl:\n    def _assertion(\n        *,\n        accept_ranges: str | None = \"bytes\",\n        negative_offset_error: tuple[int, bytes] | None = None,\n        expected_requests: int = 3,\n        request_callback_wrapper: HttpRequestCallbackWrapper | None = None,\n        redirect: bool = False,\n    ) -> None:\n        http.reset()\n\n        domain = (\n            f\"lazy-wheel-{negative_offset_error[0] if negative_offset_error else 0}.com\"\n        )\n        uri_regex = re.compile(f\"^https://{domain}/.*$\")\n        request_callback = handle_request_factory(\n            accept_ranges=accept_ranges, negative_offset_error=negative_offset_error\n        )\n        if request_callback_wrapper is not None:\n            request_callback = request_callback_wrapper(request_callback)\n\n        http.add_callback(responses.GET, uri_regex, callback=request_callback)\n        http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n        if redirect:\n            http_setup_redirect(http, responses.GET, responses.HEAD)\n\n        url_prefix = \"redirect.\" if redirect else \"\"\n        url = f\"https://{url_prefix}{domain}/poetry_core-1.5.0-py3-none-any.whl\"\n\n        metadata = metadata_from_wheel_url(\"poetry-core\", url, requests.Session())\n\n        assert metadata[\"name\"] == \"poetry-core\"\n        assert metadata[\"version\"] == \"1.5.0\"\n        assert metadata[\"author\"] == \"Sébastien Eustace\"\n        assert metadata[\"requires_dist\"] == [\n            'importlib-metadata (>=1.7.0) ; python_version < \"3.8\"'\n        ]\n\n        assert len(http.calls) == expected_requests\n\n    return _assertion\n\n\n@pytest.mark.parametrize(\n    \"negative_offset_error\",\n    [\n        None,\n        (codes.not_found, b\"Not found\"),  # Nexus\n        (codes.method_not_allowed, b\"Method not allowed\"),\n        (codes.requested_range_not_satisfiable, b\"Requested range not satisfiable\"),\n        (codes.internal_server_error, b\"Internal server error\"),  # GAR\n        (codes.not_implemented, b\"Unsupported client range\"),  # PyPI\n        (NegativeOffsetFailure.as_positive, b\"handle negative offset as positive\"),\n        (NegativeOffsetFailure.one_more, b\"one more byte than requested\"),\n    ],\n)\ndef test_metadata_from_wheel_url(\n    assert_metadata_from_wheel_url: AssertMetadataFromWheelUrl,\n    negative_offset_error: tuple[int, bytes] | None,\n) -> None:\n    # negative offsets supported:\n    # 1. end of central directory\n    # 2. whole central directory\n    # 3. METADATA file\n    # negative offsets not supported:\n    # 1. failed range request\n    # 2. HEAD request\n    # 3.-5. see negative offsets 1.-3.\n    expected_requests = 3\n    if negative_offset_error:\n        if negative_offset_error[0] in {\n            codes.requested_range_not_satisfiable,\n            NegativeOffsetFailure.as_positive,\n            NegativeOffsetFailure.one_more,\n        }:\n            expected_requests += 1\n        else:\n            expected_requests += 2\n\n    assert_metadata_from_wheel_url(\n        negative_offset_error=negative_offset_error, expected_requests=expected_requests\n    )\n\n    # second wheel -> one less request if negative offsets are not supported\n    expected_requests = min(expected_requests, 4)\n    assert_metadata_from_wheel_url(\n        negative_offset_error=negative_offset_error, expected_requests=expected_requests\n    )\n\n\ndef test_metadata_from_wheel_url_416_missing_content_range(\n    assert_metadata_from_wheel_url: AssertMetadataFromWheelUrl,\n) -> None:\n    def request_callback_wrapper(\n        request_callback: HttpRequestCallback,\n    ) -> HttpRequestCallback:\n        def _wrapped(request: PreparedRequest) -> HttpResponse:\n            status_code, response_headers, body = request_callback(request)\n            return (\n                status_code,\n                {\n                    header: response_headers[header]\n                    for header in response_headers\n                    if header.lower() != \"content-range\"\n                },\n                body,\n            )\n\n        return _wrapped\n\n    assert_metadata_from_wheel_url(\n        negative_offset_error=(\n            codes.requested_range_not_satisfiable,\n            b\"Requested range not satisfiable\",\n        ),\n        expected_requests=5,\n        request_callback_wrapper=request_callback_wrapper,\n    )\n\n\ndef test_metadata_from_wheel_url_with_redirect(\n    assert_metadata_from_wheel_url: AssertMetadataFromWheelUrl,\n) -> None:\n    assert_metadata_from_wheel_url(\n        negative_offset_error=None,\n        expected_requests=6,\n        redirect=True,\n    )\n\n\ndef test_metadata_from_wheel_url_with_redirect_after_500(\n    assert_metadata_from_wheel_url: AssertMetadataFromWheelUrl,\n) -> None:\n    assert_metadata_from_wheel_url(\n        negative_offset_error=(codes.internal_server_error, b\"Internal server error\"),\n        expected_requests=10,\n        redirect=True,\n    )\n\n\n@pytest.mark.parametrize(\n    (\"negative_offset_failure\", \"expected_requests\"),\n    [\n        (None, 1),\n        (NegativeOffsetFailure.as_positive, 1),\n        (NegativeOffsetFailure.one_more, 2),\n    ],\n)\ndef test_metadata_from_wheel_url_smaller_than_initial_chunk_size(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n    negative_offset_failure: NegativeOffsetFailure | None,\n    expected_requests: int,\n) -> None:\n    domain = f\"tiny-wheel-{str(negative_offset_failure).casefold()}.com\"\n    uri_regex = re.compile(f\"^https://{domain}/.*$\")\n    request_callback = handle_request_factory(\n        negative_offset_error=(\n            (negative_offset_failure, b\"\") if negative_offset_failure else None\n        )\n    )\n    http.add_callback(responses.GET, uri_regex, callback=request_callback)\n    http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n    url = f\"https://{domain}/zipp-3.5.0-py3-none-any.whl\"\n\n    metadata = metadata_from_wheel_url(\"zipp\", url, requests.Session())\n\n    assert metadata[\"name\"] == \"zipp\"\n    assert metadata[\"version\"] == \"3.5.0\"\n    assert metadata[\"author\"] == \"Jason R. Coombs\"\n    assert len(metadata[\"requires_dist\"]) == 12\n\n    assert len(http.calls) == expected_requests\n\n\n@pytest.mark.parametrize(\"accept_ranges\", [None, \"none\"])\ndef test_metadata_from_wheel_url_range_requests_not_supported_one_request(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n    accept_ranges: str | None,\n) -> None:\n    domain = \"no-range-requests.com\"\n    uri_regex = re.compile(f\"^https://{domain}/.*$\")\n    request_callback = handle_request_factory(accept_ranges=accept_ranges)\n    http.add_callback(responses.GET, uri_regex, callback=request_callback)\n    http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n    url = f\"https://{domain}/poetry_core-1.5.0-py3-none-any.whl\"\n\n    with pytest.raises(HTTPRangeRequestUnsupportedError):\n        metadata_from_wheel_url(\"poetry-core\", url, requests.Session())\n\n    assert len(http.calls) == 1\n    assert http.calls[0].request.method == \"GET\"\n\n\n@pytest.mark.parametrize(\n    \"negative_offset_error\",\n    [\n        (codes.method_not_allowed, b\"Method not allowed\"),\n        (codes.not_implemented, b\"Unsupported client range\"),\n    ],\n)\ndef test_metadata_from_wheel_url_range_requests_not_supported_two_requests(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n    negative_offset_error: tuple[int, bytes],\n) -> None:\n    domain = f\"no-negative-offsets-{negative_offset_error[0]}.com\"\n    uri_regex = re.compile(f\"^https://{domain}/.*$\")\n    request_callback = handle_request_factory(\n        accept_ranges=None, negative_offset_error=negative_offset_error\n    )\n    http.add_callback(responses.GET, uri_regex, callback=request_callback)\n    http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n    url = f\"https://{domain}/poetry_core-1.5.0-py3-none-any.whl\"\n\n    with pytest.raises(HTTPRangeRequestUnsupportedError):\n        metadata_from_wheel_url(\"poetry-core\", url, requests.Session())\n\n    assert len(http.calls) == 2\n    assert http.calls[0].request.method == \"GET\"\n    assert http.calls[1].request.method == \"HEAD\"\n\n\ndef test_metadata_from_wheel_url_range_requests_supported_but_not_respected(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n) -> None:\n    domain = \"range-requests-not-respected.com\"\n    uri_regex = re.compile(f\"^https://{domain}/.*$\")\n    request_callback = handle_request_factory(\n        negative_offset_error=(codes.method_not_allowed, b\"Method not allowed\"),\n        ignore_accept_ranges=True,\n    )\n    http.add_callback(responses.GET, uri_regex, callback=request_callback)\n    http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n    url = f\"https://{domain}/poetry_core-1.5.0-py3-none-any.whl\"\n\n    with pytest.raises(HTTPRangeRequestNotRespectedError):\n        metadata_from_wheel_url(\"poetry-core\", url, requests.Session())\n\n    assert len(http.calls) == 3\n    assert http.calls[0].request.method == \"GET\"\n    assert http.calls[1].request.method == \"HEAD\"\n    assert http.calls[2].request.method == \"GET\"\n\n\ndef test_metadata_from_wheel_url_invalid_wheel(\n    http: responses.RequestsMock,\n    handle_request_factory: RequestCallbackFactory,\n) -> None:\n    domain = \"invalid-wheel.com\"\n    uri_regex = re.compile(f\"^https://{domain}/.*$\")\n    request_callback = handle_request_factory()\n    http.add_callback(responses.GET, uri_regex, callback=request_callback)\n    http.add_callback(responses.HEAD, uri_regex, callback=request_callback)\n\n    url = f\"https://{domain}/demo_missing_dist_info-0.1.0-py2.py3-none-any.whl\"\n\n    with pytest.raises(InvalidWheelError):\n        metadata_from_wheel_url(\"demo-missing-dist-info\", url, requests.Session())\n\n    assert len(http.calls) == 1\n    assert http.calls[0].request.method == \"GET\"\n\n\ndef test_metadata_from_wheel_url_handles_unexpected_errors(\n    mocker: MockerFixture,\n) -> None:\n    mocker.patch(\n        \"poetry.inspection.lazy_wheel.LazyWheelOverHTTP.read_metadata\",\n        side_effect=RuntimeError(),\n    )\n\n    with pytest.raises(LazyWheelUnsupportedError):\n        metadata_from_wheel_url(\n            \"demo-missing-dist-info\",\n            \"https://runtime-error.com/demo_missing_dist_info-0.1.0-py2.py3-none-any.whl\",\n            requests.Session(),\n        )\n"
  },
  {
    "path": "tests/installation/__init__.py",
    "content": ""
  },
  {
    "path": "tests/installation/conftest.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom packaging.tags import Tag\n\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.repositories.pypi_repository import PyPiRepository\nfrom poetry.repositories.repository_pool import RepositoryPool\nfrom poetry.utils.env import MockEnv\n\n\n@pytest.fixture()\ndef env() -> MockEnv:\n    return MockEnv(\n        supported_tags=[\n            Tag(\"cp37\", \"cp37\", \"macosx_10_15_x86_64\"),\n            Tag(\"py3\", \"none\", \"any\"),\n        ]\n    )\n\n\n@pytest.fixture()\ndef pool(legacy_repository_html: LegacyRepository) -> RepositoryPool:\n    pool = RepositoryPool()\n\n    pool.add_repository(PyPiRepository(disable_cache=True))\n    pool.add_repository(\n        LegacyRepository(\"foo\", \"https://legacy.foo.bar/simple/\", disable_cache=True)\n    )\n    pool.add_repository(\n        LegacyRepository(\"foo2\", \"https://legacy.foo2.bar/simple/\", disable_cache=True)\n    )\n    return pool\n"
  },
  {
    "path": "tests/installation/fixtures/extras-with-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'extra == \"foo\"'\nfiles = []\n\n[package.dependencies]\nD = \"^1.0\"\n\n[[package]]\nname = \"D\"\nversion = \"1.1\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'extra == \"foo\"'\nfiles = []\n\n[extras]\nfoo = [\"C\"]\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/extras.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"D\"\nversion = \"1.1\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[extras]\nfoo = [\"D\"]\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/install-no-dev.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/no-dependencies.test",
    "content": "package = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/remove.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/update-with-lock.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/update-with-locked-extras.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\n\"B\" = {version = \"^1.0\", optional = true}\n\"C\" = {version = \"^1.0\", markers = \"python_version == \\\"2.7\\\"\"}\n\n[package.extras]\nfoo = [\"B\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version == \"2.7\"'\nfiles = []\n\n[[package]]\nname = \"D\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-conditional-dependency.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=3.5\"\ngroups = [\"main\"]\nmarkers = 'python_version >= \"3.5\"'\nfiles = []\n\n[metadata]\npython-versions = \"~2.7 || ^3.4\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-conflicting-dependency-extras-root.test",
    "content": "[[package]]\nname = \"conflicting-dep\"\nversion = \"1.1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"extra-one\\\" and extra != \\\"extra-two\\\"\"\n\n[[package]]\nname = \"conflicting-dep\"\nversion = \"1.2.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra != \\\"extra-one\\\" and extra == \\\"extra-two\\\"\"\n\n[extras]\nextra-one = [ \"conflicting-dep\", \"conflicting-dep\" ]\nextra-two = [ \"conflicting-dep\", \"conflicting-dep\" ]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-conflicting-dependency-extras-transitive.test",
    "content": "[[package]]\nname = \"conflicting-dep\"\nversion = \"1.1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"root-extra-one\\\" and extra != \\\"root-extra-two\\\"\"\n\n[[package]]\nname = \"conflicting-dep\"\nversion = \"1.2.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra != \\\"root-extra-one\\\" and extra == \\\"root-extra-two\\\"\"\n\n[[package]]\nname = \"intermediate-dep\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"root-extra-one\\\" and extra != \\\"root-extra-two\\\" or extra != \\\"root-extra-one\\\" and extra == \\\"root-extra-two\\\"\"\n\n[[package.dependencies.conflicting-dep]]\nversion = \"1.2.0\"\noptional = true\nmarkers = 'extra != \"extra-one\" and extra == \"extra-two\"'\n\n[[package.dependencies.conflicting-dep]]\nversion = \"1.1.0\"\noptional = true\nmarkers = 'extra == \"extra-one\" and extra != \"extra-two\"'\n\n  [package.extras]\n  extra-one = [ \"conflicting-dep (==1.1.0)\", \"conflicting-dep (==1.2.0)\" ]\n  extra-two = [ \"conflicting-dep (==1.1.0)\", \"conflicting-dep (==1.2.0)\" ]\n\n[extras]\nroot-extra-one = [ \"intermediate-dep\", \"intermediate-dep\" ]\nroot-extra-two = [ \"intermediate-dep\", \"intermediate-dep\" ]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-dependencies-differing-extras.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"extra-one\\\" and extra != \\\"extra-two\\\" or extra != \\\"extra-one\\\" and extra == \\\"extra-two\\\"\"\n\n[package.dependencies.transitive-dep-one]\nversion = \"1.1.0\"\noptional = true\nmarkers = 'extra == \"demo-extra-one\" and extra != \"demo-extra-two\"'\n\n[package.dependencies.transitive-dep-two]\nversion = \"1.2.0\"\noptional = true\nmarkers = 'extra != \"demo-extra-one\" and extra == \"demo-extra-two\"'\n\n  [package.extras]\n  demo-extra-one = [ \"transitive-dep-one\", \"transitive-dep-two\" ]\n  demo-extra-two = [ \"transitive-dep-one\", \"transitive-dep-two\" ]\n\n[[package]]\nname = \"transitive-dep-one\"\nversion = \"1.1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"extra-one\\\" and extra != \\\"extra-two\\\"\"\n\n[[package]]\nname = \"transitive-dep-two\"\nversion = \"1.2.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = [ ]\ngroups = [ \"main\" ]\nmarkers = \"extra != \\\"extra-one\\\" and extra == \\\"extra-two\\\"\"\n\n[extras]\nextra-one = [ \"demo\", \"demo\" ]\nextra-two = [ \"demo\", \"demo\" ]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-dependencies-extras.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nC = {version = \"^1.0\", optional = true}\n\n[package.extras]\nfoo = [\"C (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-dependencies-nested-extras.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = {version = \"^1.0\", optional = true, extras = [\"c\"]}\n\n[package.extras]\nb = [\"B[c] (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nC = {version = \"^1.0\", optional = true}\n\n[package.extras]\nc = [\"C (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-directory-dependency-poetry-transitive.test",
    "content": "[[package]]\ndescription = \"\"\nname = \"demo\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\nversion = \"0.1.0\"\ngroups = [\"main\"]\n\n[[package.files]]\nfile = \"demo-0.1.0-py2.py3-none-any.whl\"\nhash = \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[package.source]\ntype = \"file\"\nurl = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"inner-directory-project\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.4\"\n\n[package.source]\ntype = \"directory\"\nurl = \"project_with_transitive_file_dependencies/inner-directory-project\"\n\n[[package]]\ndescription = \"\"\nname = \"pendulum\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.4.4\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"project-with-extras\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.3\"\n\n[package.extras]\nextras-a = [\"pendulum (>=1.4.4)\"]\nextras-b = [\"cachy (>=0.2.0)\"]\n\n[package.source]\ntype = \"directory\"\nurl = \"../project_with_extras\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"project-with-transitive-directory-dependencies\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.3\"\n\n[package.dependencies]\nproject-with-extras = { \"path\" = \"../../project_with_extras\" }\nproject-with-transitive-file-dependencies = { \"path\" = \"../project_with_transitive_file_dependencies\" }\n\n[package.source]\ntype = \"directory\"\nurl = \"project_with_transitive_directory_dependencies\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"project-with-transitive-file-dependencies\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.3\"\n\n[package.dependencies]\ndemo = { \"path\" = \"../../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\ninner-directory-project = { \"path\" = \"inner-directory-project\" }\n\n[package.source]\ntype = \"directory\"\nurl = \"project_with_transitive_file_dependencies\"\n\n[metadata]\ncontent-hash = \"123456789\"\nlock-version = \"2.1\"\npython-versions = \"*\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-directory-dependency-poetry.test",
    "content": "[[package]]\ndescription = \"\"\nname = \"pendulum\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.4.4\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"project-with-extras\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.3\"\n\n[package.dependencies]\npendulum = {version = \">=1.4.4\", optional = true}\n\n[package.extras]\nextras-a = [\"pendulum (>=1.4.4)\"]\nextras-b = [\"cachy (>=0.2.0)\"]\n\n[package.source]\ntype = \"directory\"\nurl = \"tests/fixtures/project_with_extras\"\n\n[metadata]\ncontent-hash = \"123456789\"\nlock-version = \"2.1\"\npython-versions = \"*\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-directory-dependency-setuptools.test",
    "content": "[[package]]\nname = \"cachy\"\nversion = \"0.2.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"project-with-setup\"\nversion = \"0.1.2\"\ndevelop = false\ndescription = \"Demo project.\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"directory\"\nurl = \"project\"\n\n[package.dependencies]\ncachy = {version = \">=0.2.0\", extras = [\"msgpack\"]}\npendulum = \">=1.4.4\"\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-duplicate-dependencies-update.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = \"^2.0\"\n\n[[package]]\nname = \"B\"\nversion = \"2.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nC = \"1.5\"\n\n[[package]]\nname = \"C\"\nversion = \"1.5\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-duplicate-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = [\n    {version = \">=1.0,<2.0\", markers = \"python_version < \\\"4.0\\\"\"},\n    {version = \">=2.0,<3.0\", markers = \"python_version >= \\\"4.0\\\"\"},\n]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version < \"4.0\"'\nfiles = []\n\n[package.dependencies]\nC = \"1.2\"\n\n[[package]]\nname = \"B\"\nversion = \"2.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version >= \"4.0\"'\nfiles = []\n\n[package.dependencies]\nC = \"1.5\"\n\n[[package]]\nname = \"C\"\nversion = \"1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version < \"4.0\"'\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.5\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version >= \"4.0\"'\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-exclusive-extras.test",
    "content": "[[package]]\nname = \"torch\"\nversion = \"1.11.0+cpu\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = []\ngroups = [ \"main\" ]\nmarkers = \"extra == \\\"cpu\\\" and extra != \\\"cuda\\\"\"\n\n[package.source]\nreference = \"pytorch-cpu\"\ntype = \"legacy\"\nurl = \"https://download.pytorch.org/whl/cpu\"\n\n[[package]]\nname = \"torch\"\nversion = \"1.11.0+cuda\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\nfiles = []\ngroups = [ \"main\" ]\nmarkers = \"extra != \\\"cpu\\\" and extra == \\\"cuda\\\"\"\n\n[package.source]\nreference = \"pytorch-cuda\"\ntype = \"legacy\"\nurl = \"https://download.pytorch.org/whl/cuda\"\n\n[extras]\ncpu = [\"torch\", \"torch\"]\ncuda = [\"torch\", \"torch\"]\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-file-dependency-transitive.test",
    "content": "[[package]]\ndescription = \"\"\nname = \"demo\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\nversion = \"0.1.0\"\ngroups = [\"main\"]\n\n[[package.files]]\nfile = \"demo-0.1.0-py2.py3-none-any.whl\"\nhash = \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[package.source]\ntype = \"file\"\nurl = \"../distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"inner-directory-project\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.4\"\n\n[package.source]\ntype = \"directory\"\nurl = \"project_with_transitive_file_dependencies/inner-directory-project\"\n\n[[package]]\ndescription = \"\"\nname = \"pendulum\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.4.4\"\n\n[[package]]\ndescription = \"This is a description\"\ndevelop = false\nname = \"project-with-transitive-file-dependencies\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\nversion = \"1.2.3\"\n\n[package.dependencies]\ndemo = { \"path\" = \"../../distributions/demo-0.1.0-py2.py3-none-any.whl\" }\ninner-directory-project = { \"path\" = \"inner-directory-project\" }\n\n[package.source]\ntype = \"directory\"\nurl = \"project_with_transitive_file_dependencies\"\n\n[metadata]\ncontent-hash = \"123456789\"\nlock-version = \"2.1\"\npython-versions = \"*\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-file-dependency.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\n\n[[package.files]]\nfile = \"demo-0.1.0-py2.py3-none-any.whl\"\nhash = \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n\n[package.source]\ntype = \"file\"\nurl = \"tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-multiple-updates.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = \">=1.0.1\"\nC = [\n    {version = \">=1.0,<2.0\", markers = \"python_version == \\\"2.7\\\"\"},\n    {version = \">=2.0,<3.0\", markers = \"python_version >= \\\"3.4\\\" and python_version < \\\"4.0\\\"\"},\n]\n\n[[package]]\nname = \"B\"\nversion = \"1.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version == \"2.7\"'\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"2.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'python_version >= \"3.4\"'\nfiles = []\n\n[metadata]\npython-versions = \"~2.7 || ^3.4\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-optional-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'extra == \"foo\"'\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nD = \"^1.2\"\n\n[[package]]\nname = \"D\"\nversion = \"1.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[extras]\nfoo = [\"A\"]\n\n[metadata]\npython-versions = \"~2.7 || ^3.4\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-platform-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = true\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'extra == \"foo\"'\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"custom\"'\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"darwin\"'\nfiles = []\n\n[package.dependencies]\nD = \"^1.2\"\n\n[[package]]\nname = \"D\"\nversion = \"1.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"darwin\"'\nfiles = []\n\n[extras]\nfoo = [\"A\"]\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-prereleases.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0a2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-pypi-repository.test",
    "content": "# This file is automatically @generated by Poetry 1.9.0.dev0 and should not be changed by hand.\n\n[[package]]\nname = \"attrs\"\nversion = \"17.4.0\"\ndescription = \"Classes Without Boilerplate\"\noptional = false\npython-versions = \"*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"attrs-17.4.0-py2.py3-none-any.whl\", hash = \"sha256:1fbfc10ebc8c876dcbab17f016b80ae1a4f0c1413461a695871427960795beb4\"},\n    {file = \"attrs-17.4.0.tar.gz\", hash = \"sha256:eb7536a1e6928190b3008c5b350bdf9850d619fff212341cd096f87a27a5e564\"},\n]\n\n[package.extras]\ndev = [\"coverage\", \"hypothesis\", \"pympler\", \"pytest\", \"six\", \"sphinx\", \"zope.interface\", \"zope.interface\"]\ndocs = [\"sphinx\", \"zope.interface\"]\ntests = [\"coverage\", \"hypothesis\", \"pympler\", \"pytest\", \"six\", \"zope.interface\"]\n\n[[package]]\nname = \"colorama\"\nversion = \"0.3.9\"\ndescription = \"Cross-platform colored terminal text.\"\noptional = false\npython-versions = \"*\"\ngroups = [\"dev\"]\nmarkers = 'sys_platform == \"win32\"'\nfiles = [\n    {file = \"colorama-0.3.9-py2.py3-none-any.whl\", hash = \"sha256:78a441d2e984c790526cdef1cfd8415a366979ef5b3186771a055b35886953bf\"},\n    {file = \"colorama-0.3.9.tar.gz\", hash = \"sha256:4c5a15209723ce1330a5c193465fe221098f761e9640d823a2ce7c03f983137f\"},\n]\n\n[[package]]\nname = \"more-itertools\"\nversion = \"4.1.0\"\ndescription = \"More routines for operating on iterables, beyond itertools\"\noptional = false\npython-versions = \"*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"more-itertools-4.1.0.tar.gz\", hash = \"sha256:bab2dc6f4be8f9a4a72177842c5283e2dff57c167439a03e3d8d901e854f0f2e\"},\n    {file = \"more_itertools-4.1.0-py2-none-any.whl\", hash = \"sha256:0f461c2cd4ec16611396f9ee57f40433de3d59e95475d84c0c829cde02f746cd\"},\n    {file = \"more_itertools-4.1.0-py3-none-any.whl\", hash = \"sha256:580b6002d1f28feb5bcb8303278d59cf17dfbd19a63a5c2375112dae72c9bf98\"},\n]\n\n[package.dependencies]\nsix = \">=1.0.0,<2.0.0\"\n\n[[package]]\nname = \"pluggy\"\nversion = \"0.6.0\"\ndescription = \"plugin and hook calling mechanisms for python\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"pluggy-0.6.0-py2-none-any.whl\", hash = \"sha256:f5f767d398f18aa177976bf9c4d0c05d96487a7d8f07062251585803aaf56246\"},\n    {file = \"pluggy-0.6.0-py3-none-any.whl\", hash = \"sha256:d34798b80853ab688de1a3ca5b99ba4de91c459c19c76a555dc939979ae67eb0\"},\n    {file = \"pluggy-0.6.0.tar.gz\", hash = \"sha256:a982e208d054867661d27c6d2a86b17ba05fbb6b1bdc01f42660732dd107f865\"},\n]\n\n[[package]]\nname = \"py\"\nversion = \"1.5.3\"\ndescription = \"library with cross-python path, ini-parsing, io, code, log facilities\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"py-1.5.3-py2.py3-none-any.whl\", hash = \"sha256:ef4a94f47156178e42ef8f2b131db420e0f4b6aa0b3936b6dbde6ad6487476a5\"},\n    {file = \"py-1.5.3.tar.gz\", hash = \"sha256:2df2c513c3af11de15f58189ba5539ddc4768c6f33816dc5c03950c8bd6180fa\"},\n]\n\n[[package]]\nname = \"pytest\"\nversion = \"3.5.1\"\ndescription = \"pytest: simple powerful testing with Python\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"pytest-3.5.1-py2.py3-none-any.whl\", hash = \"sha256:d327df3686046c5b374a9776d9e11606f7dba6fb3db5cf5d60ebc78a31e0768e\"},\n    {file = \"pytest-3.5.1.tar.gz\", hash = \"sha256:b8fe151f3e181801dd38583a1c03818fbc662a8fce96c9063a0af624613e78f8\"},\n]\n\n[package.dependencies]\nattrs = \">=17.4.0\"\ncolorama = {version = \"*\", markers = \"sys_platform == \\\"win32\\\"\"}\nmore-itertools = \">=4.0.0\"\npluggy = \">=0.5,<0.7\"\npy = \">=1.5.0\"\nsetuptools = \"*\"\nsix = \">=1.10.0\"\n\n[[package]]\nname = \"setuptools\"\nversion = \"67.6.1\"\ndescription = \"Easily download, build, install, upgrade, and uninstall Python packages\"\noptional = false\npython-versions = \">=3.7\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"setuptools-67.6.1-py3-none-any.whl\", hash = \"sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078\"},\n    {file = \"setuptools-67.6.1.tar.gz\", hash = \"sha256:a737d365c957dd3fced9ddd246118e95dce7a62c3dc49f37e7fdd9e93475d785\"},\n]\n\n[package.extras]\ndocs = [\"furo\", \"jaraco.packaging (>=9)\", \"jaraco.tidelift (>=1.4)\", \"pygments-github-lexers (==0.0.5)\", \"rst.linker (>=1.9)\", \"sphinx (>=3.5)\", \"sphinx-favicon\", \"sphinx-hoverxref (<2)\", \"sphinx-inline-tabs\", \"sphinx-lint\", \"sphinx-notfound-page (==0.8.3)\", \"sphinx-reredirects\", \"sphinxcontrib-towncrier\"]\ntesting = [\"build[virtualenv]\", \"filelock (>=3.4.0)\", \"flake8 (<5)\", \"flake8-2020\", \"ini2toml[lite] (>=0.9)\", \"jaraco.envs (>=2.2)\", \"jaraco.path (>=3.2.0)\", \"pip (>=19.1)\", \"pip-run (>=8.8)\", \"pytest (>=6)\", \"pytest-black (>=0.3.7) ; platform_python_implementation != \\\"PyPy\\\"\", \"pytest-checkdocs (>=2.4)\", \"pytest-cov ; platform_python_implementation != \\\"PyPy\\\"\", \"pytest-enabler (>=1.3)\", \"pytest-flake8 ; python_version < \\\"3.12\\\"\", \"pytest-mypy (>=0.9.1) ; platform_python_implementation != \\\"PyPy\\\"\", \"pytest-perf\", \"pytest-timeout\", \"pytest-xdist\", \"tomli-w (>=1.0.0)\", \"virtualenv (>=13.0.0)\", \"wheel\"]\ntesting-integration = [\"build[virtualenv]\", \"filelock (>=3.4.0)\", \"jaraco.envs (>=2.2)\", \"jaraco.path (>=3.2.0)\", \"pytest\", \"pytest-enabler\", \"pytest-xdist\", \"tomli\", \"virtualenv (>=13.0.0)\", \"wheel\"]\n\n[[package]]\nname = \"six\"\nversion = \"1.11.0\"\ndescription = \"Python 2 and 3 compatibility utilities\"\noptional = false\npython-versions = \"*\"\ngroups = [\"dev\"]\nfiles = [\n    {file = \"six-1.11.0-py2.py3-none-any.whl\", hash = \"sha256:534e9875e44a507adec601c29b3cbd2ca6dae7df92bf3dd20c7289b2f99f7466\"},\n    {file = \"six-1.11.0.tar.gz\", hash = \"sha256:268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d\"},\n]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \">=3.7\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-python-versions.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"~2.7 || ^3.3\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"~2.7 || ^3.4\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-same-version-url-dependencies.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"win32\"'\nfiles = [\n    {file = \"demo-0.1.0-py2.py3-none-any.whl\", hash = \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"}\n]\n\n[package.source]\ntype = \"url\"\nurl = \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[[package]]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"linux\"'\nfiles = [\n    {file = \"demo-0.1.0.tar.gz\", hash = \"sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"}\n]\n[package.source]\ntype = \"url\"\nurl = \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = 'sys_platform == \"linux\" or sys_platform == \"win32\"'\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-all-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = {version = \"1.0\", extras = [\"all\"]}\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-all-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-b-markers.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install] ; python_version < \\\"3.9\\\"\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = \"1.0\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-download-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = {version = \"1.0\", extras = [\"download\"]}\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-download-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-install-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = {version = \"1.0\", extras = [\"install\"]}\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-install-download-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = {version = \"1.0\", extras = [\"download\", \"install\"]}\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-install-download-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-install-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-nested-deep.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\na = {version = \"1.0\", extras = [\"nested\"]}\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-nested-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\ndownload-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"download\\\"\"}\ninstall-package = {version = \">=1.0,<2.0\", optional = true, markers = \"extra == \\\"install\\\"\"}\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[[package]]\nname = \"download-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"install-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-self-referencing-extras-top.test",
    "content": "# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nall = [\"a[download,install]\"]\ndownload = [\"download-package (>=1.0,<2.0)\"]\ninstall = [\"install-package (>=1.0,<2.0)\"]\nnested = [\"a[all]\"]\npy = [\"a[py310,py38]\"]\npy310 = [\"py310-package (>=1.0,<2.0) ; python_version > \\\"3.8\\\"\"]\npy38 = [\"py38-package (>=1.0,<2.0) ; python_version == \\\"3.8\\\"\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-sub-dependencies.test",
    "content": "[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nD = \"^1.0\"\n\n[[package]]\nname = \"B\"\nversion = \"1.1\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nC = \"~1.2\"\n\n[[package]]\nname = \"C\"\nversion = \"1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"D\"\nversion = \"1.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-url-dependency.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\nfiles = [\n    {file = \"demo-0.1.0-py2.py3-none-any.whl\", hash = \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"}\n]\n\n[package.source]\ntype = \"url\"\nurl = \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.extras]\nbar = [\"tomlkit\"]\nfoo = [\"cleo\"]\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-vcs-dependency-with-extras.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\ndevelop = false\n\n[package.dependencies]\ncleo = {version = \"*\", optional = true, markers = \"extra == \\\"foo\\\"\"}\npendulum = \">=1.4.4\"\n\n[package.extras]\nfoo = [\"cleo\"]\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/demo/demo.git\"\nreference = \"master\"\nresolved_reference = \"123456\"\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"cleo\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-vcs-dependency-without-ref.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\ndevelop = false\n\n[package.dependencies]\npendulum = \">=1.4.4\"\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/demo/demo.git\"\nreference = \"HEAD\"\nresolved_reference = \"123456\"\n\n[[package]]\nname = \"pendulum\"\nversion = \"1.4.4\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/fixtures/with-wheel-dependency-no-requires-dist.test",
    "content": "[[package]]\nname = \"demo\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\n\n[[package.files]]\nfile = \"demo-0.1.0-py2.py3-none-any.whl\"\nhash = \"sha256:c25eb81459126848a1788eb3520d1a32014eb51ce3d3bae88c56bfdde4ce02db\"\n\n[package.source]\ntype = \"file\"\nurl = \"tests/fixtures/wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl\"\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n"
  },
  {
    "path": "tests/installation/test_chef.py",
    "content": "from __future__ import annotations\n\nimport os\nimport shutil\nimport tempfile\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom zipfile import ZipFile\n\nimport pytest\n\nfrom build import ProjectBuilder\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.factory import Factory\nfrom poetry.installation.chef import Chef\nfrom poetry.repositories import RepositoryPool\nfrom poetry.utils.env import EnvManager\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n    from poetry.repositories.pypi_repository import PyPiRepository\n    from poetry.utils.cache import ArtifactCache\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture()\ndef pool(pypi_repository: PyPiRepository) -> RepositoryPool:\n    pool = RepositoryPool()\n\n    pool.add_repository(pypi_repository)\n\n    return pool\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture, pool: RepositoryPool) -> None:\n    mocker.patch.object(Factory, \"create_pool\", return_value=pool)\n\n\ndef test_prepare_sdist(\n    config: Config,\n    config_cache_dir: Path,\n    artifact_cache: ArtifactCache,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    chef = Chef(\n        artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)\n    )\n    archive = (fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\").resolve()\n    destination = artifact_cache.get_cache_directory_for_link(Link(archive.as_uri()))\n\n    wheel = chef.prepare(archive)\n\n    assert wheel.parent == destination\n    assert wheel.name == \"demo-0.1.0-py3-none-any.whl\"\n\n\ndef test_prepare_directory(\n    config: Config,\n    config_cache_dir: Path,\n    artifact_cache: ArtifactCache,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    chef = Chef(\n        artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)\n    )\n    archive = fixture_dir(\"simple_project_legacy\").resolve()\n\n    wheel = chef.prepare(archive)\n\n    assert wheel.name == \"simple_project-1.2.3-py2.py3-none-any.whl\"\n\n    assert wheel.parent.parent == Path(tempfile.gettempdir())\n    # cleanup generated tmp dir artifact\n    os.unlink(wheel)\n\n\ndef test_prepare_directory_with_extensions(\n    config: Config,\n    config_cache_dir: Path,\n    artifact_cache: ArtifactCache,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    env = EnvManager.get_system_env()\n    chef = Chef(artifact_cache, env, Factory.create_pool(config))\n    archive = shutil.copytree(\n        fixture_dir(\"extended_with_no_setup\").resolve(), tmp_path / \"project\"\n    )\n\n    wheel = chef.prepare(archive)\n\n    assert wheel.parent.parent == Path(tempfile.gettempdir())\n    assert wheel.name == f\"extended-0.1-{env.supported_tags[0]}.whl\"\n\n    # cleanup generated tmp dir artifact\n    os.unlink(wheel)\n\n\ndef test_prepare_directory_editable(\n    config: Config,\n    config_cache_dir: Path,\n    artifact_cache: ArtifactCache,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    chef = Chef(\n        artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)\n    )\n    archive = fixture_dir(\"simple_project_legacy\").resolve()\n\n    wheel = chef.prepare(archive, editable=True)\n\n    assert wheel.parent.parent == Path(tempfile.gettempdir())\n    assert wheel.name == \"simple_project-1.2.3-py2.py3-none-any.whl\"\n\n    with ZipFile(wheel) as z:\n        assert \"simple_project.pth\" in z.namelist()\n\n    # cleanup generated tmp dir artifact\n    os.unlink(wheel)\n\n\ndef test_prepare_directory_script(\n    config: Config,\n    config_cache_dir: Path,\n    artifact_cache: ArtifactCache,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"\n    Building a project that requires calling a script from its build_requires.\n    \"\"\"\n    # make sure the scripts project is on the same drive (for Windows tests in CI)\n    scripts_dir = tmp_path / \"scripts\"\n    shutil.copytree(fixture_dir(\"scripts\"), scripts_dir)\n\n    orig_build_system_requires = ProjectBuilder.build_system_requires\n\n    class CustomPropertyMock:\n        def __get__(\n            self, obj: ProjectBuilder, obj_type: type[ProjectBuilder] | None = None\n        ) -> set[str]:\n            assert isinstance(obj, ProjectBuilder)\n            return {\n                req.replace(\"<scripts>\", f\"scripts @ {scripts_dir.as_uri()}\")\n                for req in orig_build_system_requires.fget(obj)  # type: ignore[attr-defined]\n            }\n\n    mocker.patch(\n        \"build.ProjectBuilder.build_system_requires\",\n        new_callable=CustomPropertyMock,\n    )\n    chef = Chef(\n        artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)\n    )\n    archive = shutil.copytree(\n        fixture_dir(\"project_with_setup_calls_script\").resolve(), tmp_path / \"project\"\n    )\n    wheel = chef.prepare(archive)\n\n    assert wheel.name == \"project_with_setup_calls_script-0.1.2-py3-none-any.whl\"\n\n    assert wheel.parent.parent == Path(tempfile.gettempdir())\n    # cleanup generated tmp dir artifact\n    os.unlink(wheel)\n"
  },
  {
    "path": "tests/installation/test_chooser.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom packaging.tags import Tag\nfrom poetry.core.packages.package import Package\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.installation.chooser import Chooser\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.utils.env import MockEnv\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.package import PackageFile\n\n    from poetry.repositories.repository_pool import RepositoryPool\n    from tests.conftest import Config\n    from tests.types import DistributionHashGetter\n    from tests.types import SpecializedLegacyRepositoryMocker\n\nJSON_FIXTURES = (\n    Path(__file__).parent.parent / \"repositories\" / \"fixtures\" / \"pypi.org\" / \"json\"\n)\n\nLEGACY_FIXTURES = Path(__file__).parent.parent / \"repositories\" / \"fixtures\" / \"legacy\"\n\n\ndef check_chosen_link_filename(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    filename: str | None,\n    config: Config | None = None,\n    package_name: str = \"pytest\",\n    package_version: str = \"3.5.0\",\n) -> None:\n    chooser = Chooser(pool, env, config)\n    package = Package(package_name, package_version)\n\n    if source_type == \"legacy\":\n        package = Package(\n            package.name,\n            package.version.text,\n            source_type=\"legacy\",\n            source_reference=\"foo\",\n            source_url=\"https://legacy.foo.bar/simple/\",\n        )\n\n    try:\n        link = chooser.choose_for(package)\n    except PoetryRuntimeError as e:\n        if filename is None:\n            assert (\n                str(e)\n                == f\"Unable to find installation candidates for {package.name} ({package.version})\"\n            )\n        else:\n            pytest.fail(\"Package was not found\")\n    else:\n        assert link.filename == filename\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_universal_wheel_link_if_available(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n) -> None:\n    check_chosen_link_filename(\n        env, source_type, pool, \"pytest-3.5.0-py2.py3-none-any.whl\"\n    )\n\n\n@pytest.mark.parametrize(\n    (\"policy\", \"filename\"),\n    [\n        (\":all:\", \"pytest-3.5.0.tar.gz\"),\n        (\":none:\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"black\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"pytest\", \"pytest-3.5.0.tar.gz\"),\n        (\"pytest,black\", \"pytest-3.5.0.tar.gz\"),\n    ],\n)\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_no_binary_policy(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    policy: str,\n    filename: str,\n    config: Config,\n) -> None:\n    config.merge({\"installer\": {\"no-binary\": policy.split(\",\")}})\n    check_chosen_link_filename(env, source_type, pool, filename, config)\n\n\n@pytest.mark.parametrize(\n    (\"policy\", \"filename\"),\n    [\n        (\":all:\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\":none:\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"black\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"pytest\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"pytest,black\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n    ],\n)\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_only_binary_policy(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    policy: str,\n    filename: str,\n    config: Config,\n) -> None:\n    config.merge({\"installer\": {\"only-binary\": policy.split(\",\")}})\n    check_chosen_link_filename(env, source_type, pool, filename, config)\n\n\n@pytest.mark.parametrize(\n    (\"no_binary\", \"only_binary\", \"filename\"),\n    [\n        # no `no_binary` nor `only_binary`\n        (\":none:\", \":none:\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"black\", \"black\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        # `no_binary` only\n        (\":all:\", \":none:\", \"pytest-3.5.0.tar.gz\"),\n        (\"pytest\", \"black\", \"pytest-3.5.0.tar.gz\"),\n        # `only_binary` only\n        (\":none:\", \":all:\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        (\"black\", \"pytest\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        # both `no_binary` and `only_binary`\n        (\"pytest\", \"pytest\", None),\n        (\":all:\", \":all:\", None),\n        (\"pytest\", \":all:\", \"pytest-3.5.0.tar.gz\"),\n        (\":all:\", \"pytest\", \"pytest-3.5.0-py2.py3-none-any.whl\"),\n        # complex cases\n        (\"pytest,black\", \"pytest,black\", None),\n    ],\n)\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_multiple_binary_policy(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    no_binary: str,\n    only_binary: str,\n    filename: str | None,\n    config: Config,\n) -> None:\n    config.merge(\n        {\n            \"installer\": {\n                \"no-binary\": no_binary.split(\",\"),\n                \"only-binary\": only_binary.split(\",\"),\n            }\n        }\n    )\n    check_chosen_link_filename(env, source_type, pool, filename, config)\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_specific_python_universal_wheel_link_if_available(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n) -> None:\n    check_chosen_link_filename(\n        env, source_type, pool, \"isort-4.3.4-py3-none-any.whl\", None, \"isort\", \"4.3.4\"\n    )\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_system_specific_wheel_link_if_available(\n    source_type: str, pool: RepositoryPool\n) -> None:\n    env = MockEnv(\n        supported_tags=[Tag(\"cp37\", \"cp37m\", \"win32\"), Tag(\"py3\", \"none\", \"any\")]\n    )\n    check_chosen_link_filename(\n        env,\n        source_type,\n        pool,\n        \"PyYAML-3.13-cp37-cp37m-win32.whl\",\n        None,\n        \"pyyaml\",\n        \"3.13.0\",\n    )\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n) -> None:\n    check_chosen_link_filename(\n        env, source_type, pool, \"PyYAML-3.13.tar.gz\", None, \"pyyaml\", \"3.13.0\"\n    )\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_distributions_that_match_the_package_hashes(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    dist_hash_getter: DistributionHashGetter,\n) -> None:\n    chooser = Chooser(pool, env)\n\n    package = Package(\"isort\", \"4.3.4\")\n    files: list[PackageFile] = [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n        }\n        for filename in [\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n    if source_type == \"legacy\":\n        package = Package(\n            package.name,\n            package.version.text,\n            source_type=\"legacy\",\n            source_reference=\"foo\",\n            source_url=\"https://legacy.foo.bar/simple/\",\n        )\n\n    package.files = files\n\n    link = chooser.choose_for(package)\n\n    assert link.filename == \"isort-4.3.4.tar.gz\"\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_chooses_yanked_if_no_others(\n    env: MockEnv,\n    source_type: str,\n    pool: RepositoryPool,\n    dist_hash_getter: DistributionHashGetter,\n) -> None:\n    chooser = Chooser(pool, env)\n\n    package = Package(\"black\", \"21.11b0\")\n    files: list[PackageFile] = [\n        {\n            \"file\": filename,\n            \"hash\": (f\"sha256:{dist_hash_getter(filename).sha256}\"),\n        }\n        for filename in [f\"{package.name}-{package.version}-py3-none-any.whl\"]\n    ]\n    if source_type == \"legacy\":\n        package = Package(\n            package.name,\n            package.version.text,\n            source_type=\"legacy\",\n            source_reference=\"foo\",\n            source_url=\"https://legacy.foo.bar/simple/\",\n        )\n\n    package.files = files\n\n    link = chooser.choose_for(package)\n\n    assert link.filename == \"black-21.11b0-py3-none-any.whl\"\n    assert link.yanked\n\n\ndef test_chooser_does_not_choose_yanked_if_others(\n    specialized_legacy_repository_mocker: SpecializedLegacyRepositoryMocker,\n    pool: RepositoryPool,\n    dist_hash_getter: DistributionHashGetter,\n) -> None:\n    chooser = Chooser(pool, MockEnv(supported_tags=[Tag(\"py2\", \"none\", \"any\")]))\n\n    repo = pool.repository(\"foo2\")\n    pool.remove_repository(\"foo2\")\n\n    assert isinstance(repo, LegacyRepository)\n    pool.add_repository(\n        specialized_legacy_repository_mocker(\"-partial-yank\", repo.name, repo.url)\n    )\n\n    package = Package(\"futures\", \"3.2.0\")\n    files: list[PackageFile] = [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n        }\n        for filename in [\n            f\"{package.name}-{package.version}-py2-none-any.whl\",\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n    package = Package(\n        package.name,\n        package.version.text,\n        source_type=\"legacy\",\n        source_reference=\"foo\",\n        source_url=\"https://legacy.foo.bar/simple/\",\n    )\n    package_partial_yank = Package(\n        package.name,\n        package.version.text,\n        source_type=\"legacy\",\n        source_reference=\"foo2\",\n        source_url=\"https://legacy.foo2.bar/simple/\",\n    )\n\n    package.files = files\n    package_partial_yank.files = files\n\n    link = chooser.choose_for(package)\n    link_partial_yank = chooser.choose_for(package_partial_yank)\n\n    assert link.filename == \"futures-3.2.0-py2-none-any.whl\"\n    assert link_partial_yank.filename == \"futures-3.2.0.tar.gz\"\n\n\n@pytest.mark.parametrize(\"source_type\", [\"\", \"legacy\"])\ndef test_chooser_throws_an_error_if_package_hashes_do_not_match(\n    env: MockEnv,\n    source_type: None,\n    pool: RepositoryPool,\n) -> None:\n    chooser = Chooser(pool, env)\n\n    package = Package(\"isort\", \"4.3.4\")\n    files: list[PackageFile] = [\n        {\n            \"hash\": (\n                \"sha256:0000000000000000000000000000000000000000000000000000000000000000\"\n            ),\n            \"file\": \"isort-4.3.4.tar.gz\",\n        }\n    ]\n    if source_type == \"legacy\":\n        package = Package(\n            package.name,\n            package.version.text,\n            source_type=\"legacy\",\n            source_reference=\"foo\",\n            source_url=\"https://legacy.foo.bar/simple/\",\n        )\n\n    package.files = files\n\n    with pytest.raises(PoetryRuntimeError) as e:\n        chooser.choose_for(package)\n\n    reason = f\"Downloaded distributions for {package.name} ({package.version}) did not match any known checksums in your lock file.\"\n    assert str(e.value) == reason\n\n    text = e.value.get_text(debug=True, strip=True)\n    assert reason in text\n    assert files[0][\"hash\"] in text\n\n\ndef test_chooser_md5_remote_fallback_to_sha256_inline_calculation(\n    env: MockEnv, pool: RepositoryPool, dist_hash_getter: DistributionHashGetter\n) -> None:\n    chooser = Chooser(pool, env)\n    package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"legacy\",\n        source_reference=\"foo\",\n        source_url=\"https://legacy.foo.bar/simple/\",\n    )\n    package.files = [\n        {\n            \"file\": filename,\n            \"hash\": (f\"sha256:{dist_hash_getter(filename).sha256}\"),\n        }\n        for filename in [f\"{package.name}-{package.version}.tar.gz\"]\n    ]\n    res = chooser.choose_for(package)\n    assert res.filename == \"demo-0.1.0.tar.gz\"\n"
  },
  {
    "path": "tests/installation/test_chooser_errors.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.installation.chooser import Chooser\n\n\nif TYPE_CHECKING:\n    from poetry.repositories.repository_pool import RepositoryPool\n    from poetry.utils.env import MockEnv\n\n\ndef test_chooser_no_links_found_error(env: MockEnv, pool: RepositoryPool) -> None:\n    chooser = Chooser(pool, env)\n    package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"legacy\",\n        source_reference=\"foo\",\n        source_url=\"https://legacy.foo.bar/simple/\",\n    )\n\n    unsupported_wheels = {\"demo-0.1.0-py3-none-any.whl\"}\n    error = chooser._no_links_found_error(\n        package=package,\n        links_seen=4,\n        wheels_skipped=3,\n        sdists_skipped=1,\n        unsupported_wheels=unsupported_wheels,\n    )\n    assert (\n        error.get_text(debug=True, strip=True)\n        == f\"\"\"\\\nUnable to find installation candidates for {package.name} ({package.version})\n\nThis is likely not a Poetry issue.\n\n  - 4 candidate(s) were identified for the package\n  - 3 wheel(s) were skipped due to your installer.no-binary policy\n  - 1 source distribution(s) were skipped due to your installer.only-binary policy\n  - 1 wheel(s) were skipped as your project's environment does not support the identified abi tags\n\nThe following wheel(s) were skipped as the current project environment does not support them due to abi compatibility \\\nissues.\n\n  - {\"  -\".join(unsupported_wheels)}\n\nIf you would like to see the supported tags in your project environment, you can execute the following command:\n\n    poetry debug tags\n\nSolutions:\nMake sure the lockfile is up-to-date. You can try one of the following;\n\n    1. Regenerate lockfile: poetry lock --no-cache --regenerate\n    2. Update package     : poetry update --no-cache {package.name}\n\nIf any of those solutions worked, you will have to clear your caches using (poetry cache clear --all).\n\nIf neither works, please first check to verify that the {package.name} has published wheels available from your configured \\\nsource ({package.source_reference}) that are compatible with your environment- ie. operating system, architecture \\\n(x86_64, arm64 etc.), python interpreter.\\\n\"\"\"\n    )\n\n    assert (\n        error.get_text(debug=False, strip=True)\n        == f\"\"\"\\\nUnable to find installation candidates for {package.name} ({package.version})\n\nThis is likely not a Poetry issue.\n\n  - 4 candidate(s) were identified for the package\n  - 3 wheel(s) were skipped due to your installer.no-binary policy\n  - 1 source distribution(s) were skipped due to your installer.only-binary policy\n  - 1 wheel(s) were skipped as your project's environment does not support the identified abi tags\n\nSolutions:\nMake sure the lockfile is up-to-date. You can try one of the following;\n\n    1. Regenerate lockfile: poetry lock --no-cache --regenerate\n    2. Update package     : poetry update --no-cache {package.name}\n\nIf any of those solutions worked, you will have to clear your caches using (poetry cache clear --all).\n\nIf neither works, please first check to verify that the {package.name} has published wheels available from your configured \\\nsource ({package.source_reference}) that are compatible with your environment- ie. operating system, architecture \\\n(x86_64, arm64 etc.), python interpreter.\n\nYou can also run your poetry command with -v to see more information.\\\n\"\"\"\n    )\n"
  },
  {
    "path": "tests/installation/test_executor.py",
    "content": "from __future__ import annotations\n\nimport csv\nimport json\nimport re\nimport shutil\nimport tempfile\n\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom build import BuildBackendException\nfrom build import ProjectBuilder\nfrom cleo.formatters.style import Style\nfrom cleo.io.buffered_io import BufferedIO\nfrom cleo.io.outputs.output import Verbosity\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.utils.utils import path_to_url\n\nfrom poetry.factory import Factory\nfrom poetry.installation.chef import Chef as BaseChef\nfrom poetry.installation.executor import Executor\nfrom poetry.installation.operations import Install\nfrom poetry.installation.operations import Uninstall\nfrom poetry.installation.operations import Update\nfrom poetry.installation.wheel_installer import WheelInstaller\nfrom poetry.repositories.repository_pool import RepositoryPool\nfrom poetry.utils.cache import ArtifactCache\nfrom poetry.utils.env import MockEnv\nfrom poetry.vcs.git.backend import Git\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n    from collections.abc import Mapping\n    from collections.abc import Sequence\n\n    from poetry.core.packages.package import PackageFile\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from poetry.installation.operations.operation import Operation\n    from poetry.repositories.pypi_repository import PyPiRepository\n    from poetry.utils.env import VirtualEnv\n    from tests.types import FixtureDirGetter\n\n\nclass Chef(BaseChef):\n    _directory_wheels: list[Path] | None = None\n    _sdist_wheels: list[Path] | None = None\n    _use_sdist = False\n\n    def set_directory_wheel(self, wheels: Path | list[Path]) -> None:\n        if not isinstance(wheels, list):\n            wheels = [wheels]\n\n        self._directory_wheels = wheels\n\n    def set_sdist_wheel(self, wheels: Path | list[Path]) -> None:\n        if not isinstance(wheels, list):\n            wheels = [wheels]\n\n        self._sdist_wheels = wheels\n\n    def _prepare_sdist(\n        self,\n        archive: Path,\n        destination: Path | None = None,\n        config_settings: Mapping[str, str | Sequence[str]] | None = None,\n        build_constraints: list[Dependency] | None = None,\n    ) -> Path:\n        if self._sdist_wheels is not None:\n            self._use_sdist = True\n\n        return super()._prepare_sdist(\n            archive,\n            destination,\n            config_settings=config_settings,\n            build_constraints=build_constraints,\n        )\n\n    def _prepare(\n        self,\n        directory: Path,\n        destination: Path,\n        *,\n        editable: bool = False,\n        config_settings: Mapping[str, str | Sequence[str]] | None = None,\n        build_constraints: list[Dependency] | None = None,\n    ) -> Path:\n        if self._use_sdist and self._sdist_wheels is not None:\n            self._use_sdist = False\n            wheel = self._sdist_wheels.pop(0)\n            self._sdist_wheels.append(wheel)\n\n            return wheel\n\n        if self._directory_wheels is not None:\n            wheel = self._directory_wheels.pop(0)\n            self._directory_wheels.append(wheel)\n\n            destination.mkdir(parents=True, exist_ok=True)\n            dst_wheel = destination / wheel.name\n            shutil.copyfile(wheel, dst_wheel)\n            return dst_wheel\n\n        return super()._prepare(\n            directory,\n            destination,\n            editable=editable,\n            config_settings=config_settings,\n            build_constraints=build_constraints,\n        )\n\n\n@pytest.fixture\ndef env(tmp_path: Path) -> MockEnv:\n    path = tmp_path / \".venv\"\n    path.mkdir(parents=True)\n\n    return MockEnv(path=path, is_venv=True)\n\n\n@pytest.fixture\ndef io() -> BufferedIO:\n    io = BufferedIO()\n    io.output.formatter.set_style(\"c1_dark\", Style(\"cyan\", options=[\"dark\"]))\n    io.output.formatter.set_style(\"c2_dark\", Style(\"default\", options=[\"bold\", \"dark\"]))\n    io.output.formatter.set_style(\"success_dark\", Style(\"green\", options=[\"dark\"]))\n    io.output.formatter.set_style(\"warning\", Style(\"yellow\"))\n\n    return io\n\n\n@pytest.fixture\ndef io_decorated() -> BufferedIO:\n    io = BufferedIO(decorated=True)\n    io.output.formatter.set_style(\"c1\", Style(\"cyan\"))\n    io.output.formatter.set_style(\"success\", Style(\"green\"))\n\n    return io\n\n\n@pytest.fixture\ndef io_not_decorated() -> BufferedIO:\n    io = BufferedIO(decorated=False)\n\n    return io\n\n\n@pytest.fixture\ndef pool(pypi_repository: PyPiRepository) -> RepositoryPool:\n    pool = RepositoryPool()\n\n    pypi_repository._fallback = True\n    pool.add_repository(pypi_repository)\n\n    return pool\n\n\n@pytest.fixture\ndef copy_wheel(tmp_path: Path, fixture_dir: FixtureDirGetter) -> Callable[[], Path]:\n    def _copy_wheel() -> Path:\n        tmp_name = tempfile.mktemp()\n        (tmp_path / tmp_name).mkdir()\n\n        shutil.copyfile(\n            fixture_dir(\"distributions\") / \"demo-0.1.2-py2.py3-none-any.whl\",\n            tmp_path / tmp_name / \"demo-0.1.2-py2.py3-none-any.whl\",\n        )\n        return tmp_path / tmp_name / \"demo-0.1.2-py2.py3-none-any.whl\"\n\n    return _copy_wheel\n\n\n@pytest.fixture\ndef wheel(copy_wheel: Callable[[], Path]) -> Iterator[Path]:\n    archive = copy_wheel()\n\n    yield archive\n\n    if archive.exists():\n        archive.unlink()\n\n\ndef test_execute_executes_a_batch_of_operations(\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n    copy_wheel: Callable[[], Path],\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    wheel_install = mocker.patch.object(WheelInstaller, \"install\")\n\n    config.merge({\"cache-dir\": str(tmp_path)})\n    artifact_cache = ArtifactCache(cache_dir=config.artifacts_cache_directory)\n\n    prepare_spy = mocker.spy(Chef, \"_prepare\")\n    chef = Chef(artifact_cache, env, Factory.create_pool(config))\n    chef.set_directory_wheel([copy_wheel(), copy_wheel()])\n    chef.set_sdist_wheel(copy_wheel())\n\n    io.set_verbosity(Verbosity.VERY_VERBOSE)\n\n    executor = Executor(env, pool, config, io)\n    executor._chef = chef\n\n    file_package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"file\",\n        source_url=(fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\")\n        .resolve()\n        .as_posix(),\n    )\n\n    directory_package = Package(\n        \"simple-project\",\n        \"1.2.3\",\n        source_type=\"directory\",\n        source_url=fixture_dir(\"simple_project\").resolve().as_posix(),\n    )\n\n    git_package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_url=\"https://github.com/demo/demo.git\",\n        develop=True,\n    )\n\n    return_code = executor.execute(\n        [\n            Install(Package(\"pytest\", \"3.5.1\")),\n            Uninstall(Package(\"attrs\", \"17.4.0\")),\n            Update(Package(\"requests\", \"2.18.3\"), Package(\"requests\", \"2.18.4\")),\n            Update(Package(\"pytest\", \"3.5.1\"), Package(\"pytest\", \"3.5.0\")),\n            Uninstall(Package(\"clikit\", \"0.2.3\")).skip(\"Not currently installed\"),\n            Install(file_package),\n            Install(directory_package),\n            Install(git_package),\n        ]\n    )\n\n    expected = f\"\"\"\nPackage operations: 4 installs, 2 updates, 1 removal\n\n  - Installing pytest (3.5.1)\n  - Removing attrs (17.4.0)\n  - Updating requests (2.18.3 -> 2.18.4)\n  - Downgrading pytest (3.5.1 -> 3.5.0)\n  - Installing demo (0.1.0 {file_package.source_url})\n  - Installing simple-project (1.2.3 {directory_package.source_url})\n  - Installing demo (0.1.0 master)\n\"\"\"\n\n    expected_lines = set(expected.splitlines())\n    output_lines = set(io.fetch_output().splitlines())\n    assert output_lines == expected_lines\n    assert wheel_install.call_count == 6\n    # 3 pip uninstalls: one for the remove operation and two for the update operations\n    assert len(env.executed) == 3\n    assert return_code == 0\n\n    assert prepare_spy.call_count == 2\n    assert {\n        args.args[1].name.split(\"-\")[0]: args.kwargs.get(\"editable\")\n        for args in prepare_spy.call_args_list\n    } == {\"simple_project\": False, \"demo\": True}\n\n\n@pytest.mark.parametrize(\"source_type\", [\"git\", \"file\", \"url\"])\ndef test_execute_build_config_settings_passed(\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n    copy_wheel: Callable[[], Path],\n    fixture_dir: FixtureDirGetter,\n    source_type: str,\n) -> None:\n    wheel_install = mocker.patch.object(WheelInstaller, \"install\")\n\n    config_settings_demo = {\"CC\": \"gcc\", \"--build-option\": [\"--one\", \"--two\"]}\n\n    config.merge(\n        {\n            \"cache-dir\": str(tmp_path),\n            \"installer\": {\"build-config-settings\": {\"demo\": config_settings_demo}},\n        }\n    )\n    artifact_cache = ArtifactCache(cache_dir=config.artifacts_cache_directory)\n\n    prepare_spy = mocker.spy(Chef, \"_prepare\")\n    chef = Chef(artifact_cache, env, Factory.create_pool(config))\n    chef.set_directory_wheel([copy_wheel(), copy_wheel()])\n    chef.set_sdist_wheel(copy_wheel())\n\n    executor = Executor(env, pool, config, io)\n    executor._chef = chef\n\n    directory_package = Package(\n        \"simple-project\",\n        \"1.2.3\",\n        source_type=\"directory\",\n        source_url=fixture_dir(\"simple_project\").resolve().as_posix(),\n    )\n\n    if source_type == \"git\":\n        ref = \"master\"\n        demo_package = Package(\n            \"demo\",\n            \"0.1.0\",\n            source_type=\"git\",\n            source_reference=ref,\n            source_url=\"https://github.com/demo/demo.git\",\n        )\n        version_info = ref\n    elif source_type == \"file\":\n        url = (fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\").resolve().as_posix()\n        demo_package = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=url)\n        version_info = url\n    elif source_type == \"url\":\n        url = \"https://files.pythonhosted.org/demo-0.1.0.tar.gz\"\n        demo_package = Package(\"demo\", \"0.1.0\", source_type=\"url\", source_url=url)\n        version_info = url\n    else:\n        raise ValueError\n\n    return_code = executor.execute(\n        [\n            Install(directory_package),\n            Install(demo_package),\n        ]\n    )\n\n    expected = f\"\"\"\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing simple-project (1.2.3 {directory_package.source_url})\n  - Installing demo (0.1.0 {version_info})\n\"\"\"\n\n    expected_lines = set(expected.splitlines())\n    output_lines = set(io.fetch_output().splitlines())\n    assert output_lines == expected_lines\n    assert wheel_install.call_count == 2\n    assert return_code == 0\n\n    assert prepare_spy.call_count == 2\n    assert {\n        args.args[1].name.split(\"-\")[0]: args.kwargs.get(\"config_settings\")\n        for args in prepare_spy.call_args_list\n    } == {\"simple_project\": None, \"demo\": config_settings_demo}\n\n\n@pytest.mark.parametrize(\"source_type\", [\"git\", \"file\"])\ndef test_execute_build_constraints_passed(\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n    copy_wheel: Callable[[], Path],\n    fixture_dir: FixtureDirGetter,\n    source_type: str,\n) -> None:\n    wheel_install = mocker.patch.object(WheelInstaller, \"install\")\n\n    artifact_cache = ArtifactCache(cache_dir=config.artifacts_cache_directory)\n\n    prepare_spy = mocker.spy(Chef, \"_prepare\")\n    chef = Chef(artifact_cache, env, Factory.create_pool(config))\n    chef.set_directory_wheel([copy_wheel(), copy_wheel()])\n    chef.set_sdist_wheel(copy_wheel())\n\n    build_constraints_demo = [Dependency(\"setuptools\", \"<75\")]\n    build_constraints = {canonicalize_name(\"demo\"): build_constraints_demo}\n    executor = Executor(env, pool, config, io, build_constraints=build_constraints)\n    executor._chef = chef\n\n    directory_package = Package(\n        \"simple-project\",\n        \"1.2.3\",\n        source_type=\"directory\",\n        source_url=fixture_dir(\"simple_project\").resolve().as_posix(),\n    )\n\n    if source_type == \"git\":\n        ref = \"master\"\n        demo_package = Package(\n            \"demo\",\n            \"0.1.0\",\n            source_type=\"git\",\n            source_reference=ref,\n            source_url=\"https://github.com/demo/demo.git\",\n        )\n        version_info = ref\n    elif source_type == \"file\":\n        url = (fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\").resolve().as_posix()\n        demo_package = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=url)\n        version_info = url\n    elif source_type == \"url\":\n        url = \"https://files.pythonhosted.org/demo-0.1.0.tar.gz\"\n        demo_package = Package(\"demo\", \"0.1.0\", source_type=\"url\", source_url=url)\n        version_info = url\n    else:\n        raise ValueError\n\n    return_code = executor.execute(\n        [\n            Install(directory_package),\n            Install(demo_package),\n        ]\n    )\n\n    expected = f\"\"\"\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing simple-project (1.2.3 {directory_package.source_url})\n  - Installing demo (0.1.0 {version_info})\n\"\"\"\n\n    expected_lines = set(expected.splitlines())\n    output_lines = set(io.fetch_output().splitlines())\n    assert output_lines == expected_lines\n    assert wheel_install.call_count == 2\n    assert return_code == 0\n\n    assert prepare_spy.call_count == 2\n    assert {\n        args.args[1].name.split(\"-\")[0]: args.kwargs.get(\"build_constraints\")\n        for args in prepare_spy.call_args_list\n    } == {\"simple_project\": None, \"demo\": build_constraints_demo}\n\n\n@pytest.mark.parametrize(\n    \"operations, has_warning\",\n    [\n        (\n            [Install(Package(\"black\", \"21.11b0\")), Install(Package(\"pytest\", \"3.5.1\"))],\n            True,\n        ),\n        (\n            [\n                Uninstall(Package(\"black\", \"21.11b0\")),\n                Uninstall(Package(\"pytest\", \"3.5.1\")),\n            ],\n            False,\n        ),\n        (\n            [\n                Update(Package(\"black\", \"19.10b0\"), Package(\"black\", \"21.11b0\")),\n                Update(Package(\"pytest\", \"3.5.0\"), Package(\"pytest\", \"3.5.1\")),\n            ],\n            True,\n        ),\n    ],\n)\ndef test_execute_prints_warning_for_yanked_package(\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n    operations: list[Operation],\n    has_warning: bool,\n) -> None:\n    config.merge({\"cache-dir\": str(tmp_path)})\n\n    executor = Executor(env, pool, config, io)\n\n    return_code = executor.execute(operations)\n\n    expected = (\n        \"Warning: The file chosen for install of black 21.11b0 \"\n        \"(black-21.11b0-py3-none-any.whl) is yanked. Reason for being yanked: \"\n        \"Broken regex dependency. Use 21.11b1 instead.\"\n    )\n    output = io.fetch_output()\n    error = io.fetch_error()\n    assert return_code == 0, f\"\\noutput: {output}\\nerror: {error}\\n\"\n    assert \"pytest\" not in error\n    if has_warning:\n        assert expected in error\n        assert error.count(\"is yanked\") == 1\n    else:\n        assert expected not in error\n        assert error.count(\"yanked\") == 0\n\n\n@pytest.mark.skip(reason=\"https://github.com/python-poetry/poetry/issues/7983\")\ndef test_execute_prints_warning_for_invalid_wheels(\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n) -> None:\n    config.merge({\"cache-dir\": str(tmp_path)})\n\n    executor = Executor(env, pool, config, io)\n\n    base_url = \"https://files.pythonhosted.org/\"\n    wheel1 = \"demo_invalid_record-0.1.0-py2.py3-none-any.whl\"\n    wheel2 = \"demo_invalid_record2-0.1.0-py2.py3-none-any.whl\"\n    return_code = executor.execute(\n        [\n            Install(\n                Package(\n                    \"demo-invalid-record\",\n                    \"0.1.0\",\n                    source_type=\"url\",\n                    source_url=f\"{base_url}/{wheel1}\",\n                )\n            ),\n            Install(\n                Package(\n                    \"demo-invalid-record2\",\n                    \"0.1.0\",\n                    source_type=\"url\",\n                    source_url=f\"{base_url}/{wheel2}\",\n                )\n            ),\n        ]\n    )\n\n    warning1 = f\"\"\"\\\n<warning>Warning: Validation of the RECORD file of {wheel1} failed.\\\n Please report to the maintainers of that package so they can fix their build process.\\\n Details:\nIn .*?{wheel1}, demo/__init__.py is not mentioned in RECORD\nIn .*?{wheel1}, demo_invalid_record-0.1.0.dist-info/WHEEL is not mentioned in RECORD\n\"\"\"\n\n    warning2 = f\"\"\"\\\n<warning>Warning: Validation of the RECORD file of {wheel2} failed.\\\n Please report to the maintainers of that package so they can fix their build process.\\\n Details:\nIn .*?{wheel2}, hash / size of demo_invalid_record2-0.1.0.dist-info/METADATA didn't\\\n match RECORD\n\"\"\"\n\n    output = io.fetch_output()\n    error = io.fetch_error()\n    assert return_code == 0, f\"\\noutput: {output}\\nerror: {error}\\n\"\n    assert re.match(f\"{warning1}\\n{warning2}\", error) or re.match(\n        f\"{warning2}\\n{warning1}\", error\n    ), error\n\n\ndef test_execute_shows_skipped_operations_if_verbose(\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    config_cache_dir: Path,\n    env: MockEnv,\n) -> None:\n    config.merge({\"cache-dir\": config_cache_dir.as_posix()})\n\n    executor = Executor(env, pool, config, io)\n    executor.verbose()\n\n    assert (\n        executor.execute(\n            [Uninstall(Package(\"clikit\", \"0.2.3\")).skip(\"Not currently installed\")]\n        )\n        == 0\n    )\n\n    expected = \"\"\"\nPackage operations: 0 installs, 0 updates, 0 removals, 1 skipped\n\n  - Removing clikit (0.2.3): Skipped for the following reason: Not currently installed\n\"\"\"\n    assert io.fetch_output() == expected\n    assert len(env.executed) == 0\n\n\ndef test_execute_should_show_errors(\n    config: Config,\n    pool: RepositoryPool,\n    mocker: MockerFixture,\n    io: BufferedIO,\n    env: MockEnv,\n) -> None:\n    executor = Executor(env, pool, config, io)\n    executor.verbose()\n\n    mocker.patch.object(executor, \"_install\", side_effect=Exception(\"It failed!\"))\n\n    assert executor.execute([Install(Package(\"clikit\", \"0.2.3\"))]) == 1\n\n    expected = \"\"\"\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing clikit (0.2.3)\n\n  Exception\n\n  It failed!\n\"\"\"\n\n    assert expected in io.fetch_output()\n\n\ndef test_execute_works_with_ansi_output(\n    config: Config,\n    pool: RepositoryPool,\n    io_decorated: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n) -> None:\n    config.merge({\"cache-dir\": str(tmp_path)})\n\n    executor = Executor(env, pool, config, io_decorated)\n\n    return_code = executor.execute(\n        [\n            Install(Package(\"cleo\", \"1.0.0a5\")),\n        ]\n    )\n\n    # fmt: off\n    expected = [\n        \"\\x1b[39;1mPackage operations\\x1b[39;22m: \\x1b[34m1\\x1b[39m install, \\x1b[34m0\\x1b[39m updates, \\x1b[34m0\\x1b[39m removals\",\n        \"\\x1b[34;1m-\\x1b[39;22m \\x1b[39mInstalling \\x1b[39m\\x1b[36mcleo\\x1b[39m\\x1b[39m (\\x1b[39m\\x1b[39;1m1.0.0a5\\x1b[39;22m\\x1b[39m)\\x1b[39m: \\x1b[34mPending...\\x1b[39m\",\n        \"\\x1b[34;1m-\\x1b[39;22m \\x1b[39mInstalling \\x1b[39m\\x1b[36mcleo\\x1b[39m\\x1b[39m (\\x1b[39m\\x1b[39;1m1.0.0a5\\x1b[39;22m\\x1b[39m)\\x1b[39m: \\x1b[34mDownloading...\\x1b[39m\",\n        \"\\x1b[34;1m-\\x1b[39;22m \\x1b[39mInstalling \\x1b[39m\\x1b[36mcleo\\x1b[39m\\x1b[39m (\\x1b[39m\\x1b[39;1m1.0.0a5\\x1b[39;22m\\x1b[39m)\\x1b[39m: \\x1b[34mInstalling...\\x1b[39m\",\n        \"\\x1b[32;1m-\\x1b[39;22m \\x1b[39mInstalling \\x1b[39m\\x1b[36mcleo\\x1b[39m\\x1b[39m (\\x1b[39m\\x1b[32m1.0.0a5\\x1b[39m\\x1b[39m)\\x1b[39m\",  # finished\n    ]\n    # fmt: on\n\n    output = io_decorated.fetch_output()\n    # hint: use print(repr(output)) if you need to debug this\n\n    for line in expected:\n        assert line in output\n    assert return_code == 0\n\n\ndef test_execute_works_with_no_ansi_output(\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io_not_decorated: BufferedIO,\n    tmp_path: Path,\n    env: MockEnv,\n) -> None:\n    config.merge({\"cache-dir\": str(tmp_path)})\n\n    executor = Executor(env, pool, config, io_not_decorated)\n\n    return_code = executor.execute(\n        [\n            Install(Package(\"cleo\", \"1.0.0a5\")),\n        ]\n    )\n\n    expected = \"\"\"\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing cleo (1.0.0a5)\n\"\"\"\n    expected_lines = set(expected.splitlines())\n    output_lines = set(io_not_decorated.fetch_output().splitlines())\n    assert output_lines == expected_lines\n    assert return_code == 0\n\n\ndef test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_interrupt(\n    config: Config,\n    pool: RepositoryPool,\n    mocker: MockerFixture,\n    io: BufferedIO,\n    env: MockEnv,\n) -> None:\n    executor = Executor(env, pool, config, io)\n    executor.verbose()\n\n    # A return code of -2 means KeyboardInterrupt in the pip subprocess\n    mocker.patch.object(executor, \"_install\", return_value=-2)\n\n    assert executor.execute([Install(Package(\"clikit\", \"0.2.3\"))]) == 1\n\n    expected = \"\"\"\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing clikit (0.2.3)\n  - Installing clikit (0.2.3): Cancelled\n\"\"\"\n\n    assert io.fetch_output() == expected\n\n\ndef test_execute_should_gracefully_handle_io_error(\n    config: Config,\n    pool: RepositoryPool,\n    mocker: MockerFixture,\n    io: BufferedIO,\n    env: MockEnv,\n) -> None:\n    executor = Executor(env, pool, config, io)\n    executor.verbose()\n\n    original_write_line = executor._io.write_line\n\n    def write_line(string: str, **kwargs: Any) -> None:\n        # Simulate UnicodeEncodeError\n        string = string.replace(\"-\", \"•\")\n        string.encode(\"ascii\")\n        original_write_line(string, **kwargs)\n\n    mocker.patch.object(io, \"write_line\", side_effect=write_line)\n\n    assert executor.execute([Install(Package(\"clikit\", \"0.2.3\"))]) == 1\n\n    expected = r\"\"\"\nPackage operations: 1 install, 0 updates, 0 removals\n\n\n\\s*Unicode\\w+Error\n\"\"\"\n\n    assert re.match(expected, io.fetch_output())\n\n\ndef test_executor_should_delete_incomplete_downloads(\n    config: Config,\n    io: BufferedIO,\n    tmp_path: Path,\n    mocker: MockerFixture,\n    pool: RepositoryPool,\n    env: MockEnv,\n) -> None:\n    cached_archive = tmp_path / \"tomlkit-0.5.3-py2.py3-none-any.whl\"\n\n    def download_fail(*_: Any) -> None:\n        cached_archive.touch()  # broken archive\n        raise Exception(\"Download error\")\n\n    mocker.patch(\n        \"poetry.installation.executor.Executor._download_archive\",\n        side_effect=download_fail,\n    )\n    mocker.patch(\n        \"poetry.utils.cache.ArtifactCache._get_cached_archive\",\n        return_value=None,\n    )\n    mocker.patch(\n        \"poetry.utils.cache.ArtifactCache.get_cache_directory_for_link\",\n        return_value=tmp_path,\n    )\n\n    config.merge({\"cache-dir\": str(tmp_path)})\n\n    executor = Executor(env, pool, config, io)\n\n    with pytest.raises(Exception, match=\"Download error\"):\n        executor._download(Install(Package(\"tomlkit\", \"0.5.3\")))\n\n    assert not cached_archive.exists()\n\n\ndef verify_installed_distribution(\n    venv: VirtualEnv, package: Package, url_reference: dict[str, Any] | None = None\n) -> None:\n    distributions = list(venv.site_packages.distributions(name=package.name))\n    assert len(distributions) == 1\n\n    distribution = distributions[0]\n    metadata = distribution.metadata\n    assert metadata\n    assert metadata[\"Name\"] == package.name\n    assert metadata[\"Version\"] == package.version.text\n\n    direct_url_file = distribution._path.joinpath(  # type: ignore[attr-defined]\n        \"direct_url.json\"\n    )\n\n    if url_reference is not None:\n        record_file = distribution._path.joinpath(  # type: ignore[attr-defined]\n            \"RECORD\"\n        )\n        with open(record_file, encoding=\"utf-8\", newline=\"\") as f:\n            reader = csv.reader(f)\n            rows = list(reader)\n        assert all(len(row) == 3 for row in rows)\n        record_entries = {row[0] for row in rows}\n        direct_url_entry = direct_url_file.relative_to(record_file.parent.parent)\n        assert direct_url_file.exists()\n        assert str(direct_url_entry) in record_entries\n        assert json.loads(direct_url_file.read_text(encoding=\"utf-8\")) == url_reference\n    else:\n        assert not direct_url_file.exists()\n\n\n@pytest.mark.parametrize(\n    \"package\",\n    [\n        Package(\"demo\", \"0.1.0\"),  # PyPI\n        Package(  # private source\n            \"demo\",\n            \"0.1.0\",\n            source_type=\"legacy\",\n            source_url=\"http://localhost:3141/root/pypi/+simple\",\n            source_reference=\"private\",\n        ),\n    ],\n)\ndef test_executor_should_not_write_pep610_url_references_for_cached_package(\n    package: Package,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n) -> None:\n    link_cached = fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": (\n                \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n            ),\n        }\n    ]\n\n    mocker.patch(\n        \"poetry.installation.executor.Executor._download\", return_value=link_cached\n    )\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor.execute([Install(package)])\n    verify_installed_distribution(tmp_venv, package)\n    assert link_cached.exists(), \"cached file should not be deleted\"\n\n\ndef test_executor_should_write_pep610_url_references_for_wheel_files(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    url = (fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\").resolve()\n    package = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=url.as_posix())\n    # Set package.files so the executor will attempt to hash the package\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": (\n                \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n            ),\n        }\n    ]\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor.execute([Install(package)])\n    expected_url_reference = {\n        \"archive_info\": {\n            \"hashes\": {\n                \"sha256\": (\n                    \"70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n                )\n            },\n        },\n        \"url\": url.as_uri(),\n    }\n    verify_installed_distribution(tmp_venv, package, expected_url_reference)\n    assert url.exists(), \"source file should not be deleted\"\n\n\ndef test_executor_should_write_pep610_url_references_for_non_wheel_files(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    url = (fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\").resolve()\n    package = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=url.as_posix())\n    # Set package.files so the executor will attempt to hash the package\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0.tar.gz\",\n            \"hash\": (\n                \"sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n            ),\n        }\n    ]\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor.execute([Install(package)])\n    expected_url_reference = {\n        \"archive_info\": {\n            \"hashes\": {\n                \"sha256\": (\n                    \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n                )\n            },\n        },\n        \"url\": url.as_uri(),\n    }\n    verify_installed_distribution(tmp_venv, package, expected_url_reference)\n    assert url.exists(), \"source file should not be deleted\"\n\n\ndef test_executor_should_write_pep610_url_references_for_directories(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    url = (fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\").resolve()\n    package = Package(\n        \"demo\", \"0.1.2\", source_type=\"directory\", source_url=url.as_posix()\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    prepare_spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n    verify_installed_distribution(\n        tmp_venv, package, {\"dir_info\": {}, \"url\": url.as_uri()}\n    )\n    assert not prepare_spy.spy_return.exists(), \"archive not cleaned up\"\n\n\ndef test_executor_should_write_pep610_url_references_for_editable_directories(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    url = (fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\").resolve()\n    package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"directory\",\n        source_url=url.as_posix(),\n        develop=True,\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    prepare_spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n    verify_installed_distribution(\n        tmp_venv, package, {\"dir_info\": {\"editable\": True}, \"url\": url.as_uri()}\n    )\n    assert not prepare_spy.spy_return.exists(), \"archive not cleaned up\"\n\n\n@pytest.mark.parametrize(\"is_artifact_cached\", [False, True])\ndef test_executor_should_write_pep610_url_references_for_wheel_urls(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    is_artifact_cached: bool,\n) -> None:\n    if is_artifact_cached:\n        link_cached = fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n        mocker.patch(\n            \"poetry.utils.cache.ArtifactCache.get_cached_archive_for_link\",\n            return_value=link_cached,\n        )\n    download_spy = mocker.spy(Executor, \"_download_archive\")\n\n    package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"url\",\n        source_url=\"https://files.pythonhosted.org/demo-0.1.0-py2.py3-none-any.whl\",\n    )\n    # Set package.files so the executor will attempt to hash the package\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": (\n                \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n            ),\n        }\n    ]\n\n    executor = Executor(tmp_venv, pool, config, io)\n    operation = Install(package)\n    executor.execute([operation])\n    expected_url_reference = {\n        \"archive_info\": {\n            \"hashes\": {\n                \"sha256\": (\n                    \"70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\"\n                )\n            },\n        },\n        \"url\": package.source_url,\n    }\n    verify_installed_distribution(tmp_venv, package, expected_url_reference)\n    if is_artifact_cached:\n        download_spy.assert_not_called()\n    else:\n        assert package.source_url is not None\n        download_spy.assert_called_once_with(\n            mocker.ANY,\n            operation,\n            package.source_url,\n            dest=mocker.ANY,\n        )\n        dest = download_spy.call_args.args[3]\n        assert dest.exists(), \"cached file should not be deleted\"\n\n\n@pytest.mark.parametrize(\n    (\n        \"is_sdist_cached\",\n        \"is_wheel_cached\",\n        \"expect_artifact_building\",\n        \"expect_artifact_download\",\n    ),\n    [\n        (True, False, True, False),\n        (True, True, False, False),\n        (False, False, True, True),\n        (False, True, False, True),\n    ],\n)\ndef test_executor_should_write_pep610_url_references_for_non_wheel_urls(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    is_sdist_cached: bool,\n    is_wheel_cached: bool,\n    expect_artifact_building: bool,\n    expect_artifact_download: bool,\n) -> None:\n    built_wheel = fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n    mock_prepare = mocker.patch(\n        \"poetry.installation.chef.Chef._prepare\",\n        return_value=built_wheel,\n    )\n    download_spy = mocker.spy(Executor, \"_download_archive\")\n\n    if is_sdist_cached or is_wheel_cached:\n        cached_sdist = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n        cached_wheel = fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n\n        def mock_get_cached_archive_func(\n            _cache_dir: Path, *, strict: bool, **__: Any\n        ) -> Path | None:\n            if is_wheel_cached and not strict:\n                return cached_wheel\n            if is_sdist_cached:\n                return cached_sdist\n            return None\n\n        mocker.patch(\n            \"poetry.utils.cache.ArtifactCache._get_cached_archive\",\n            side_effect=mock_get_cached_archive_func,\n        )\n\n    package = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"url\",\n        source_url=\"https://files.pythonhosted.org/demo-0.1.0.tar.gz\",\n    )\n    # Set package.files so the executor will attempt to hash the package\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0.tar.gz\",\n            \"hash\": (\n                \"sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n            ),\n        }\n    ]\n\n    executor = Executor(tmp_venv, pool, config, io)\n    operation = Install(package)\n    executor.execute([operation])\n    expected_url_reference = {\n        \"archive_info\": {\n            \"hashes\": {\n                \"sha256\": (\n                    \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n                )\n            },\n        },\n        \"url\": package.source_url,\n    }\n    verify_installed_distribution(tmp_venv, package, expected_url_reference)\n\n    if expect_artifact_building:\n        mock_prepare.assert_called_once()\n    else:\n        mock_prepare.assert_not_called()\n\n    if expect_artifact_download:\n        assert package.source_url is not None\n        download_spy.assert_called_once_with(\n            mocker.ANY, operation, package.source_url, dest=mocker.ANY\n        )\n        dest = download_spy.call_args.args[3]\n        assert dest.exists(), \"cached file should not be deleted\"\n    else:\n        download_spy.assert_not_called()\n\n\n@pytest.mark.parametrize(\n    \"source_url,written_source_url\",\n    [\n        (\"https://github.com/demo/demo.git\", \"https://github.com/demo/demo.git\"),\n        (\"git@github.com:demo/demo.git\", \"ssh://git@github.com/demo/demo.git\"),\n    ],\n)\n@pytest.mark.parametrize(\"is_artifact_cached\", [False, True])\ndef test_executor_should_write_pep610_url_references_for_git(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    source_url: str,\n    written_source_url: str,\n    is_artifact_cached: bool,\n) -> None:\n    if is_artifact_cached:\n        link_cached = fixture_dir(\"distributions\") / \"demo-0.1.2-py2.py3-none-any.whl\"\n        mocker.patch(\n            \"poetry.utils.cache.ArtifactCache.get_cached_archive_for_git\",\n            return_value=link_cached,\n        )\n    clone_spy = mocker.spy(Git, \"clone\")\n\n    source_resolved_reference = \"123456\"\n    source_url = source_url\n\n    package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=source_resolved_reference,\n        source_url=source_url,\n    )\n\n    assert package.source_url == written_source_url\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    prepare_spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n    verify_installed_distribution(\n        tmp_venv,\n        package,\n        {\n            \"vcs_info\": {\n                \"vcs\": \"git\",\n                \"requested_revision\": \"master\",\n                \"commit_id\": \"123456\",\n            },\n            \"url\": package.source_url,\n        },\n    )\n\n    if is_artifact_cached:\n        clone_spy.assert_not_called()\n        prepare_spy.assert_not_called()\n    else:\n        clone_spy.assert_called_once_with(\n            url=package.source_url,\n            source_root=mocker.ANY,\n            revision=source_resolved_reference,\n        )\n        prepare_spy.assert_called_once()\n        assert prepare_spy.spy_return.exists(), \"cached file should not be deleted\"\n        assert (prepare_spy.spy_return.parent / \".created_from_git_dependency\").exists()\n\n\ndef test_executor_should_write_pep610_url_references_for_editable_git(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    source_resolved_reference = \"123456\"\n    source_url = \"https://github.com/demo/demo.git\"\n\n    package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=source_resolved_reference,\n        source_url=source_url,\n        develop=True,\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    prepare_spy = mocker.spy(chef, \"prepare\")\n    cache_spy = mocker.spy(artifact_cache, \"get_cached_archive_for_git\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n    assert package.source_url is not None\n    verify_installed_distribution(\n        tmp_venv,\n        package,\n        {\n            \"dir_info\": {\"editable\": True},\n            \"url\": Path(package.source_url).as_uri(),\n        },\n    )\n\n    cache_spy.assert_not_called()\n    prepare_spy.assert_called_once()\n    assert not prepare_spy.spy_return.exists(), \"editable git should not be cached\"\n    assert not (prepare_spy.spy_return.parent / \".created_from_git_dependency\").exists()\n\n\ndef test_executor_should_append_subdirectory_for_git(\n    mocker: MockerFixture,\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n) -> None:\n    package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/demo/subdirectories.git\",\n        source_subdirectory=\"two\",\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n\n    archive_arg = spy.call_args[0][0]\n    assert archive_arg == tmp_venv.path / \"src/subdirectories/two\"\n\n\ndef test_executor_should_install_multiple_packages_from_same_git_repository(\n    mocker: MockerFixture,\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n) -> None:\n    package_a = Package(\n        \"package_a\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/demo/subdirectories.git\",\n        source_subdirectory=\"package_a\",\n    )\n    package_b = Package(\n        \"package_b\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/demo/subdirectories.git\",\n        source_subdirectory=\"package_b\",\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package_a), Install(package_b)])\n\n    archive_arg = spy.call_args_list[0][0][0]\n    assert archive_arg == tmp_venv.path / \"src/subdirectories/package_a\"\n\n    archive_arg = spy.call_args_list[1][0][0]\n    assert archive_arg == tmp_venv.path / \"src/subdirectories/package_b\"\n\n\ndef test_executor_should_install_multiple_packages_from_forked_git_repository(\n    mocker: MockerFixture,\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n) -> None:\n    package_a = Package(\n        \"one\",\n        \"1.0.0\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/demo/subdirectories.git\",\n        source_subdirectory=\"one\",\n    )\n    package_b = Package(\n        \"two\",\n        \"2.0.0\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/forked_demo/subdirectories.git\",\n        source_subdirectory=\"two\",\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n    prepare_spy = mocker.spy(chef, \"prepare\")\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package_a), Install(package_b)])\n\n    # Verify that the repo for package_a is not re-used for package_b.\n    # both repos must be cloned serially into separate directories.\n    # If so, executor.prepare() will be called twice.\n    assert prepare_spy.call_count == 2\n\n\ndef test_executor_should_write_pep610_url_references_for_git_with_subdirectories(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    artifact_cache: ArtifactCache,\n    io: BufferedIO,\n    wheel: Path,\n) -> None:\n    package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n        source_url=\"https://github.com/demo/subdirectories.git\",\n        source_subdirectory=\"two\",\n    )\n\n    chef = Chef(artifact_cache, tmp_venv, Factory.create_pool(config))\n    chef.set_directory_wheel(wheel)\n\n    executor = Executor(tmp_venv, pool, config, io)\n    executor._chef = chef\n    executor.execute([Install(package)])\n    verify_installed_distribution(\n        tmp_venv,\n        package,\n        {\n            \"vcs_info\": {\n                \"vcs\": \"git\",\n                \"requested_revision\": \"master\",\n                \"commit_id\": \"123456\",\n            },\n            \"url\": package.source_url,\n            \"subdirectory\": package.source_subdirectory,\n        },\n    )\n\n\n@pytest.mark.parametrize(\n    (\"max_workers\", \"cpu_count\", \"side_effect\", \"expected_workers\"),\n    [\n        (None, 3, None, 7),\n        (3, 4, None, 3),\n        (8, 3, None, 7),\n        (None, 8, NotImplementedError(), 5),\n        (2, 8, NotImplementedError(), 2),\n        (8, 8, NotImplementedError(), 5),\n    ],\n)\ndef test_executor_should_be_initialized_with_correct_workers(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    mocker: MockerFixture,\n    max_workers: int | None,\n    cpu_count: int | None,\n    side_effect: Exception | None,\n    expected_workers: int,\n) -> None:\n    config.merge({\"installer\": {\"max-workers\": max_workers}})\n\n    mocker.patch(\"os.cpu_count\", return_value=cpu_count, side_effect=side_effect)\n\n    executor = Executor(tmp_venv, pool, config, io)\n\n    assert executor._max_workers == expected_workers\n\n\n@pytest.mark.parametrize(\"failing_method\", [\"build\", \"get_requires_for_build\"])\n@pytest.mark.parametrize(\n    \"exception\",\n    [\n        CalledProcessError(1, [\"pip\"], output=b\"original error\"),\n        Exception(\"original error\"),\n    ],\n)\n@pytest.mark.parametrize(\"editable\", [False, True])\n@pytest.mark.parametrize(\"source_type\", [\"directory\", \"git\", \"git subdirectory\"])\ndef test_build_backend_errors_are_reported_correctly_if_caused_by_subprocess(\n    failing_method: str,\n    exception: Exception,\n    editable: bool,\n    source_type: str,\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    env: MockEnv,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    error = BuildBackendException(exception, description=\"hide the original error\")\n    mocker.patch.object(ProjectBuilder, failing_method, side_effect=error)\n    io.set_verbosity(Verbosity.NORMAL)\n\n    executor = Executor(env, pool, config, io)\n\n    package_name = \"simple-project\"\n    package_version = \"1.2.3\"\n    source_reference: str | None = None\n    source_sub_directory: str | None = None\n    if source_type == \"directory\":\n        source_url = fixture_dir(\"simple_project\").resolve().as_posix()\n        source_resolved_reference = None\n        pip_url = path_to_url(source_url)\n        pip_editable_requirement = source_url\n    elif source_type == \"git\":\n        source_url = \"https://github.com/demo/demo.git\"\n        source_reference = \"v2.0\"\n        source_resolved_reference = \"12345678\"\n        pip_url = f\"git+{source_url}@{source_reference}\"\n        pip_editable_requirement = f\"{pip_url}#egg={package_name}\"\n    elif source_type == \"git subdirectory\":\n        source_type = \"git\"\n        source_sub_directory = \"one\"\n        source_url = \"https://github.com/demo/subdirectories.git\"\n        source_reference = \"v2.0\"\n        source_resolved_reference = \"12345678\"\n        pip_base_url = f\"git+{source_url}@{source_reference}\"\n        pip_url = f\"{pip_base_url}#subdirectory={source_sub_directory}\"\n        pip_editable_requirement = (\n            f\"{pip_base_url}#egg={package_name}&subdirectory={source_sub_directory}\"\n        )\n    else:\n        raise ValueError(f\"Unknown source type: {source_type}\")\n    package = Package(\n        package_name,\n        package_version,\n        source_type=source_type,\n        source_url=source_url,\n        source_reference=source_reference,\n        source_resolved_reference=source_resolved_reference,\n        source_subdirectory=source_sub_directory,\n        develop=editable,\n    )\n    # must not be included in the error message\n    package.python_versions = \">=3.7\"\n\n    return_code = executor.execute([Install(package)])\n\n    assert return_code == 1\n\n    assert package.source_url is not None\n    if editable:\n        pip_command = \"pip wheel --no-cache-dir --use-pep517 --editable\"\n        requirement = pip_editable_requirement\n        if source_type == \"directory\":\n            assert Path(requirement).exists()\n    else:\n        pip_command = \"pip wheel --no-cache-dir --use-pep517\"\n        requirement = f\"{package_name} @ {pip_url}\"\n\n    version_details = package.source_resolved_reference or package.source_url\n    expected_source_string = f\"{package_name} ({package_version} {version_details})\"\n    expected_pip_command = f'{pip_command} \"{requirement}\"'\n\n    expected_output = f\"\"\"\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing {expected_source_string}\n\nPEP517 build of a dependency failed\n\nhide the original error\n\"\"\"\n\n    if isinstance(exception, CalledProcessError):\n        expected_output += (\n            \"\\n    | Command '['pip']' returned non-zero exit status 1.\"\n            \"\\n    | \"\n            \"\\n    | original error\"\n            \"\\n\"\n        )\n\n    expected_output += f\"\"\"\nNote: This error originates from the build backend, and is likely not a problem \\\nwith poetry but one of the following issues with {expected_source_string}\n\n  - not supporting PEP 517 builds\n  - not specifying PEP 517 build requirements correctly\n  - the build requirements are incompatible with your operating system or Python version\n  - the build requirements are missing system dependencies (eg: compilers, libraries, headers).\n\nYou can verify this by running {expected_pip_command}.\n\n\"\"\"\n\n    assert io.fetch_output() == expected_output\n\n\n@pytest.mark.parametrize(\"encoding\", [\"utf-8\", \"latin-1\"])\n@pytest.mark.parametrize(\"stderr\", [None, \"Errör on stderr\"])\ndef test_build_backend_errors_are_reported_correctly_if_caused_by_subprocess_encoding(\n    encoding: str,\n    stderr: str | None,\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    env: MockEnv,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    \"\"\"Test that the output of the subprocess is decoded correctly.\"\"\"\n    stdout = \"Errör on stdout\"\n    error = BuildBackendException(\n        CalledProcessError(\n            1,\n            [\"pip\"],\n            output=stdout.encode(encoding),\n            stderr=stderr.encode(encoding) if stderr else None,\n        )\n    )\n    mocker.patch.object(ProjectBuilder, \"get_requires_for_build\", side_effect=error)\n    io.set_verbosity(Verbosity.NORMAL)\n\n    executor = Executor(env, pool, config, io)\n\n    directory_package = Package(\n        \"simple-project\",\n        \"1.2.3\",\n        source_type=\"directory\",\n        source_url=fixture_dir(\"simple_project\").resolve().as_posix(),\n    )\n\n    return_code = executor.execute([Install(directory_package)])\n\n    assert return_code == 1\n    assert (stderr or stdout) in io.fetch_output()\n\n\ndef test_build_system_requires_not_available(\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    env: MockEnv,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    io.set_verbosity(Verbosity.NORMAL)\n\n    executor = Executor(env, pool, config, io)\n\n    package_name = \"simple-project\"\n    package_version = \"1.2.3\"\n    directory_package = Package(\n        package_name,\n        package_version,\n        source_type=\"directory\",\n        source_url=fixture_dir(\"build_system_requires_not_available\")\n        .resolve()\n        .as_posix(),\n    )\n\n    return_code = executor.execute([Install(directory_package)])\n\n    assert return_code == 1\n\n    package_url = directory_package.source_url\n    expected_start = f\"\"\"\\\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing {package_name} ({package_version} {package_url})\n\n  SolveFailureError\n\n  Because -root- depends on poetry-core (0.999) which doesn't match any versions,\\\n version solving failed.\n\"\"\"\n    expected_end = \"Cannot resolve build-system.requires for simple-project.\"\n\n    output = io.fetch_output().strip()\n    assert output.startswith(expected_start)\n    assert output.endswith(expected_end)\n\n\ndef test_build_system_requires_install_failure(\n    mocker: MockerFixture,\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    env: MockEnv,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    mocker.patch(\"poetry.installation.installer.Installer.run\", return_value=1)\n    mocker.patch(\"cleo.io.buffered_io.BufferedIO.fetch_output\", return_value=\"output\")\n    mocker.patch(\"cleo.io.buffered_io.BufferedIO.fetch_error\", return_value=\"error\")\n    io.set_verbosity(Verbosity.NORMAL)\n\n    executor = Executor(env, pool, config, io)\n\n    package_name = \"simple-project\"\n    package_version = \"1.2.3\"\n    directory_package = Package(\n        package_name,\n        package_version,\n        source_type=\"directory\",\n        source_url=fixture_dir(\"simple_project\").resolve().as_posix(),\n    )\n\n    return_code = executor.execute([Install(directory_package)])\n\n    assert return_code == 1\n\n    package_url = directory_package.source_url\n    expected_start = f\"\"\"\\\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing {package_name} ({package_version} {package_url})\n\n  IsolatedBuildInstallError\n\n  Failed to install poetry-core>=1.1.0a7.\n  \\\n\n  Output:\n  output\n  \\\n\n  Error:\n  error\n\n\"\"\"\n    expected_end = \"Cannot install build-system.requires for simple-project.\"\n\n    mocker.stopall()  # to get real output\n    output = io.fetch_output().strip()\n\n    assert output.startswith(expected_start)\n    assert output.endswith(expected_end)\n\n\ndef test_other_error(\n    config: Config,\n    pool: RepositoryPool,\n    io: BufferedIO,\n    env: MockEnv,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    io.set_verbosity(Verbosity.NORMAL)\n\n    executor = Executor(env, pool, config, io)\n\n    package_name = \"simple-project\"\n    package_version = \"1.2.3\"\n    directory_package = Package(\n        package_name,\n        package_version,\n        source_type=\"directory\",\n        source_url=fixture_dir(\"non-existing\").resolve().as_posix(),\n    )\n\n    return_code = executor.execute([Install(directory_package)])\n\n    assert return_code == 1\n\n    package_url = directory_package.source_url\n    expected_start = f\"\"\"\\\nPackage operations: 1 install, 0 updates, 0 removals\n\n  - Installing {package_name} ({package_version} {package_url})\n\n  FileNotFoundError\n\"\"\"\n    expected_end = \"Cannot install simple-project.\"\n\n    output = io.fetch_output().strip()\n    assert output.startswith(expected_start)\n    assert output.endswith(expected_end)\n\n\n@pytest.mark.parametrize(\n    \"package_files,expected_url_reference\",\n    [\n        (\n            [\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"sha512:766ecf369b6bdf801f6f7bbfe23923cc9793d633a55619472cd3d5763f9154711fbf57c8b6ca74e4a82fa9bd8380af831e7b8668e68e362669fc60b1d81d79ad\",\n                },\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"md5:d1912c917363a64e127318655f7d1fe7\",\n                },\n                {\n                    \"file\": \"demo-0.1.0.whl\",\n                    \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n                },\n            ],\n            {\n                \"archive_info\": {\n                    \"hashes\": {\n                        \"sha512\": \"766ecf369b6bdf801f6f7bbfe23923cc9793d633a55619472cd3d5763f9154711fbf57c8b6ca74e4a82fa9bd8380af831e7b8668e68e362669fc60b1d81d79ad\"\n                    },\n                },\n            },\n        ),\n        (\n            [\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"md5:d1912c917363a64e127318655f7d1fe7\",\n                }\n            ],\n            {\n                \"archive_info\": {\n                    \"hashes\": {\"md5\": \"d1912c917363a64e127318655f7d1fe7\"},\n                },\n            },\n        ),\n        (\n            [\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"sha3_512:196f4af9099185054ed72ca1d4c57707da5d724df0af7c3dfcc0fd018b0e0533908e790a291600c7d196fe4411b4f5f6db45213fe6e5cd5512bf18b2e9eff728\",\n                },\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"sha512:766ecf369b6bdf801f6f7bbfe23923cc9793d633a55619472cd3d5763f9154711fbf57c8b6ca74e4a82fa9bd8380af831e7b8668e68e362669fc60b1d81d79ad\",\n                },\n                {\n                    \"file\": \"demo-0.1.0.tar.gz\",\n                    \"hash\": \"md5:d1912c917363a64e127318655f7d1fe7\",\n                },\n                {\n                    \"file\": \"demo-0.1.0.whl\",\n                    \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n                },\n            ],\n            {\n                \"archive_info\": {\n                    \"hashes\": {\n                        \"sha3_512\": \"196f4af9099185054ed72ca1d4c57707da5d724df0af7c3dfcc0fd018b0e0533908e790a291600c7d196fe4411b4f5f6db45213fe6e5cd5512bf18b2e9eff728\"\n                    },\n                },\n            },\n        ),\n    ],\n)\ndef test_executor_known_hashes(\n    package_files: list[PackageFile],\n    expected_url_reference: dict[str, Any],\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    package_source_url: Path = (\n        fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    ).resolve()\n    package = Package(\n        \"demo\", \"0.1.0\", source_type=\"file\", source_url=package_source_url.as_posix()\n    )\n    package.files = package_files\n    executor = Executor(tmp_venv, pool, config, io)\n    executor.execute([Install(package)])\n    expected_url_reference[\"url\"] = package_source_url.as_uri()\n    verify_installed_distribution(tmp_venv, package, expected_url_reference)\n\n\ndef test_executor_no_supported_hash_types(\n    tmp_venv: VirtualEnv,\n    pool: RepositoryPool,\n    config: Config,\n    io: BufferedIO,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    url = (fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\").resolve()\n    package = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=url.as_posix())\n    # Set package.files so the executor will attempt to hash the package\n    package.files = [\n        {\n            \"file\": \"demo-0.1.0.tar.gz\",\n            \"hash\": \"hash_blah:1234567890abcdefghijklmnopqrstyzwxyz\",\n        },\n        {\n            \"file\": \"demo-0.1.0.whl\",\n            \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n        },\n    ]\n\n    executor = Executor(tmp_venv, pool, config, io)\n    return_code = executor.execute([Install(package)])\n    distributions = list(tmp_venv.site_packages.distributions(name=package.name))\n    assert len(distributions) == 0\n\n    output = io.fetch_output()\n    error = io.fetch_error()\n    assert return_code == 1, f\"\\noutput: {output}\\nerror: {error}\\n\"\n    assert \"No usable hash type(s) for demo\" in output\n    assert \"hash_blah:1234567890abcdefghijklmnopqrstyzwxyz\" in output\n"
  },
  {
    "path": "tests/installation/test_installer.py",
    "content": "from __future__ import annotations\n\nimport json\nimport re\nimport shutil\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import cast\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom cleo.io.inputs.input import Input\nfrom cleo.io.null_io import NullIO\nfrom cleo.io.outputs.output import Verbosity\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.dependency_group import DependencyGroup\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.factory import Factory\nfrom poetry.installation import Installer\nfrom poetry.packages import Locker as BaseLocker\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils.constants import POETRY_SYSTEM_PROJECT_NAME\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env import NullEnv\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import TestExecutor\nfrom tests.helpers import get_dependency\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from _pytest.fixtures import FixtureRequest\n    from pytest_mock import MockerFixture\n    from tomlkit import TOMLDocument\n\n    from poetry.repositories.legacy_repository import LegacyRepository\n    from poetry.repositories.pypi_repository import PyPiRepository\n    from poetry.utils.env import Env\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n    from tests.types import PackageFactory\n\n\nclass CustomInstalledRepository(InstalledRepository):\n    @classmethod\n    def load(\n        cls, env: Env, with_dependencies: bool = False\n    ) -> CustomInstalledRepository:\n        return cls()\n\n\nclass Locker(BaseLocker):\n    def __init__(self, lock_path: Path) -> None:\n        self._lock = lock_path / \"poetry.lock\"\n        self._written_data = None\n        self._locked = False\n        self._fresh = True\n        self._lock_data = None\n        self._content_hash = self._get_content_hash()\n\n    @property\n    def written_data(self) -> dict[str, Any]:\n        assert self._written_data is not None\n        return self._written_data\n\n    def set_lock_path(self, lock: Path) -> Locker:\n        self._lock = lock / \"poetry.lock\"\n\n        return self\n\n    def locked(self, is_locked: bool = True) -> Locker:\n        self._locked = is_locked\n\n        return self\n\n    def mock_lock_data(self, data: dict[str, Any]) -> None:\n        self._lock_data = data\n\n    def is_locked(self) -> bool:\n        return self._locked\n\n    def fresh(self, is_fresh: bool = True) -> Locker:\n        self._fresh = is_fresh\n\n        return self\n\n    def is_fresh(self) -> bool:\n        return self._fresh\n\n    def _get_content_hash(self, *, with_dependency_groups: bool = True) -> str:\n        return \"123456789\"\n\n    def _write_lock_data(self, data: dict[str, Any]) -> None:\n        for package in data[\"package\"]:\n            python_versions = str(package[\"python-versions\"])\n            package[\"python-versions\"] = python_versions\n\n        self._written_data = json.loads(json.dumps(data))\n        self._lock_data = data\n\n\n@pytest.fixture(autouse=True, params=[False, True])\ndef config_installer_reresolve(\n    config: Config, request: FixtureRequest\n) -> Iterator[bool]:\n    config.config[\"installer\"][\"re-resolve\"] = request.param\n    yield request.param\n\n\n@pytest.fixture()\ndef package() -> ProjectPackage:\n    p = ProjectPackage(\"root\", \"1.0\")\n    p.root_dir = Path.cwd()\n\n    return p\n\n\n@pytest.fixture()\ndef repo() -> Repository:\n    return Repository(\"repo\")\n\n\n@pytest.fixture()\ndef pool(repo: Repository) -> RepositoryPool:\n    pool = RepositoryPool()\n    pool.add_repository(repo)\n\n    return pool\n\n\n@pytest.fixture()\ndef installed() -> CustomInstalledRepository:\n    return CustomInstalledRepository()\n\n\n@pytest.fixture()\ndef locker(project_root: Path) -> Locker:\n    return Locker(lock_path=project_root)\n\n\n@pytest.fixture()\ndef env(tmp_path: Path) -> NullEnv:\n    return NullEnv(path=tmp_path)\n\n\n@pytest.fixture()\ndef installer(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    locker: Locker,\n    env: NullEnv,\n    installed: CustomInstalledRepository,\n    config: Config,\n) -> Installer:\n    return Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n\n\ndef fixture(name: str, data: dict[str, Any] | None = None) -> dict[str, Any]:\n    \"\"\"\n    Create or load a fixture file in TOML format.\n\n    This function retrieves the contents of a test fixture file, optionally writing\n    data to it before reading, and returns the data as a dictionary. It is used to\n    manage testing fixtures for TOML-based configurations.\n\n    :param name: Name of the fixture file (without extension, default of .test is appended).\n    :param data: Dictionary to write to the file as a TOML document. If None,\n                 no data is written (use this only when generating fixtures).\n    :return: Dictionary representing the contents of the TOML fixture file.\n    \"\"\"\n    file = TOMLFile(Path(__file__).parent / \"fixtures\" / f\"{name}.test\")\n\n    if data:\n        # if data is provided write it, this is helpful for generating fixtures\n        # we expect lock data to be compatible with TOMLDocument for our purposes\n        file.write(cast(\"TOMLDocument\", data))\n\n    content: dict[str, Any] = file.read()\n\n    return content\n\n\ndef fix_lock_data(lock_data: dict[str, Any]) -> None:\n    if Version.parse(lock_data[\"metadata\"][\"lock-version\"]) >= Version.parse(\"2.1\"):\n        for locked_package in lock_data[\"package\"]:\n            locked_package[\"groups\"] = [\"main\"]\n            locked_package[\"files\"] = []\n        del lock_data[\"metadata\"][\"files\"]\n\n\ndef test_run_no_dependencies(installer: Installer, locker: Locker) -> None:\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"no-dependencies\")\n    assert locker.written_data == expected\n\n\ndef test_not_fresh_lock(installer: Installer, locker: Locker) -> None:\n    locker.locked().fresh(False)\n    with pytest.raises(\n        ValueError,\n        match=re.escape(\n            \"pyproject.toml changed significantly since poetry.lock was last generated. \"\n            \"Run `poetry lock` to fix the lock file.\"\n        ),\n    ):\n        installer.run()\n\n\ndef test_not_fresh_lock_self_project(installer: Installer, locker: Locker) -> None:\n    installer.set_package(ProjectPackage(POETRY_SYSTEM_PROJECT_NAME, \"1.0\"))\n    locker.locked().fresh(False)\n    with pytest.raises(\n        ValueError,\n        match=re.escape(\n            \"pyproject.toml changed significantly since poetry.lock was last generated. \"\n            \"Run `poetry self lock` to fix the lock file.\"\n        ),\n    ):\n        installer.run()\n\n\ndef test_run_with_dependencies(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies\")\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_update_after_removing_dependencies(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.2\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": [], \"C\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c = get_package(\"C\", \"1.2\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    installed.add_package(package_a)\n    installed.add_package(package_b)\n    installed.add_package(package_c)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"~1.1\"))\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies\")\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 1\n\n\ndef _configure_run_install_dev(\n    lock_version: str,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    with_optional_group: bool = False,\n    with_packages_installed: bool = False,\n) -> None:\n    \"\"\"\n    Perform common test setup for `test_run_install_*dev*()` methods.\n    \"\"\"\n    lock_data: dict[str, Any] = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.2\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": [], \"C\": []},\n        },\n    }\n    if lock_version == \"2.1\":\n        for locked_package in lock_data[\"package\"]:\n            locked_package[\"groups\"] = [\n                \"dev\" if locked_package[\"name\"] == \"C\" else \"main\"\n            ]\n            locked_package[\"files\"] = []\n        del lock_data[\"metadata\"][\"files\"]\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c = get_package(\"C\", \"1.2\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    if with_packages_installed:\n        installed.add_package(package_a)\n        installed.add_package(package_b)\n        installed.add_package(package_c)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"~1.1\"))\n\n    group = DependencyGroup(\"dev\", optional=with_optional_group)\n    group.add_dependency(Factory.create_dependency(\"C\", \"~1.2\", groups=[\"dev\"]))\n    package.add_dependency_group(group)\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\n@pytest.mark.parametrize(\"update\", [False, True])\n@pytest.mark.parametrize(\"requires_synchronization\", [False, True])\n@pytest.mark.parametrize(\n    (\"groups\", \"installs\", \"updates\", \"removals\", \"with_packages_installed\"),\n    [\n        (None, 2, 0, 0, False),\n        (None, 0, 0, 1, True),\n        ([], 0, 0, 0, False),\n        ([], 0, 0, 3, True),\n        ([\"dev\"], 1, 0, 0, False),\n        ([\"dev\"], 0, 0, 2, True),\n        ([MAIN_GROUP], 2, 0, 0, False),\n        ([MAIN_GROUP], 0, 0, 1, True),\n        ([MAIN_GROUP, \"dev\"], 3, 0, 0, False),\n        ([MAIN_GROUP, \"dev\"], 0, 0, 0, True),\n    ],\n)\ndef test_run_install_with_dependency_groups(\n    groups: list[str] | None,\n    installs: int,\n    updates: int,\n    removals: int,\n    with_packages_installed: bool,\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    update: bool,\n    requires_synchronization: bool,\n    lock_version: str,\n) -> None:\n    _configure_run_install_dev(\n        lock_version,\n        locker,\n        repo,\n        package,\n        installed,\n        with_optional_group=True,\n        with_packages_installed=with_packages_installed,\n    )\n\n    if groups is not None:\n        installer.only_groups({canonicalize_name(g) for g in groups})\n\n    installer.update(update)\n    installer.requires_synchronization(requires_synchronization)\n    result = installer.run()\n    assert result == 0\n\n    if not requires_synchronization:\n        removals = 0\n    assert installer.executor.installations_count == installs\n    assert installer.executor.updates_count == updates\n    assert installer.executor.removals_count == removals\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_install_does_not_remove_locked_packages_if_installed_but_not_required(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    package_a = get_package(\"a\", \"1.0\")\n    package_b = get_package(\"b\", \"1.1\")\n    package_c = get_package(\"c\", \"1.2\")\n\n    repo.add_package(package_a)\n    installed.add_package(package_a)\n    repo.add_package(package_b)\n    installed.add_package(package_b)\n    repo.add_package(package_c)\n    installed.add_package(package_c)\n\n    installed.add_package(package)  # Root package never removed.\n\n    package.add_dependency(\n        Factory.create_dependency(package_a.name, str(package_a.version))\n    )\n\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": package_a.name,\n                \"version\": package_a.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_b.name,\n                \"version\": package_b.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_c.name,\n                \"version\": package_c.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {package_a.name: [], package_b.name: [], package_c.name: []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_install_removes_locked_packages_if_installed_and_synchronization_is_required(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n    config_installer_reresolve: bool,\n) -> None:\n    package_a = get_package(\"a\", \"1.0\")\n    package_b = get_package(\"b\", \"1.1\")\n    package_c = get_package(\"c\", \"1.2\")\n\n    repo.add_package(package_a)\n    installed.add_package(package_a)\n    repo.add_package(package_b)\n    installed.add_package(package_b)\n    repo.add_package(package_c)\n    installed.add_package(package_c)\n\n    installed.add_package(package)  # Root package never removed.\n\n    package.add_dependency(\n        Factory.create_dependency(package_a.name, str(package_a.version))\n    )\n\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": package_a.name,\n                \"version\": package_a.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_b.name,\n                \"version\": package_b.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_c.name,\n                \"version\": package_c.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {package_a.name: [], package_b.name: [], package_c.name: []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    installer.update(True)\n    installer.requires_synchronization(True)\n    installer.run()\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 2\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_install_removes_no_longer_locked_packages_if_installed(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    package_a = get_package(\"a\", \"1.0\")\n    package_b = get_package(\"b\", \"1.1\")\n    package_c = get_package(\"c\", \"1.2\")\n\n    repo.add_package(package_a)\n    installed.add_package(package_a)\n    repo.add_package(package_b)\n    installed.add_package(package_b)\n    repo.add_package(package_c)\n    installed.add_package(package_c)\n\n    installed.add_package(package)  # Root package never removed.\n\n    package.add_dependency(\n        Factory.create_dependency(package_a.name, str(package_a.version))\n    )\n\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": package_a.name,\n                \"version\": package_a.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_b.name,\n                \"version\": package_b.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": package_c.name,\n                \"version\": package_c.version.text,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {package_a.name: [], package_b.name: [], package_c.name: []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 2\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\n@pytest.mark.parametrize(\n    \"managed_reserved_package_names\",\n    [(), (\"pip\",)],\n)\ndef test_run_install_with_synchronization(\n    managed_reserved_package_names: tuple[str, ...],\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    package_a = get_package(\"a\", \"1.0\")\n    package_b = get_package(\"b\", \"1.1\")\n    package_c = get_package(\"c\", \"1.2\")\n    package_pip = get_package(\"pip\", \"20.0.0\")\n\n    all_packages = [\n        package_a,\n        package_b,\n        package_c,\n        package_pip,\n    ]\n\n    managed_reserved_packages = [\n        pkg for pkg in all_packages if pkg.name in managed_reserved_package_names\n    ]\n    locked_packages = [package_a, *managed_reserved_packages]\n\n    for pkg in all_packages:\n        repo.add_package(pkg)\n        installed.add_package(pkg)\n\n    installed.add_package(package)  # Root package never removed.\n\n    package.add_dependency(\n        Factory.create_dependency(package_a.name, str(package_a.version))\n    )\n\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": pkg.name,\n                \"version\": pkg.version,\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            }\n            for pkg in locked_packages\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {pkg.name: [] for pkg in locked_packages},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    installer.update(True)\n    installer.requires_synchronization(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 2 + len(managed_reserved_packages)\n\n    expected_removals = {\n        package_b.name,\n        package_c.name,\n        *managed_reserved_package_names,\n    }\n\n    assert isinstance(installer.executor, TestExecutor)\n    assert {r.name for r in installer.executor.removals} == expected_removals\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_whitelist_add(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            }\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0\")\n    package_a_new = get_package(\"A\", \"1.1\")\n    package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(package_a_new)\n    repo.add_package(package_b)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    installer.update(True)\n    installer.whitelist([\"B\"])\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies\")\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_whitelist_remove(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    installed.add_package(package_b)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n\n    installer.update(True)\n    installer.whitelist([\"B\"])\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"remove\")\n    assert locker.written_data == expected\n    assert installer.executor.installations_count == 1\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 1\n\n\ndef test_add_with_sub_dependencies(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c = get_package(\"C\", \"1.2\")\n    package_d = get_package(\"D\", \"1.3\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    package_a.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"~1.2\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-sub-dependencies\")\n    assert locker.written_data == expected\n\n\ndef test_run_with_python_versions(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package.python_versions = \"~2.7 || ^3.4\"\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c12.python_versions = \"~2.7 || ^3.3\"\n    package_c13 = get_package(\"C\", \"1.3\")\n    package_c13.python_versions = \"~3.3\"\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c12)\n    repo.add_package(package_c13)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n    package.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-python-versions\")\n    assert locker.written_data == expected\n\n\ndef test_run_with_optional_and_python_restricted_dependencies(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package.python_versions = \"~2.7 || ^3.4\"\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c13 = get_package(\"C\", \"1.3\")\n    package_d = get_package(\"D\", \"1.4\")\n    package_c13.add_dependency(Factory.create_dependency(\"D\", \"^1.2\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c12)\n    repo.add_package(package_c13)\n    repo.add_package(package_d)\n\n    package.extras = {canonicalize_name(\"foo\"): [get_dependency(\"A\", \"~1.0\")]}\n    dep_a = Factory.create_dependency(\"A\", {\"version\": \"~1.0\", \"optional\": True})\n    dep_a._in_extras = [canonicalize_name(\"foo\")]\n    package.add_dependency(dep_a)\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"~2.4\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"python\": \"~2.7 || ^3.4\"})\n    )\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-optional-dependencies\")\n    assert locker.written_data == expected\n\n    # We should only have 2 installs:\n    # C,D since python version is not compatible\n    # with B's python constraint and A is optional\n    assert isinstance(installer.executor, TestExecutor)\n    assert installer.executor.installations_count == 2\n    assert installer.executor.installations[0].name == \"d\"\n    assert installer.executor.installations[1].name == \"c\"\n\n\ndef test_run_with_optional_and_platform_restricted_dependencies(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    mocker: MockerFixture,\n) -> None:\n    mocker.patch(\"sys.platform\", \"darwin\")\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c13 = get_package(\"C\", \"1.3\")\n    package_d = get_package(\"D\", \"1.4\")\n    package_c13.add_dependency(Factory.create_dependency(\"D\", \"^1.2\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c12)\n    repo.add_package(package_c13)\n    repo.add_package(package_d)\n\n    package.extras = {canonicalize_name(\"foo\"): [get_dependency(\"A\", \"~1.0\")]}\n    dep_a = Factory.create_dependency(\"A\", {\"version\": \"~1.0\", \"optional\": True})\n    dep_a._in_extras = [canonicalize_name(\"foo\")]\n    package.add_dependency(dep_a)\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"platform\": \"custom\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"platform\": \"darwin\"})\n    )\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-platform-dependencies\")\n    assert locker.written_data == expected\n\n    # We should only have 2 installs:\n    # C,D since the mocked python version is not compatible\n    # with B's python constraint and A is optional\n    assert isinstance(installer.executor, TestExecutor)\n    assert installer.executor.installations_count == 2\n    assert installer.executor.installations[0].name == \"d\"\n    assert installer.executor.installations[1].name == \"c\"\n\n\ndef test_run_with_dependencies_extras(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    package_b.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\", \"^1.0\")]}\n    package_b.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    )\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"extras\": [\"foo\"]})\n    )\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies-extras\")\n    assert locker.written_data == expected\n\n\ndef test_run_with_dependencies_nested_extras(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    dependency_c = Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    dependency_b = Factory.create_dependency(\n        \"B\", {\"version\": \"^1.0\", \"optional\": True, \"extras\": [\"C\"]}\n    )\n    dependency_a = Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"extras\": [\"B\"]})\n\n    package_b.extras = {canonicalize_name(\"c\"): [dependency_c]}\n    package_b.add_dependency(dependency_c)\n\n    package_a.add_dependency(dependency_b)\n    package_a.extras = {canonicalize_name(\"b\"): [dependency_b]}\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    package.add_dependency(dependency_a)\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies-nested-extras\")\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\n    \"enabled_extras\",\n    [\n        ([]),\n        ([\"all\"]),\n        ([\"nested\"]),\n        ([\"install\", \"download\"]),\n        ([\"install\"]),\n        ([\"download\"]),\n    ],\n)\n@pytest.mark.parametrize(\"top_level_dependency\", [True, False])\ndef test_solver_resolves_self_referential_extras(\n    enabled_extras: list[str],\n    top_level_dependency: bool,\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    create_package: PackageFactory,\n) -> None:\n    dependency = (\n        create_package(\n            \"A\",\n            str(package.version),\n            extras={\n                \"download\": [\"download-package\"],\n                \"install\": [\"install-package\"],\n                \"py38\": [\"py38-package ; python_version == '3.8'\"],\n                \"py310\": [\"py310-package ; python_version > '3.8'\"],\n                \"all\": [\"a[download,install]\"],\n                \"py\": [\"a[py38,py310]\"],\n                \"nested\": [\"a[all]\"],\n            },\n        )\n        .to_dependency()\n        .with_features(enabled_extras)\n    )\n\n    if not top_level_dependency:\n        dependency = create_package(\n            \"B\", \"1.0\", dependencies=[dependency]\n        ).to_dependency()\n\n    package.add_dependency(dependency)\n\n    result = installer.run()\n    assert result == 0\n\n    name = \"-\".join(\n        [\n            \"with-self-referencing-extras\",\n            *enabled_extras,\n            \"top\" if top_level_dependency else \"deep\",\n        ]\n    )\n\n    expected = fixture(name)\n    assert locker.written_data == expected\n\n\ndef test_solver_resolves_self_referential_extras_with_markers(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    create_package: PackageFactory,\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"all\"]})\n    )\n\n    create_package(\n        \"A\",\n        str(package.version),\n        extras={\n            \"download\": [\"download-package\"],\n            \"install\": [\"install-package\"],\n            \"all\": [\"a[download,install] ; python_version < '3.9'\"],\n        },\n    )\n\n    result = installer.run()\n    assert result == 0\n\n    name = \"-\".join([\"with-self-referencing-extras\", \"b\", \"markers\"])\n\n    # FIXME: At the time of writing this test case, the markers from self-ref extras are not\n    #  correctly propagated into the dependency specs. For example, given this case,\n    #  the package \"install-package\" should have a final marker of\n    #  \"extra == 'install' or extra == 'all' and python_version < '3.9'\".\n    expected = fixture(name)\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\"root\", [True, False])\n@pytest.mark.parametrize(\"locked\", [False, True])\n@pytest.mark.parametrize(\"extra\", [None, \"extra-one\", \"extra-two\"])\ndef test_run_with_conflicting_dependency_extras(\n    installer: Installer,\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    repo: Repository,\n    config: Config,\n    package: ProjectPackage,\n    extra: str | None,\n    locked: bool,\n    root: bool,\n) -> None:\n    \"\"\"\n    - https://github.com/python-poetry/poetry/issues/6419\n\n    Tests resolution of extras with conflicting dependencies. Tests in both as direct dependencies of\n    root package and as transitive dependencies.\n    \"\"\"\n    # A package with two optional dependencies, one for each extra\n    # If root, this is the root package, otherwise an intermediate package\n    main_package = package if root else get_package(\"intermediate-dep\", \"1.0.0\")\n\n    # Two conflicting versions of a dependency, one in each extra\n    conflicting_dep_one_pkg = get_package(\"conflicting-dep\", \"1.1.0\")\n    conflicting_dep_two_pkg = get_package(\"conflicting-dep\", \"1.2.0\")\n\n    conflicting_dep_one = Factory.create_dependency(\n        \"conflicting-dep\",\n        {\n            \"version\": \"1.1.0\",\n            \"markers\": \"extra == 'extra-one' and extra != 'extra-two'\",\n            \"optional\": True,\n        },\n    )\n    conflicting_dep_two = Factory.create_dependency(\n        \"conflicting-dep\",\n        {\n            \"version\": \"1.2.0\",\n            \"markers\": \"extra != 'extra-one' and extra == 'extra-two'\",\n            \"optional\": True,\n        },\n    )\n\n    # Include both just for extra validation that our marker validation works as expected\n    main_package.extras = {\n        canonicalize_name(\"extra-one\"): [conflicting_dep_one, conflicting_dep_two],\n        canonicalize_name(\"extra-two\"): [conflicting_dep_one, conflicting_dep_two],\n    }\n    main_package.add_dependency(conflicting_dep_one)\n    main_package.add_dependency(conflicting_dep_two)\n\n    repo.add_package(conflicting_dep_one_pkg)\n    repo.add_package(conflicting_dep_two_pkg)\n    if not root:\n        repo.add_package(main_package)\n\n    # If we have an intermediate package, add extras to our root package\n    if not root:\n        extra_one_dep = Factory.create_dependency(\n            \"intermediate-dep\",\n            {\n                \"version\": \"1.0.0\",\n                \"markers\": \"extra == 'root-extra-one' and extra != 'root-extra-two'\",\n                \"extras\": [\"extra-one\"],\n                \"optional\": True,\n            },\n        )\n        extra_two_dep = Factory.create_dependency(\n            \"intermediate-dep\",\n            {\n                \"version\": \"1.0.0\",\n                \"markers\": \"extra != 'root-extra-one' and extra == 'root-extra-two'\",\n                \"extras\": [\"extra-two\"],\n                \"optional\": True,\n            },\n        )\n        package.add_dependency(extra_one_dep)\n        package.add_dependency(extra_two_dep)\n        # Include both just for extra validation that our marker validation works as expected\n        package.extras = {\n            canonicalize_name(\"root-extra-one\"): [extra_one_dep, extra_two_dep],\n            canonicalize_name(\"root-extra-two\"): [extra_one_dep, extra_two_dep],\n        }\n\n    fixture_name = \"with-conflicting-dependency-extras-\" + (\n        \"root\" if root else \"transitive\"\n    )\n    locker.locked(locked)\n    if locked:\n        locker.mock_lock_data(dict(fixture(fixture_name)))\n\n    if extra is not None:\n        extras = [f\"root-{extra}\"] if not root else [extra]\n        installer.extras(extras)\n    result = installer.run()\n    assert result == 0\n\n    if not locked:\n        expected = fixture(fixture_name)\n        assert locker.written_data == expected\n\n    # Results of installation are consistent with the 'extra' input\n    assert isinstance(installer.executor, TestExecutor)\n\n    expected_installations = []\n    if extra == \"extra-one\":\n        expected_installations.append(conflicting_dep_one_pkg)\n    elif extra == \"extra-two\":\n        expected_installations.append(conflicting_dep_two_pkg)\n    if not root and extra is not None:\n        expected_installations.append(get_package(\"intermediate-dep\", \"1.0.0\"))\n\n    assert len(installer.executor.installations) == len(expected_installations)\n    assert set(installer.executor.installations) == set(expected_installations)\n\n\n@pytest.mark.parametrize(\"locked\", [True, False])\n@pytest.mark.parametrize(\"extra\", [None, \"cpu\", \"cuda\"])\ndef test_run_with_exclusive_extras_different_sources(\n    installer: Installer,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    package: ProjectPackage,\n    extra: str | None,\n    locked: bool,\n) -> None:\n    \"\"\"\n    - https://github.com/python-poetry/poetry/issues/6409\n    - https://github.com/python-poetry/poetry/issues/6419\n    - https://github.com/python-poetry/poetry/issues/7748\n    - https://github.com/python-poetry/poetry/issues/9537\n    \"\"\"\n    # Setup repo for each of our sources\n    cpu_repo = Repository(\"pytorch-cpu\")\n    cuda_repo = Repository(\"pytorch-cuda\")\n    pool = RepositoryPool()\n    pool.add_repository(cpu_repo)\n    pool.add_repository(cuda_repo)\n    config.config[\"repositories\"] = {\n        \"pytorch-cpu\": {\"url\": \"https://download.pytorch.org/whl/cpu\"},\n        \"pytorch-cuda\": {\"url\": \"https://download.pytorch.org/whl/cuda\"},\n    }\n\n    # Configure packages that read from each of the different sources\n    torch_cpu_pkg = get_package(\"torch\", \"1.11.0+cpu\")\n    torch_cpu_pkg._source_reference = \"pytorch-cpu\"\n    torch_cpu_pkg._source_type = \"legacy\"\n    torch_cpu_pkg._source_url = \"https://download.pytorch.org/whl/cpu\"\n    torch_cuda_pkg = get_package(\"torch\", \"1.11.0+cuda\")\n    torch_cuda_pkg._source_reference = \"pytorch-cuda\"\n    torch_cuda_pkg._source_type = \"legacy\"\n    torch_cuda_pkg._source_url = \"https://download.pytorch.org/whl/cuda\"\n    cpu_repo.add_package(torch_cpu_pkg)\n    cuda_repo.add_package(torch_cuda_pkg)\n\n    # Depend on each package based on exclusive extras\n    torch_cpu_dep = Factory.create_dependency(\n        \"torch\",\n        {\n            \"version\": \"1.11.0+cpu\",\n            \"markers\": \"extra == 'cpu' and extra != 'cuda'\",\n            \"source\": \"pytorch-cpu\",\n        },\n    )\n    torch_cuda_dep = Factory.create_dependency(\n        \"torch\",\n        {\n            \"version\": \"1.11.0+cuda\",\n            \"markers\": \"extra != 'cpu' and extra == 'cuda'\",\n            \"source\": \"pytorch-cuda\",\n        },\n    )\n    package.add_dependency(torch_cpu_dep)\n    package.add_dependency(torch_cuda_dep)\n    # We don't want to cheat by only including the correct dependency in the 'extra' mapping\n    package.extras = {\n        canonicalize_name(\"cpu\"): [torch_cpu_dep, torch_cuda_dep],\n        canonicalize_name(\"cuda\"): [torch_cpu_dep, torch_cuda_dep],\n    }\n\n    # Set locker state\n    locker.locked(locked)\n    if locked:\n        locker.mock_lock_data(dict(fixture(\"with-exclusive-extras\")))\n\n    # Perform install\n    installer = Installer(\n        NullIO(),\n        MockEnv(),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n    if extra is not None:\n        installer.extras([extra])\n    result = installer.run()\n    assert result == 0\n\n    # Results of locking are expected and installation are consistent with the 'extra' input\n    if not locked:\n        expected = fixture(\"with-exclusive-extras\")\n        assert locker.written_data == expected\n    assert isinstance(installer.executor, TestExecutor)\n    if extra is None:\n        assert len(installer.executor.installations) == 0\n    else:\n        assert len(installer.executor.installations) == 1\n        version = f\"1.11.0+{extra}\"\n        source_url = f\"https://download.pytorch.org/whl/{extra}\"\n        source_reference = f\"pytorch-{extra}\"\n        assert installer.executor.installations[0] == Package(\n            \"torch\",\n            version,\n            source_type=\"legacy\",\n            source_url=source_url,\n            source_reference=source_reference,\n        )\n\n\n@pytest.mark.parametrize(\"locked\", [True, False])\n@pytest.mark.parametrize(\"extra\", [None, \"extra-one\", \"extra-two\"])\ndef test_run_with_different_dependency_extras(\n    installer: Installer,\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    repo: Repository,\n    config: Config,\n    package: ProjectPackage,\n    extra: str | None,\n    locked: bool,\n) -> None:\n    \"\"\"\n    - https://github.com/python-poetry/poetry/issues/834\n    - https://github.com/python-poetry/poetry/issues/7748\n\n    This tests different sets of extras in a dependency of the root project. These different dependency extras are\n    themselves conditioned on extras in the root project.\n    \"\"\"\n    # Three packages in addition to root: demo (direct dependency) and two transitive dep packages\n    demo_pkg = get_package(\"demo\", \"1.0.0\")\n    transitive_one_pkg = get_package(\"transitive-dep-one\", \"1.1.0\")\n    transitive_two_pkg = get_package(\"transitive-dep-two\", \"1.2.0\")\n\n    # Switch each transitive dependency based on extra markers in the 'demo' package\n    transitive_dep_one = Factory.create_dependency(\n        \"transitive-dep-one\",\n        {\n            \"version\": \"1.1.0\",\n            \"markers\": \"extra == 'demo-extra-one' and extra != 'demo-extra-two'\",\n            \"optional\": True,\n        },\n    )\n    transitive_dep_two = Factory.create_dependency(\n        \"transitive-dep-two\",\n        {\n            \"version\": \"1.2.0\",\n            \"markers\": \"extra != 'demo-extra-one' and extra == 'demo-extra-two'\",\n            \"optional\": True,\n        },\n    )\n    # Include both packages in both demo extras, to validate that they're filtered out based on extra markers alone\n    demo_pkg.extras = {\n        canonicalize_name(\"demo-extra-one\"): [\n            get_dependency(\"transitive-dep-one\"),\n            get_dependency(\"transitive-dep-two\"),\n        ],\n        canonicalize_name(\"demo-extra-two\"): [\n            get_dependency(\"transitive-dep-one\"),\n            get_dependency(\"transitive-dep-two\"),\n        ],\n    }\n    demo_pkg.add_dependency(transitive_dep_one)\n    demo_pkg.add_dependency(transitive_dep_two)\n\n    # Now define the demo dependency, similarly switched on extra markers in the root package\n    extra_one_dep = Factory.create_dependency(\n        \"demo\",\n        {\n            \"version\": \"1.0.0\",\n            \"markers\": \"extra == 'extra-one' and extra != 'extra-two'\",\n            \"extras\": [\"demo-extra-one\"],\n        },\n    )\n    extra_two_dep = Factory.create_dependency(\n        \"demo\",\n        {\n            \"version\": \"1.0.0\",\n            \"markers\": \"extra != 'extra-one' and extra == 'extra-two'\",\n            \"extras\": [\"demo-extra-two\"],\n        },\n    )\n    package.add_dependency(extra_one_dep)\n    package.add_dependency(extra_two_dep)\n    # Again we don't want to cheat by only including the correct dependency in the 'extra' mapping\n    package.extras = {\n        canonicalize_name(\"extra-one\"): [extra_one_dep, extra_two_dep],\n        canonicalize_name(\"extra-two\"): [extra_one_dep, extra_two_dep],\n    }\n\n    repo.add_package(demo_pkg)\n    repo.add_package(transitive_one_pkg)\n    repo.add_package(transitive_two_pkg)\n\n    locker.locked(locked)\n    if locked:\n        locker.mock_lock_data(dict(fixture(\"with-dependencies-differing-extras\")))\n\n    installer = Installer(\n        NullIO(),\n        MockEnv(),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n    if extra is not None:\n        installer.extras([extra])\n    result = installer.run()\n    assert result == 0\n\n    if not locked:\n        expected = fixture(\"with-dependencies-differing-extras\")\n        assert locker.written_data == expected\n\n    # Results of installation are consistent with the 'extra' input\n    assert isinstance(installer.executor, TestExecutor)\n    if extra is None:\n        assert len(installer.executor.installations) == 0\n    else:\n        assert len(installer.executor.installations) == 2\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\n@pytest.mark.parametrize(\"is_installed\", [False, True])\n@pytest.mark.parametrize(\"with_extras\", [False, True])\n@pytest.mark.parametrize(\"do_update\", [False, True])\n@pytest.mark.parametrize(\"do_sync\", [False, True])\ndef test_run_installs_extras_with_deps_if_requested(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    installed: CustomInstalledRepository,\n    package: ProjectPackage,\n    is_locked: bool,\n    is_installed: bool,\n    with_extras: bool,\n    do_update: bool,\n    do_sync: bool,\n) -> None:\n    package.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\")]}\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_d = get_package(\"D\", \"1.1\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n    dep_c = Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    dep_c._in_extras = [canonicalize_name(\"foo\")]\n    package.add_dependency(dep_c)\n\n    package_c.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    if is_locked:\n        locker.locked(True)\n        locker.mock_lock_data(fixture(\"extras-with-dependencies\"))\n\n    if is_installed:\n        installed.add_package(package_a)\n        installed.add_package(package_b)\n        installed.add_package(package_c)\n        installed.add_package(package_d)\n\n    if with_extras:\n        installer.extras([\"foo\"])\n    installer.update(do_update)\n    installer.requires_synchronization(do_sync)\n    result = installer.run()\n    assert result == 0\n\n    if not is_locked:\n        assert locker.written_data == fixture(\"extras-with-dependencies\")\n\n    if with_extras:\n        # A, B, C, D\n        expected_installations_count = 0 if is_installed else 4\n        expected_removals_count = 0\n    else:\n        # A, B\n        expected_installations_count = 0 if is_installed else 2\n        # We only want to uninstall extras if we do a \"poetry install\" without extras,\n        # not if we do a \"poetry update\" or \"poetry add\".\n        expected_removals_count = 2 if is_installed and do_sync else 0\n\n    assert installer.executor.installations_count == expected_installations_count\n    assert installer.executor.removals_count == expected_removals_count\n\n\ndef test_installer_with_pypi_repository(\n    package: ProjectPackage,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    env: NullEnv,\n    pypi_repository: PyPiRepository,\n) -> None:\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository)\n\n    installer = Installer(\n        NullIO(), env, package, locker, pool, config, installed=installed\n    )\n\n    package.python_versions = \">=3.7\"\n    package.add_dependency(Factory.create_dependency(\"pytest\", \"^3.5\", groups=[\"dev\"]))\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-pypi-repository\")\n    assert locker.written_data == expected\n\n\ndef test_run_installs_with_local_file(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    root_dir = Path(__file__).parent.parent.parent\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    file_path = fixture_dir(\"distributions/demo-0.1.0-py2.py3-none-any.whl\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"file\": str(file_path.relative_to(root_dir))}, root_dir=root_dir\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-file-dependency\")\n\n    assert locker.written_data == expected\n    assert installer.executor.installations_count == 2\n\n\ndef test_run_installs_wheel_with_no_requires_dist(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    root_dir = Path(__file__).parent.parent.parent\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    file_path = fixture_dir(\n        \"wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl\"\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"file\": str(file_path.relative_to(root_dir))}, root_dir=root_dir\n        )\n    )\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-wheel-dependency-no-requires-dist\")\n\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 1\n\n\ndef test_run_installs_with_local_poetry_directory_and_extras(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    tmpdir: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    root_dir = Path(__file__).parent.parent.parent\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    file_path = fixture_dir(\"project_with_extras\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"project-with-extras\",\n            {\"path\": str(file_path.relative_to(root_dir)), \"extras\": [\"extras_a\"]},\n            root_dir=root_dir,\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-directory-dependency-poetry\")\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 2\n\n\n@pytest.mark.parametrize(\"skip_directory\", [True, False])\ndef test_run_installs_with_local_poetry_directory_and_skip_directory_flag(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    skip_directory: bool,\n) -> None:\n    \"\"\"When we set Installer.skip_directory(True) no path dependencies should\n    be installed (including transitive dependencies).\n    \"\"\"\n    root_dir = fixture_dir(\"directory\")\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    directory = root_dir.joinpath(\"project_with_transitive_directory_dependencies\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"project-with-transitive-directory-dependencies\",\n            {\"path\": str(directory.relative_to(root_dir))},\n            root_dir=root_dir,\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"cachy\", \"0.2.0\"))\n\n    installer.skip_directory(skip_directory)\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-directory-dependency-poetry-transitive\")\n\n    assert locker.written_data == expected\n\n    assert isinstance(installer.executor, TestExecutor)\n    directory_installs = [\n        p.name for p in installer.executor.installations if p.source_type == \"directory\"\n    ]\n\n    if skip_directory:\n        assert not directory_installs, directory_installs\n        assert installer.executor.installations_count == 2\n    else:\n        assert directory_installs, directory_installs\n        assert installer.executor.installations_count == 6\n\n\ndef test_run_installs_with_local_poetry_file_transitive(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    tmpdir: str,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    root_dir = fixture_dir(\"directory\")\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    directory = fixture_dir(\"directory\").joinpath(\n        \"project_with_transitive_file_dependencies\"\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"project-with-transitive-file-dependencies\",\n            {\"path\": str(directory.relative_to(root_dir))},\n            root_dir=root_dir,\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"cachy\", \"0.2.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-file-dependency-transitive\")\n\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 4\n\n\ndef test_run_installs_with_local_setuptools_directory(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    tmp_path: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    root_dir = tmp_path / \"root\"\n    package.root_dir = root_dir\n    locker.set_lock_path(root_dir)\n    file_path = shutil.copytree(fixture_dir(\"project_with_setup\"), root_dir / \"project\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"project-with-setup\",\n            {\"path\": str(file_path.relative_to(root_dir))},\n            root_dir=root_dir,\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"cachy\", \"0.2.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-directory-dependency-setuptools\")\n\n    assert locker.written_data == expected\n    assert installer.executor.installations_count == 3\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_with_prereleases(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0a2\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            }\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0a2\")\n    package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"allow-prereleases\": True})\n    )\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.1\"))\n\n    installer.update(True)\n    installer.whitelist({\"B\": \"^1.1\"})\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-prereleases\")\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_update_all_with_lock(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": True,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            }\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.1\")\n    repo.add_package(get_package(\"A\", \"1.0\"))\n    repo.add_package(package_a)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    installer.update(True)\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"update-with-lock\")\n    assert locker.written_data == expected\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_update_with_locked_extras(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"B\": \"^1.0\", \"C\": \"^1.0\"},\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"requirements\": {\"python\": \"~2.7\"},\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": [], \"C\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.extras = {canonicalize_name(\"foo\"): [get_dependency(\"B\")]}\n    b_dependency = get_dependency(\"B\", \"^1.0\", optional=True)\n    b_dependency._in_extras = [canonicalize_name(\"foo\")]\n    c_dependency = get_dependency(\"C\", \"^1.0\")\n    c_dependency.python_versions = \"~2.7\"\n    package_a.add_dependency(b_dependency)\n    package_a.add_dependency(c_dependency)\n\n    repo.add_package(package_a)\n    repo.add_package(get_package(\"B\", \"1.0\"))\n    repo.add_package(get_package(\"C\", \"1.1\"))\n    repo.add_package(get_package(\"D\", \"1.1\"))\n\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"extras\": [\"foo\"]})\n    )\n    package.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    installer.update(True)\n    installer.whitelist(\"D\")\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"update-with-locked-extras\")\n    assert locker.written_data == expected\n\n\ndef test_run_install_duplicate_dependencies_different_constraints(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \">=4.0\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b10.add_dependency(Factory.create_dependency(\"C\", \"1.2\"))\n    package_b20.add_dependency(Factory.create_dependency(\"C\", \"1.5\"))\n\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c15 = get_package(\"C\", \"1.5\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n    repo.add_package(package_c12)\n    repo.add_package(package_c15)\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-duplicate-dependencies\")\n\n    assert locker.written_data == expected\n\n    assert isinstance(installer.executor, TestExecutor)\n    installs = installer.executor.installations\n    assert installer.executor.installations_count == 3\n    assert installs[0] == package_c12\n    assert installs[1] == package_b10\n    assert installs[2] == package_a\n\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_install_duplicate_dependencies_different_constraints_with_lock(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\n                    \"B\": [\n                        {\"version\": \"^1.0\", \"python\": \"<4.0\"},\n                        {\"version\": \"^2.0\", \"python\": \">=4.0\"},\n                    ]\n                },\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"C\": \"1.2\"},\n                \"requirements\": {\"python\": \"<4.0\"},\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"2.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"C\": \"1.5\"},\n                \"requirements\": {\"python\": \">=4.0\"},\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.2\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.5\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": [], \"C\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \">=4.0\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b10.add_dependency(Factory.create_dependency(\"C\", \"1.2\"))\n    package_b20.add_dependency(Factory.create_dependency(\"C\", \"1.5\"))\n\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c15 = get_package(\"C\", \"1.5\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n    repo.add_package(package_c12)\n    repo.add_package(package_c15)\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-duplicate-dependencies\")\n\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 3\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_update_uninstalls_after_removal_transitive_dependency(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"B\": {\"version\": \"^1.0\", \"python\": \"<2.0\"}},\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<2.0\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n\n    installed.add_package(get_package(\"A\", \"1.0\"))\n    installed.add_package(get_package(\"B\", \"1.0\"))\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 1\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_run_install_duplicate_dependencies_different_constraints_with_lock_update(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\n                    \"B\": [\n                        {\"version\": \"^1.0\", \"python\": \"<2.7\"},\n                        {\"version\": \"^2.0\", \"python\": \">=2.7\"},\n                    ]\n                },\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"C\": \"1.2\"},\n                \"requirements\": {\"python\": \"<2.7\"},\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"2.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"C\": \"1.5\"},\n                \"requirements\": {\"python\": \">=2.7\"},\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.2\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n            {\n                \"name\": \"C\",\n                \"version\": \"1.5\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": [], \"C\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.1\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^2.0\"))\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b10.add_dependency(Factory.create_dependency(\"C\", \"1.2\"))\n    package_b20.add_dependency(Factory.create_dependency(\"C\", \"1.5\"))\n\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c15 = get_package(\"C\", \"1.5\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n    repo.add_package(package_c12)\n    repo.add_package(package_c15)\n\n    installed.add_package(get_package(\"A\", \"1.0\"))\n\n    installer.update(True)\n    installer.whitelist([\"A\"])\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-duplicate-dependencies-update\")\n\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 2\n    assert installer.executor.updates_count == 1\n    assert installer.executor.removals_count == 0\n\n\ndef test_installer_test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n) -> None:\n    package.python_versions = \"~2.7 || ^3.4\"\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.5\"})\n    )\n\n    package_a101 = get_package(\"A\", \"1.0.1\")\n    package_a101.python_versions = \">=3.6\"\n\n    package_a100 = get_package(\"A\", \"1.0.0\")\n    package_a100.python_versions = \">=3.5\"\n\n    repo.add_package(package_a100)\n    repo.add_package(package_a101)\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-conditional-dependency\")\n    assert locker.written_data == expected\n    assert installer.executor.installations_count == 1\n\n\ndef test_installer_required_extras_should_not_be_removed_when_updating_single_dependency(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    env: NullEnv,\n    pool: RepositoryPool,\n    config: Config,\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", {\"version\": \"^1.0\"}))\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"extras\": [\"foo\"]})\n    )\n\n    package_b = get_package(\"B\", \"1.0.0\")\n    package_b.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    )\n    package_b.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\")]}\n\n    package_c = get_package(\"C\", \"1.0.0\")\n    package_d = get_package(\"D\", \"1.0.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 3\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n    package.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n    locker.locked(True)\n    locker.mock_lock_data(locker.written_data)\n\n    installed.add_package(package_a)\n    installed.add_package(package_b)\n    installed.add_package(package_c)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n    installer.update(True)\n    installer.whitelist([\"D\"])\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 1\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\ndef test_installer_required_extras_should_not_be_removed_when_updating_single_dependency_pypi_repository(\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    env: NullEnv,\n    mocker: MockerFixture,\n    config: Config,\n    pypi_repository: PyPiRepository,\n) -> None:\n    mocker.patch(\"sys.platform\", \"darwin\")\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"with-transitive-extra-dependency\", {\"version\": \"^0.12\"}\n        )\n    )\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 3\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n    package.add_dependency(Factory.create_dependency(\"pytest\", \"^3.5\"))\n\n    locker.locked(True)\n    locker.mock_lock_data(locker.written_data)\n\n    assert isinstance(installer.executor, TestExecutor)\n    for pkg in installer.executor.installations:\n        installed.add_package(pkg)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n    installer.update(True)\n    installer.whitelist([\"pytest\"])\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 7\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\ndef test_installer_required_extras_should_be_installed(\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    env: NullEnv,\n    config: Config,\n    pypi_repository: PyPiRepository,\n) -> None:\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"with-extra-dependency\", {\"version\": \"^0.12\", \"extras\": [\"filecache\"]}\n        )\n    )\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 2\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n    locker.locked(True)\n    locker.mock_lock_data(locker.written_data)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 2\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_update_multiple_times_with_split_dependencies_is_idempotent(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"A\",\n                \"version\": \"1.0\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"B\": \">=1.0\"},\n            },\n            {\n                \"name\": \"B\",\n                \"version\": \"1.0.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*\",\n                \"checksum\": [],\n                \"dependencies\": {},\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"A\": [], \"B\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    package.python_versions = \"~2.7 || ^3.4\"\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n\n    a10 = get_package(\"A\", \"1.0\")\n    a11 = get_package(\"A\", \"1.1\")\n    a11.add_dependency(Factory.create_dependency(\"B\", \">=1.0.1\"))\n    a11.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"python\": \"~2.7\"})\n    )\n    a11.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^2.0\", \"python\": \"^3.4\"})\n    )\n    b101 = get_package(\"B\", \"1.0.1\")\n    b110 = get_package(\"B\", \"1.1.0\")\n    repo.add_package(a10)\n    repo.add_package(a11)\n    repo.add_package(b101)\n    repo.add_package(b110)\n    repo.add_package(get_package(\"C\", \"1.0\"))\n    repo.add_package(get_package(\"C\", \"2.0\"))\n    expected = fixture(\"with-multiple-updates\")\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert locker.written_data == expected\n\n    locker.mock_lock_data(locker.written_data)\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert locker.written_data == expected\n\n    locker.mock_lock_data(locker.written_data)\n\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert locker.written_data == expected\n\n\ndef test_installer_can_install_dependencies_from_forced_source(\n    locker: Locker,\n    package: ProjectPackage,\n    installed: CustomInstalledRepository,\n    env: NullEnv,\n    config: Config,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(\n        Factory.create_dependency(\"tomlkit\", {\"version\": \"^0.5\", \"source\": \"legacy\"})\n    )\n\n    pool = RepositoryPool()\n    pool.add_repository(legacy_repository)\n    pool.add_repository(pypi_repository)\n\n    installer = Installer(\n        NullIO(),\n        env,\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(env, pool, config, NullIO()),\n    )\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 1\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\ndef test_run_installs_with_url_file(\n    installer: Installer, locker: Locker, repo: Repository, package: ProjectPackage\n) -> None:\n    url = \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"url\": url}))\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-url-dependency\")\n\n    assert locker.written_data == expected\n\n    assert installer.executor.installations_count == 2\n\n\n@pytest.mark.parametrize(\"env_platform\", [\"linux\", \"win32\"])\ndef test_run_installs_with_same_version_url_files(\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    repo: Repository,\n    package: ProjectPackage,\n    env_platform: str,\n) -> None:\n    urls = {\n        \"linux\": \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\",\n        \"win32\": (\n            \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n        ),\n    }\n    for platform, url in urls.items():\n        package.add_dependency(\n            Factory.create_dependency(\n                \"demo\",\n                {\"url\": url, \"markers\": f\"sys_platform == '{platform}'\"},\n            )\n        )\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    installer = Installer(\n        NullIO(),\n        MockEnv(platform=env_platform),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(platform=env_platform),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-same-version-url-dependencies\")\n    assert locker.written_data == expected\n    assert isinstance(installer.executor, TestExecutor)\n    assert installer.executor.installations_count == 2\n    demo_package = next(p for p in installer.executor.installations if p.name == \"demo\")\n    assert demo_package.source_url == urls[env_platform]\n\n\ndef test_installer_uses_prereleases_if_they_are_compatible(\n    installer: Installer, locker: Locker, package: ProjectPackage, repo: Repository\n) -> None:\n    package.python_versions = \"~2.7 || ^3.4\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"prerelease\", {\"git\": \"https://github.com/demo/prerelease.git\"}\n        )\n    )\n\n    package_b = get_package(\"b\", \"2.0.0\")\n    package_b.add_dependency(Factory.create_dependency(\"prerelease\", \">=0.19\"))\n\n    repo.add_package(package_b)\n\n    result = installer.run()\n    assert result == 0\n\n    locker.locked(True)\n    locker.mock_lock_data(locker.written_data)\n\n    package.add_dependency(Factory.create_dependency(\"b\", \"^2.0.0\"))\n\n    installer.whitelist([\"b\"])\n    installer.update(True)\n    result = installer.run()\n    assert result == 0\n\n    assert installer.executor.installations_count == 2\n\n\ndef test_installer_does_not_write_lock_file_when_installation_fails(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    mocker: MockerFixture,\n) -> None:\n    repo.add_package(get_package(\"A\", \"1.0\"))\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n\n    locker.locked(False)\n\n    mocker.patch(\"poetry.installation.installer.Installer._execute\", return_value=1)\n    result = installer.run()\n    assert result == 1  # error\n\n    assert locker._lock_data is None\n\n    assert installer.executor.installations_count == 0\n    assert installer.executor.updates_count == 0\n    assert installer.executor.removals_count == 0\n\n\n@pytest.mark.parametrize(\"quiet\", [True, False])\ndef test_run_with_dependencies_quiet(\n    installer: Installer,\n    locker: Locker,\n    repo: Repository,\n    package: ProjectPackage,\n    quiet: bool,\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    installer._io = BufferedIO(Input())\n    installer._io.set_verbosity(Verbosity.QUIET if quiet else Verbosity.NORMAL)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    expected = fixture(\"with-dependencies\")\n    assert locker.written_data == expected\n\n    output = installer._io.fetch_output()\n    if quiet:\n        assert output == \"\"\n    else:\n        assert output != \"\"\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"1.1\", \"2.1\"))\ndef test_installer_should_use_the_locked_version_of_git_dependencies(\n    installer: Installer,\n    locker: Locker,\n    package: ProjectPackage,\n    repo: Repository,\n    lock_version: str,\n) -> None:\n    lock_data = {\n        \"package\": [\n            {\n                \"name\": \"demo\",\n                \"version\": \"0.1.1\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {\"pendulum\": \">=1.4.4\"},\n                \"source\": {\n                    \"type\": \"git\",\n                    \"url\": \"https://github.com/demo/demo.git\",\n                    \"reference\": \"master\",\n                    \"resolved_reference\": \"123456\",\n                },\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"1.4.4\",\n                \"optional\": False,\n                \"platform\": \"*\",\n                \"python-versions\": \"*\",\n                \"checksum\": [],\n                \"dependencies\": {},\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"platform\": \"*\",\n            \"content-hash\": \"123456789\",\n            \"files\": {\"demo\": [], \"pendulum\": []},\n        },\n    }\n    fix_lock_data(lock_data)\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"git\": \"https://github.com/demo/demo.git\", \"branch\": \"master\"}\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    result = installer.run()\n    assert result == 0\n\n    assert isinstance(installer.executor, TestExecutor)\n    demo_installation = next(\n        package\n        for package in installer.executor.installations\n        if package.name == \"demo\"\n    )\n    assert demo_installation == Package(\n        \"demo\",\n        \"0.1.1\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=\"master\",\n        source_resolved_reference=\"123456\",\n    )\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\ndef test_installer_should_use_the_locked_version_of_git_dependencies_with_extras(\n    installer: Installer,\n    locker: Locker,\n    package: ProjectPackage,\n    repo: Repository,\n    is_locked: bool,\n) -> None:\n    if is_locked:\n        locker.locked(True)\n        locker.mock_lock_data(fixture(\"with-vcs-dependency-with-extras\"))\n        expected_reference = \"123456\"\n    else:\n        expected_reference = MOCK_DEFAULT_GIT_REVISION\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"git\": \"https://github.com/demo/demo.git\",\n                \"branch\": \"master\",\n                \"extras\": [\"foo\"],\n            },\n        )\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"cleo\", \"1.0.0\"))\n\n    result = installer.run()\n    assert result == 0\n\n    assert isinstance(installer.executor, TestExecutor)\n    assert len(installer.executor.installations) == 3\n    demo_installation = next(\n        package\n        for package in installer.executor.installations\n        if package.name == \"demo\"\n    )\n    assert demo_installation == Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=\"master\",\n        source_resolved_reference=expected_reference,\n    )\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\ndef test_installer_should_use_the_locked_version_of_git_dependencies_without_reference(\n    installer: Installer,\n    locker: Locker,\n    package: ProjectPackage,\n    repo: Repository,\n    is_locked: bool,\n) -> None:\n    \"\"\"\n    If there is no explicit reference (branch or tag or rev) in pyproject.toml,\n    HEAD is used.\n    \"\"\"\n    if is_locked:\n        locker.locked(True)\n        locker.mock_lock_data(fixture(\"with-vcs-dependency-without-ref\"))\n        expected_reference = \"123456\"\n    else:\n        expected_reference = MOCK_DEFAULT_GIT_REVISION\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n\n    result = installer.run()\n    assert result == 0\n\n    assert isinstance(installer.executor, TestExecutor)\n    assert len(installer.executor.installations) == 2\n    demo_installation = next(\n        package\n        for package in installer.executor.installations\n        if package.name == \"demo\"\n    )\n    assert demo_installation == Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=\"HEAD\",\n        source_resolved_reference=expected_reference,\n    )\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"2.0\", \"2.1\"))\n@pytest.mark.parametrize(\"env_platform\", [\"darwin\", \"linux\"])\ndef test_installer_distinguishes_locked_packages_with_local_version_by_source(\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    repo: Repository,\n    package: ProjectPackage,\n    env_platform: str,\n    lock_version: str,\n) -> None:\n    \"\"\"https://github.com/python-poetry/poetry/issues/6710\"\"\"\n    # Require 1.11.0+cpu from pytorch for most platforms, but specify 1.11.0 and pypi on\n    # darwin.\n    package.add_dependency(\n        Factory.create_dependency(\n            \"torch\",\n            {\n                \"version\": \"1.11.0+cpu\",\n                \"markers\": \"sys_platform != 'darwin'\",\n                \"source\": \"pytorch\",\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"torch\",\n            {\n                \"version\": \"1.11.0\",\n                \"markers\": \"sys_platform == 'darwin'\",\n                \"source\": \"pypi\",\n            },\n        )\n    )\n\n    # Locking finds both the pypi and the pytorch packages.\n    lock_data: dict[str, Any] = {\n        \"package\": [\n            {\n                \"name\": \"torch\",\n                \"version\": \"1.11.0\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n            },\n            {\n                \"name\": \"torch\",\n                \"version\": \"1.11.0+cpu\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n                \"source\": {\n                    \"type\": \"legacy\",\n                    \"url\": \"https://download.pytorch.org/whl\",\n                    \"reference\": \"pytorch\",\n                },\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n        },\n    }\n    if lock_version == \"2.1\":\n        lock_data[\"package\"][0][\"groups\"] = [\"main\"]\n        lock_data[\"package\"][0][\"markers\"] = \"sys_platform == 'darwin'\"\n        lock_data[\"package\"][1][\"groups\"] = [\"main\"]\n        lock_data[\"package\"][1][\"markers\"] = \"sys_platform != 'darwin'\"\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    installer = Installer(\n        NullIO(),\n        MockEnv(platform=env_platform),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(platform=env_platform),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n    result = installer.run()\n    assert result == 0\n\n    # Results of installation are consistent with the platform requirements.\n    version = \"1.11.0\" if env_platform == \"darwin\" else \"1.11.0+cpu\"\n    source_type = None if env_platform == \"darwin\" else \"legacy\"\n    source_url = (\n        None if env_platform == \"darwin\" else \"https://download.pytorch.org/whl\"\n    )\n    source_reference = None if env_platform == \"darwin\" else \"pytorch\"\n\n    assert isinstance(installer.executor, TestExecutor)\n    assert len(installer.executor.installations) == 1\n    assert installer.executor.installations[0] == Package(\n        \"torch\",\n        version,\n        source_type=source_type,\n        source_url=source_url,\n        source_reference=source_reference,\n    )\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"2.0\", \"2.1\"))\n@pytest.mark.parametrize(\"env_platform_machine\", [\"aarch64\", \"amd64\"])\ndef test_installer_distinguishes_locked_packages_with_same_version_by_source(\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    repo: Repository,\n    package: ProjectPackage,\n    env_platform_machine: str,\n    lock_version: str,\n) -> None:\n    \"\"\"https://github.com/python-poetry/poetry/issues/8303\"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"kivy\",\n            {\n                \"version\": \"2.2.1\",\n                \"markers\": \"platform_machine == 'aarch64'\",\n                \"source\": \"pywheels\",\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"kivy\",\n            {\n                \"version\": \"2.2.1\",\n                \"markers\": \"platform_machine != 'aarch64'\",\n                \"source\": \"PyPI\",\n            },\n        )\n    )\n\n    # Locking finds both the pypi and the pyhweels packages.\n    lock_data: dict[str, Any] = {\n        \"package\": [\n            {\n                \"name\": \"kivy\",\n                \"version\": \"2.2.1\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n            },\n            {\n                \"name\": \"kivy\",\n                \"version\": \"2.2.1\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n                \"source\": {\n                    \"type\": \"legacy\",\n                    \"url\": \"https://www.piwheels.org/simple\",\n                    \"reference\": \"pywheels\",\n                },\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n        },\n    }\n    if lock_version == \"2.1\":\n        lock_data[\"package\"][0][\"groups\"] = [\"main\"]\n        lock_data[\"package\"][0][\"markers\"] = \"platform_machine != 'aarch64'\"\n        lock_data[\"package\"][1][\"groups\"] = [\"main\"]\n        lock_data[\"package\"][1][\"markers\"] = \"platform_machine == 'aarch64'\"\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    installer = Installer(\n        NullIO(),\n        MockEnv(platform_machine=env_platform_machine),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(platform_machine=env_platform_machine),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n    result = installer.run()\n    assert result == 0\n\n    # Results of installation are consistent with the platform requirements.\n    version = \"2.2.1\"\n    if env_platform_machine == \"aarch64\":\n        source_type = \"legacy\"\n        source_url = \"https://www.piwheels.org/simple\"\n        source_reference = \"pywheels\"\n    else:\n        source_type = None\n        source_url = None\n        source_reference = None\n\n    assert isinstance(installer.executor, TestExecutor)\n    assert len(installer.executor.installations) == 1\n    assert installer.executor.installations[0] == Package(\n        \"kivy\",\n        version,\n        source_type=source_type,\n        source_url=source_url,\n        source_reference=source_reference,\n    )\n\n\n@pytest.mark.parametrize(\"lock_version\", (\"2.0\", \"2.1\"))\n@pytest.mark.parametrize(\"env_platform\", [\"darwin\", \"linux\"])\ndef test_explicit_source_dependency_with_direct_origin_dependency(\n    pool: RepositoryPool,\n    locker: Locker,\n    installed: CustomInstalledRepository,\n    config: Config,\n    repo: Repository,\n    package: ProjectPackage,\n    env_platform: str,\n    lock_version: str,\n) -> None:\n    \"\"\"\n    A dependency with explicit source should not be satisfied by\n    a direct origin dependency even if there is a version match.\n    \"\"\"\n    demo_url = (\n        \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"markers\": \"sys_platform != 'darwin'\",\n                \"url\": demo_url,\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"version\": \"0.1.0\",\n                \"markers\": \"sys_platform == 'darwin'\",\n                \"source\": \"repo\",\n            },\n        )\n    )\n    # The url demo dependency depends on pendulum.\n    repo.add_package(get_package(\"pendulum\", \"1.4.4\"))\n    repo.add_package(get_package(\"demo\", \"0.1.0\"))\n\n    # Locking finds both the direct origin and the explicit source packages.\n    lock_data: dict[str, Any] = {\n        \"package\": [\n            {\n                \"name\": \"demo\",\n                \"version\": \"0.1.0\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n                \"dependencies\": {\"pendulum\": \">=1.4.4\"},\n                \"source\": {\n                    \"type\": \"url\",\n                    \"url\": demo_url,\n                },\n            },\n            {\n                \"name\": \"demo\",\n                \"version\": \"0.1.0\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n                \"source\": {\n                    \"type\": \"legacy\",\n                    \"url\": \"https://www.demo.org/simple\",\n                    \"reference\": \"repo\",\n                },\n            },\n            {\n                \"name\": \"pendulum\",\n                \"version\": \"1.4.4\",\n                \"optional\": False,\n                \"files\": [],\n                \"python-versions\": \"*\",\n            },\n        ],\n        \"metadata\": {\n            \"lock-version\": lock_version,\n            \"python-versions\": \"*\",\n            \"content-hash\": \"123456789\",\n        },\n    }\n    if lock_version == \"2.1\":\n        for locked_package in lock_data[\"package\"]:\n            locked_package[\"groups\"] = [\"main\"]\n        lock_data[\"package\"][0][\"markers\"] = \"sys_platform != 'darwin'\"\n        lock_data[\"package\"][1][\"markers\"] = \"sys_platform == 'darwin'\"\n        lock_data[\"package\"][2][\"markers\"] = \"sys_platform != 'darwin'\"\n    locker.locked(True)\n    locker.mock_lock_data(lock_data)\n    installer = Installer(\n        NullIO(),\n        MockEnv(platform=env_platform),\n        package,\n        locker,\n        pool,\n        config,\n        installed=installed,\n        executor=TestExecutor(\n            MockEnv(platform=env_platform),\n            pool,\n            config,\n            NullIO(),\n        ),\n    )\n\n    result = installer.run()\n\n    assert result == 0\n    assert isinstance(installer.executor, TestExecutor)\n    if env_platform == \"linux\":\n        assert set(installer.executor.installations) == {\n            Package(\"pendulum\", \"1.4.4\"),\n            Package(\n                \"demo\",\n                \"0.1.0\",\n                source_type=\"url\",\n                source_url=demo_url,\n            ),\n        }\n    else:\n        assert installer.executor.installations == [\n            Package(\n                \"demo\",\n                \"0.1.0\",\n                source_type=\"legacy\",\n                source_url=\"https://www.demo.org/simple\",\n                source_reference=\"repo\",\n            )\n        ]\n"
  },
  {
    "path": "tests/installation/test_wheel_installer.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version import parse_constraint\n\nfrom poetry.installation.wheel_installer import WheelInstaller\nfrom poetry.utils.env import MockEnv\n\n\nif TYPE_CHECKING:\n    from pytest import TempPathFactory\n\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture\ndef env(tmp_path: Path) -> MockEnv:\n    return MockEnv(path=tmp_path)\n\n\n@pytest.fixture(scope=\"module\")\ndef demo_wheel(fixture_dir: FixtureDirGetter) -> Path:\n    return fixture_dir(\"distributions/demo-0.1.0-py2.py3-none-any.whl\")\n\n\n@pytest.fixture(scope=\"module\")\ndef default_installation(tmp_path_factory: TempPathFactory, demo_wheel: Path) -> Path:\n    env = MockEnv(path=tmp_path_factory.mktemp(\"default_install\"))\n    installer = WheelInstaller(env)\n    installer.install(demo_wheel)\n    return Path(env.paths[\"purelib\"])\n\n\ndef test_default_installation_source_dir_content(default_installation: Path) -> None:\n    source_dir = default_installation / \"demo\"\n    assert source_dir.exists()\n    assert (source_dir / \"__init__.py\").exists()\n\n\ndef test_default_installation_dist_info_dir_content(default_installation: Path) -> None:\n    dist_info_dir = default_installation / \"demo-0.1.0.dist-info\"\n    assert dist_info_dir.exists()\n    assert (dist_info_dir / \"INSTALLER\").exists()\n    assert (dist_info_dir / \"METADATA\").exists()\n    assert (dist_info_dir / \"RECORD\").exists()\n    assert (dist_info_dir / \"WHEEL\").exists()\n\n\ndef test_installer_file_contains_valid_version(default_installation: Path) -> None:\n    installer_file = default_installation / \"demo-0.1.0.dist-info\" / \"INSTALLER\"\n    with open(installer_file, encoding=\"utf-8\") as f:\n        installer_content = f.read()\n    match = re.match(r\"Poetry (?P<version>.*)\", installer_content)\n    assert match\n    parse_constraint(match.group(\"version\"))  # must not raise an error\n\n\ndef test_default_installation_no_bytecode(default_installation: Path) -> None:\n    cache_dir = default_installation / \"demo\" / \"__pycache__\"\n    assert not cache_dir.exists()\n\n\n@pytest.mark.parametrize(\"compile\", [True, False])\ndef test_enable_bytecode_compilation(\n    env: MockEnv, demo_wheel: Path, compile: bool\n) -> None:\n    installer = WheelInstaller(env)\n    installer.enable_bytecode_compilation(compile)\n    installer.install(demo_wheel)\n    cache_dir = Path(env.paths[\"purelib\"]) / \"demo\" / \"__pycache__\"\n    if compile:\n        assert cache_dir.exists()\n        assert list(cache_dir.glob(\"*.pyc\"))\n        assert not list(cache_dir.glob(\"*.opt-1.pyc\"))\n        assert not list(cache_dir.glob(\"*.opt-2.pyc\"))\n    else:\n        assert not cache_dir.exists()\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/test_utils_vcs_git.py",
    "content": "from __future__ import annotations\n\nimport os\nimport uuid\n\nfrom copy import deepcopy\nfrom hashlib import sha1\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import TypedDict\nfrom urllib.parse import urlparse\nfrom urllib.parse import urlunparse\n\nimport pytest\n\nfrom dulwich.client import HTTPUnauthorized\nfrom dulwich.client import get_transport_and_path\nfrom dulwich.config import CaseInsensitiveOrderedMultiDict\nfrom dulwich.config import ConfigFile\nfrom dulwich.refs import Ref\nfrom dulwich.repo import Repo\n\nfrom poetry.console.exceptions import PoetryConsoleError\nfrom poetry.pyproject.toml import PyProjectTOML\nfrom poetry.utils.authenticator import Authenticator\nfrom poetry.vcs.git import Git\nfrom poetry.vcs.git.backend import GitRefSpec\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from dulwich.client import FetchPackResult\n    from dulwich.client import GitClient\n    from pytest import TempPathFactory\n    from pytest_mock import MockerFixture\n\n    from tests.conftest import Config\n\n\n# these tests are integration as they rely on an external repository\n# see `source_url` fixture\npytestmark = pytest.mark.integration\n\n\nclass GitCloneKwargs(TypedDict):\n    name: str | None\n    branch: str | None\n    tag: str | None\n    revision: str | None\n    source_root: Path | None\n    clean: bool\n\n\n@pytest.fixture(autouse=True)\ndef git_mock() -> None:\n    pass\n\n\n@pytest.fixture(autouse=True)\ndef setup(config: Config) -> None:\n    pass\n\n\nREVISION_TO_VERSION_MAP = {\n    \"b6204750a763268e941cec1f05f8986b6c66913e\": \"0.1.0\",  # Annotated Tag\n    \"18d3ff247d288da701fc7f9ce2ec718388fca266\": \"0.1.1-alpha.0\",\n    \"dd07e8d4efb82690e7975b289917a7782fbef29b\": \"0.2.0-alpha.0\",\n    \"7263819922b4cd008afbb447f425a562432dad7d\": \"0.2.0-alpha.1\",\n}\n\nBRANCH_TO_REVISION_MAP = {\"0.1\": \"18d3ff247d288da701fc7f9ce2ec718388fca266\"}\n\nTAG_TO_REVISION_MAP = {\"v0.1.0\": \"b6204750a763268e941cec1f05f8986b6c66913e\"}\n\nREF_TO_REVISION_MAP = {\n    \"branch\": BRANCH_TO_REVISION_MAP,\n    \"tag\": TAG_TO_REVISION_MAP,\n}\n\n\n@pytest.fixture\ndef use_system_git_client(config: Config) -> None:\n    config.merge({\"system-git-client\": True})\n\n\n@pytest.fixture(scope=\"module\")\ndef source_url() -> str:\n    return \"https://github.com/python-poetry/test-fixture-vcs-repository.git\"\n\n\n@pytest.fixture(scope=\"module\")\ndef source_directory_name(source_url: str) -> str:\n    return Git.get_name_from_source_url(url=source_url)\n\n\n@pytest.fixture(scope=\"module\")\ndef local_repo(\n    tmp_path_factory: TempPathFactory, source_directory_name: str\n) -> Iterator[Repo]:\n    with Repo.init(\n        str(tmp_path_factory.mktemp(\"src\") / source_directory_name), mkdir=True\n    ) as repo:\n        yield repo\n\n\n@pytest.fixture(scope=\"module\")\ndef _remote_refs(source_url: str, local_repo: Repo) -> FetchPackResult:\n    client: GitClient\n    path: str\n    client, path = get_transport_and_path(source_url)\n    return client.fetch(\n        path, local_repo, determine_wants=local_repo.object_store.determine_wants_all\n    )\n\n\n@pytest.fixture\ndef remote_refs(_remote_refs: FetchPackResult) -> FetchPackResult:\n    return deepcopy(_remote_refs)\n\n\n@pytest.fixture(scope=\"module\")\ndef remote_default_ref(_remote_refs: FetchPackResult) -> Ref:\n    ref: Ref = _remote_refs.symrefs[Ref(b\"HEAD\")]\n    return ref\n\n\n@pytest.fixture(scope=\"module\")\ndef remote_default_branch(remote_default_ref: bytes) -> str:\n    return remote_default_ref.decode(\"utf-8\").replace(\"refs/heads/\", \"\")\n\n\n# Regression test for https://github.com/python-poetry/poetry/issues/6722\ndef test_use_system_git_client_from_environment_variables() -> None:\n    os.environ[\"POETRY_SYSTEM_GIT_CLIENT\"] = \"true\"\n\n    assert Git.is_using_legacy_client()\n\n\ndef test_git_local_info(\n    source_url: str, remote_refs: FetchPackResult, remote_default_ref: Ref\n) -> None:\n    with Git.clone(url=source_url) as repo:\n        info = Git.info(repo=repo)\n        assert info.origin == source_url\n        ref = remote_refs.refs[remote_default_ref]\n        assert ref is not None\n        assert info.revision == ref.decode(\"utf-8\")\n\n\n@pytest.mark.parametrize(\n    \"specification\", [{}, {\"revision\": \"HEAD\"}, {\"branch\": \"HEAD\"}]\n)\ndef test_git_clone_default_branch_head(\n    specification: GitCloneKwargs,\n    source_url: str,\n    remote_refs: FetchPackResult,\n    remote_default_ref: Ref,\n    mocker: MockerFixture,\n) -> None:\n    spy = mocker.spy(Git, \"_clone\")\n    spy_legacy = mocker.spy(Git, \"_clone_legacy\")\n\n    with Git.clone(url=source_url, **specification) as repo:\n        assert remote_refs.refs[remote_default_ref] == repo.head()\n\n    spy_legacy.assert_not_called()\n    spy.assert_called()\n\n\ndef test_git_clone_fails_for_non_existent_branch(source_url: str) -> None:\n    branch = uuid.uuid4().hex\n\n    with pytest.raises(PoetryConsoleError) as e:\n        Git.clone(url=source_url, branch=branch)\n\n    assert f\"Failed to clone {source_url} at '{branch}'\" in str(e.value)\n\n\ndef test_git_clone_fails_for_non_existent_revision(source_url: str) -> None:\n    revision = sha1(uuid.uuid4().bytes).hexdigest()\n\n    with pytest.raises(PoetryConsoleError) as e:\n        Git.clone(url=source_url, revision=revision)\n\n    assert f\"Failed to clone {source_url} at '{revision}'\" in str(e.value)\n\n\ndef assert_version(repo: Repo, expected_revision: str) -> None:\n    version = PyProjectTOML(\n        path=Path(repo.path).joinpath(\"pyproject.toml\")\n    ).poetry_config[\"version\"]\n\n    revision = Git.get_revision(repo=repo)\n\n    assert revision == expected_revision\n    assert revision in REVISION_TO_VERSION_MAP\n    assert version == REVISION_TO_VERSION_MAP[revision]\n\n\ndef test_git_clone_when_branch_is_ref(source_url: str) -> None:\n    with Git.clone(url=source_url, branch=\"refs/heads/0.1\") as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n\n@pytest.mark.parametrize(\"branch\", [*BRANCH_TO_REVISION_MAP.keys()])\ndef test_git_clone_branch(\n    source_url: str, remote_refs: FetchPackResult, branch: str\n) -> None:\n    with Git.clone(url=source_url, branch=branch) as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[branch])\n\n\n@pytest.mark.parametrize(\"tag\", [*TAG_TO_REVISION_MAP.keys()])\ndef test_git_clone_tag(source_url: str, remote_refs: FetchPackResult, tag: str) -> None:\n    with Git.clone(url=source_url, tag=tag) as repo:\n        assert_version(repo, TAG_TO_REVISION_MAP[tag])\n\n\ndef test_git_clone_multiple_times(\n    source_url: str, remote_refs: FetchPackResult\n) -> None:\n    for revision in REVISION_TO_VERSION_MAP:\n        with Git.clone(url=source_url, revision=revision) as repo:\n            assert_version(repo, revision)\n\n\ndef test_git_clone_revision_is_branch(\n    source_url: str, remote_refs: FetchPackResult\n) -> None:\n    with Git.clone(url=source_url, revision=\"0.1\") as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n\ndef test_git_clone_revision_is_ref(\n    source_url: str, remote_refs: FetchPackResult\n) -> None:\n    with Git.clone(url=source_url, revision=\"refs/heads/0.1\") as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n\n@pytest.mark.parametrize(\n    (\"revision\", \"expected_revision\"),\n    [\n        (\"0.1\", BRANCH_TO_REVISION_MAP[\"0.1\"]),\n        (\"v0.1.0\", TAG_TO_REVISION_MAP[\"v0.1.0\"]),\n        *zip(REVISION_TO_VERSION_MAP, REVISION_TO_VERSION_MAP),\n    ],\n)\ndef test_git_clone_revision_is_tag(\n    source_url: str, remote_refs: FetchPackResult, revision: str, expected_revision: str\n) -> None:\n    with Git.clone(url=source_url, revision=revision) as repo:\n        assert_version(repo, expected_revision)\n\n\ndef test_git_clone_clones_submodules(source_url: str) -> None:\n    with Git.clone(url=source_url) as repo:\n        submodule_package_directory = (\n            Path(repo.path) / \"submodules\" / \"sample-namespace-packages\"\n        )\n\n    assert submodule_package_directory.exists()\n    assert submodule_package_directory.joinpath(\"README.md\").exists()\n    assert len(list(submodule_package_directory.glob(\"*\"))) > 1\n\n\ndef test_git_clone_clones_submodules_with_relative_urls(source_url: str) -> None:\n    with Git.clone(url=source_url, branch=\"relative_submodule\") as repo:\n        submodule_package_directory = (\n            Path(repo.path) / \"submodules\" / \"relative-url-submodule\"\n        )\n\n    assert submodule_package_directory.exists()\n    assert submodule_package_directory.joinpath(\"README.md\").exists()\n    assert len(list(submodule_package_directory.glob(\"*\"))) > 1\n\n\ndef test_git_clone_clones_submodules_with_relative_urls_and_explicit_base(\n    source_url: str,\n) -> None:\n    with Git.clone(url=source_url, branch=\"relative_submodule\") as repo:\n        submodule_package_directory = (\n            Path(repo.path) / \"submodules\" / \"relative-url-submodule-with-base\"\n        )\n\n    assert submodule_package_directory.exists()\n    assert submodule_package_directory.joinpath(\"README.md\").exists()\n    assert len(list(submodule_package_directory.glob(\"*\"))) > 1\n\n\ndef test_system_git_fallback_on_http_401(\n    mocker: MockerFixture,\n    source_url: str,\n    tmp_path: Path,\n) -> None:\n    spy = mocker.spy(Git, \"_clone_legacy\")\n    mocker.patch.object(\n        Git,\n        \"_clone\",\n        side_effect=HTTPUnauthorized(None, source_url),\n    )\n\n    # use tmp_path for source_root to get a shorter path,\n    # because long paths can cause issues with the system git client on Windows\n    # despite of setting core.longpaths=true\n    with Git.clone(url=source_url, branch=\"0.1\", source_root=tmp_path) as repo:\n        path = Path(repo.path)\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n    spy.assert_called_with(\n        url=\"https://github.com/python-poetry/test-fixture-vcs-repository.git\",\n        target=path,\n        refspec=GitRefSpec(branch=\"0.1\", revision=None, tag=None, ref=Ref(b\"HEAD\")),\n    )\n    spy.assert_called_once()\n\n\nGIT_USERNAME = os.environ.get(\"POETRY_TEST_INTEGRATION_GIT_USERNAME\")\nGIT_PASSWORD = os.environ.get(\"POETRY_TEST_INTEGRATION_GIT_PASSWORD\")\nHTTP_AUTH_CREDENTIALS_UNAVAILABLE = not (GIT_USERNAME and GIT_PASSWORD)\n\n\n@pytest.mark.skipif(\n    HTTP_AUTH_CREDENTIALS_UNAVAILABLE,\n    reason=\"HTTP authentication credentials not available\",\n)\ndef test_configured_repository_http_auth(\n    mocker: MockerFixture, source_url: str, config: Config\n) -> None:\n    from poetry.vcs.git import backend\n\n    spy_clone_legacy = mocker.spy(Git, \"_clone_legacy\")\n    spy_get_transport_and_path = mocker.spy(backend, \"get_transport_and_path\")\n\n    config.merge(\n        {\n            \"repositories\": {\"git-repo\": {\"url\": source_url}},\n            \"http-basic\": {\n                \"git-repo\": {\n                    \"username\": GIT_USERNAME,\n                    \"password\": GIT_PASSWORD,\n                }\n            },\n        }\n    )\n\n    dummy_git_config = ConfigFile()\n    mocker.patch(\n        \"poetry.vcs.git.backend.Repo.get_config_stack\",\n        return_value=dummy_git_config,\n    )\n\n    mocker.patch(\n        \"poetry.vcs.git.backend.get_default_authenticator\",\n        return_value=Authenticator(config=config),\n    )\n\n    with Git.clone(url=source_url, branch=\"0.1\") as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n    spy_clone_legacy.assert_not_called()\n\n    spy_get_transport_and_path.assert_called_with(\n        location=source_url,\n        config=dummy_git_config,\n        username=GIT_USERNAME,\n        password=GIT_PASSWORD,\n    )\n    spy_get_transport_and_path.assert_called_once()\n\n\ndef test_username_password_parameter_is_not_passed_to_dulwich(\n    mocker: MockerFixture, source_url: str, config: Config\n) -> None:\n    from poetry.vcs.git import backend\n\n    spy_clone = mocker.spy(Git, \"_clone\")\n    spy_get_transport_and_path = mocker.spy(backend, \"get_transport_and_path\")\n\n    dummy_git_config = ConfigFile()\n    mocker.patch(\n        \"poetry.vcs.git.backend.Repo.get_config_stack\",\n        return_value=dummy_git_config,\n    )\n\n    with Git.clone(url=source_url, branch=\"0.1\") as repo:\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n    spy_clone.assert_called_once()\n\n    spy_get_transport_and_path.assert_called_with(\n        location=source_url,\n        config=dummy_git_config,\n        username=None,\n        password=None,\n    )\n    spy_get_transport_and_path.assert_called_once()\n\n\ndef test_system_git_called_when_configured(\n    mocker: MockerFixture, source_url: str, use_system_git_client: None, tmp_path: Path\n) -> None:\n    spy_legacy = mocker.spy(Git, \"_clone_legacy\")\n    spy = mocker.spy(Git, \"_clone\")\n\n    # use tmp_path for source_root to get a shorter path,\n    # because long paths can cause issues with the system git client on Windows\n    # despite of setting core.longpaths=true\n    with Git.clone(url=source_url, branch=\"0.1\", source_root=tmp_path) as repo:\n        path = Path(repo.path)\n        assert_version(repo, BRANCH_TO_REVISION_MAP[\"0.1\"])\n\n    spy.assert_not_called()\n\n    spy_legacy.assert_called_once()\n    spy_legacy.assert_called_with(\n        url=source_url,\n        target=path,\n        refspec=GitRefSpec(branch=\"0.1\", revision=None, tag=None, ref=Ref(b\"HEAD\")),\n    )\n\n\ndef test_relative_submodules_with_ssh(\n    source_url: str, tmpdir: Path, mocker: MockerFixture\n) -> None:\n    target = tmpdir / \"temp\"\n    ssh_source_url = urlunparse(urlparse(source_url)._replace(scheme=\"ssh\"))\n\n    repo_with_unresolved_submodules = Git._clone(\n        url=source_url,\n        refspec=GitRefSpec(branch=\"relative_submodule\"),\n        target=target,\n    )\n\n    # construct fake git config\n    values = CaseInsensitiveOrderedMultiDict.make(\n        {b\"url\": ssh_source_url.encode(\"utf-8\")}\n    )\n    fake_config = ConfigFile({(b\"remote\", b\"origin\"): values})\n    # trick Git into thinking remote.origin is an ssh url\n    mock_get_config = mocker.patch.object(repo_with_unresolved_submodules, \"get_config\")\n    mock_get_config.return_value = fake_config\n\n    submodules = Git._get_submodules(repo_with_unresolved_submodules)\n\n    assert [s.url for s in submodules] == [\n        \"https://github.com/pypa/sample-namespace-packages.git\",\n        \"ssh://github.com/python-poetry/test-fixture-vcs-repository.git\",\n        \"ssh://github.com/python-poetry/test-fixture-vcs-repository.git\",\n    ]\n"
  },
  {
    "path": "tests/json/__init__.py",
    "content": ""
  },
  {
    "path": "tests/json/fixtures/build_constraints.toml",
    "content": "[project]\nname = \"build-constraints\"\nversion = \"0.1.0\"\n\n[tool.poetry.build-constraints]\nLegacy-Lib = { setuptools = \"<75\" }\nno-constraints = {}\n\n[tool.poetry.build-constraints.c-ext-lib]\nCython = { version = \"<3.1\", source = \"pypi\" }\nsetuptools = [\n    { version = \">=60,<75\", python = \"<3.9\" },\n    { version = \">=75\", python = \">=3.8\" }\n]\n"
  },
  {
    "path": "tests/json/fixtures/self_invalid_plugin.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\n\n[tool.poetry.requires-plugins]\nfoo = 5\n"
  },
  {
    "path": "tests/json/fixtures/self_invalid_version.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\nrequires-poetry = 2\n"
  },
  {
    "path": "tests/json/fixtures/self_valid.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\nrequires-poetry = \">=2.0\"\n\n[tool.poetry.requires-plugins]\nfoo = \">=1.0\"\n"
  },
  {
    "path": "tests/json/fixtures/source/complete_invalid_priority.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\n\n[[tool.poetry.source]]\nname = \"pypi-simple\"\nurl = \"https://pypi.org/simple/\"\npriority = \"arbitrary\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/json/fixtures/source/complete_invalid_url.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\n\n[[tool.poetry.source]]\nname = \"pypi-simple\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/json/fixtures/source/complete_valid.toml",
    "content": "[tool.poetry]\nname = \"foobar\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\n\n[[tool.poetry.source]]\nname = \"pypi-simple\"\nurl = \"https://pypi.org/simple/\"\npriority = \"explicit\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "tests/json/test_schema.py",
    "content": "from __future__ import annotations\n\nimport json\n\nfrom importlib.resources import files\nfrom pathlib import Path\nfrom typing import Any\n\nfrom poetry.factory import Factory\nfrom poetry.toml import TOMLFile\n\n\nSCHEMA_FILE = files(\"poetry.json\") / \"schemas\" / \"poetry.json\"\nFIXTURE_DIR = Path(__file__).parent / \"fixtures\"\nSOURCE_FIXTURE_DIR = FIXTURE_DIR / \"source\"\n\n\ndef test_pyproject_toml_valid() -> None:\n    toml: dict[str, Any] = TOMLFile(SOURCE_FIXTURE_DIR / \"complete_valid.toml\").read()\n    assert Factory.validate(toml) == {\"errors\": [], \"warnings\": []}\n\n\ndef test_pyproject_toml_invalid_priority() -> None:\n    toml: dict[str, Any] = TOMLFile(\n        SOURCE_FIXTURE_DIR / \"complete_invalid_priority.toml\"\n    ).read()\n    assert Factory.validate(toml) == {\n        \"errors\": [\n            \"tool.poetry.source[0].priority must be one of ['primary',\"\n            \" 'supplemental', 'explicit']\"\n        ],\n        \"warnings\": [],\n    }\n\n\ndef test_self_valid() -> None:\n    toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / \"self_valid.toml\").read()\n    assert Factory.validate(toml) == {\"errors\": [], \"warnings\": []}\n\n\ndef test_self_invalid_version() -> None:\n    toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / \"self_invalid_version.toml\").read()\n    assert Factory.validate(toml) == {\n        \"errors\": [\"tool.poetry.requires-poetry must be string\"],\n        \"warnings\": [],\n    }\n\n\ndef test_self_invalid_plugin() -> None:\n    toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / \"self_invalid_plugin.toml\").read()\n    assert Factory.validate(toml) == {\n        \"errors\": [\n            \"tool.poetry.requires-plugins.foo must be valid exactly by one definition\"\n            \" (0 matches found)\"\n        ],\n        \"warnings\": [],\n    }\n\n\ndef test_build_constraints() -> None:\n    toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / \"build_constraints.toml\").read()\n    assert Factory.validate(toml) == {\"errors\": [], \"warnings\": []}\n\n\ndef test_dependencies_is_consistent_to_poetry_core_schema() -> None:\n    with SCHEMA_FILE.open(encoding=\"utf-8\") as f:\n        schema = json.load(f)\n    dependency_definitions = {\n        key: value for key, value in schema[\"definitions\"].items() if \"depend\" in key\n    }\n    with (files(\"poetry.core\") / \"json\" / \"schemas\" / \"poetry-schema.json\").open(\n        encoding=\"utf-8\"\n    ) as f:\n        core_schema = json.load(f)\n    core_dependency_definitions = {\n        key: value\n        for key, value in core_schema[\"definitions\"].items()\n        if \"depend\" in key\n    }\n    assert dependency_definitions == core_dependency_definitions\n"
  },
  {
    "path": "tests/masonry/builders/__init__.py",
    "content": ""
  },
  {
    "path": "tests/masonry/builders/fixtures/excluded_subpackage/README.rst",
    "content": "My Package\n==========\n"
  },
  {
    "path": "tests/masonry/builders/fixtures/excluded_subpackage/example/__init__.py",
    "content": "from __future__ import annotations\n\n\n__version__ = \"0.1.0\"\n"
  },
  {
    "path": "tests/masonry/builders/fixtures/excluded_subpackage/example/test/__init__.py",
    "content": ""
  },
  {
    "path": "tests/masonry/builders/fixtures/excluded_subpackage/example/test/excluded.py",
    "content": "from __future__ import annotations\n\nfrom tests.masonry.builders.fixtures.excluded_subpackage.example import __version__\n\n\ndef test_version():\n    assert __version__ == \"0.1.0\"\n"
  },
  {
    "path": "tests/masonry/builders/fixtures/excluded_subpackage/pyproject.toml",
    "content": "[tool.poetry]\nname = \"example\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"Sébastien Eustace <sebastien@eustace.io>\"]\nexclude = [\n    \"**/test/**/*\",\n]\n\n[tool.poetry.dependencies]\npython = \"^3.6\"\n\n[tool.poetry.group.dev.dependencies]\npytest = \"^3.0\"\n\n[build-system]\nrequires = [\"poetry>=0.12\"]\nbuild-backend = \"poetry.masonry.api\"\n"
  },
  {
    "path": "tests/masonry/builders/test_editable_builder.py",
    "content": "from __future__ import annotations\n\nimport csv\nimport json\nimport os\nimport shutil\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom deepdiff.diff import DeepDiff\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.masonry.metadata import Metadata\nfrom poetry.core.packages.package import Package\n\nfrom poetry.factory import Factory\nfrom poetry.masonry.builders.editable import EditableBuilder\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.utils._compat import getencoding\nfrom poetry.utils.env import EnvCommandError\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env import VirtualEnv\nfrom poetry.utils.env import ephemeral_environment\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture()\ndef simple_poetry(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"simple_project\"))\n\n    return poetry\n\n\n@pytest.fixture()\ndef project_with_include(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"with-include\"))\n\n    return poetry\n\n\n@pytest.fixture()\ndef extended_poetry(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"extended_project\"))\n\n    return poetry\n\n\n@pytest.fixture()\ndef extended_without_setup_poetry(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"extended_project_without_setup\"))\n\n    return poetry\n\n\n@pytest.fixture\ndef with_multiple_readme_files(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"with_multiple_readme_files\"))\n\n    return poetry\n\n\n@pytest.fixture()\ndef env_manager(simple_poetry: Poetry) -> EnvManager:\n    return EnvManager(simple_poetry)\n\n\n@pytest.fixture\ndef tmp_venv(tmp_path: Path, env_manager: EnvManager) -> Iterator[VirtualEnv]:\n    venv_path = tmp_path / \"venv\"\n\n    env_manager.build_venv(venv_path)\n\n    venv = VirtualEnv(venv_path)\n    yield venv\n\n    shutil.rmtree(str(venv.path))\n\n\n@pytest.fixture()\ndef bad_scripts_no_colon(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"bad_scripts_project/no_colon\"))\n\n    return poetry\n\n\n@pytest.fixture()\ndef bad_scripts_too_many_colon(fixture_dir: FixtureDirGetter) -> Poetry:\n    poetry = Factory().create_poetry(fixture_dir(\"bad_scripts_project/too_many_colon\"))\n\n    return poetry\n\n\ndef expected_metadata_version() -> str:\n    # Get the metadata version that we should expect: which is poetry-core's default\n    # value.\n    metadata = Metadata()\n    return metadata.metadata_version\n\n\ndef expected_python_classifiers(min_version: str | None = None) -> str:\n    if min_version:\n        min_version_tuple = tuple(map(int, min_version.split(\".\")))\n        relevant_versions = set()\n        for version in Package.AVAILABLE_PYTHONS:\n            version_tuple = tuple(map(int, version.split(\".\")))\n            if version_tuple >= min_version_tuple[: len(version_tuple)]:\n                relevant_versions.add(version)\n    else:\n        relevant_versions = Package.AVAILABLE_PYTHONS\n    return \"\\n\".join(\n        f\"Classifier: Programming Language :: Python :: {version}\"\n        for version in sorted(\n            relevant_versions, key=lambda x: tuple(map(int, x.split(\".\")))\n        )\n    )\n\n\n@pytest.mark.parametrize(\"project\", (\"simple_project\", \"simple_project_legacy\"))\ndef test_builder_installs_proper_files_for_standard_packages(\n    project: str,\n    simple_poetry: Poetry,\n    tmp_path: Path,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    simple_poetry = Factory().create_poetry(fixture_dir(project))\n    env_manager = EnvManager(simple_poetry)\n    venv_path = tmp_path / \"venv\"\n    env_manager.build_venv(venv_path)\n    tmp_venv = VirtualEnv(venv_path)\n\n    builder = EditableBuilder(simple_poetry, tmp_venv, NullIO())\n\n    builder.build()\n\n    assert tmp_venv._bin_dir.joinpath(\"foo\").exists()\n    pth_file = Path(\"simple_project.pth\")\n    assert tmp_venv.site_packages.exists(pth_file)\n    assert (\n        simple_poetry.file.path.parent.resolve().as_posix()\n        == tmp_venv.site_packages.find(pth_file)[0]\n        .read_text(encoding=\"utf-8\")\n        .strip(os.linesep)\n    )\n\n    dist_info = Path(\"simple_project-1.2.3.dist-info\")\n    assert tmp_venv.site_packages.exists(dist_info)\n\n    dist_info = tmp_venv.site_packages.find(dist_info)[0]\n\n    assert dist_info.joinpath(\"entry_points.txt\").exists()\n    assert dist_info.joinpath(\"WHEEL\").exists()\n    assert dist_info.joinpath(\"METADATA\").exists()\n    assert dist_info.joinpath(\"licenses/LICENSE\").exists()\n    assert dist_info.joinpath(\"INSTALLER\").exists()\n    assert dist_info.joinpath(\"RECORD\").exists()\n    assert dist_info.joinpath(\"direct_url.json\").exists()\n\n    assert not DeepDiff(\n        {\n            \"dir_info\": {\"editable\": True},\n            \"url\": simple_poetry.file.path.parent.as_uri(),\n        },\n        json.loads(dist_info.joinpath(\"direct_url.json\").read_text(encoding=\"utf-8\")),\n    )\n\n    assert dist_info.joinpath(\"INSTALLER\").read_text(encoding=\"utf-8\") == \"poetry\"\n    assert (\n        dist_info.joinpath(\"entry_points.txt\").read_text(encoding=\"utf-8\")\n        == \"[console_scripts]\\nbaz=bar:baz.boom.bim\\nfoo=foo:bar\\n\"\n        \"fox=fuz.foo:bar.baz\\n\\n\"\n    )\n    metadata = f\"\"\"\\\nMetadata-Version: {expected_metadata_version()}\nName: simple-project\nVersion: 1.2.3\nSummary: Some description.\nLicense: MIT\nLicense-File: LICENSE\nKeywords: packaging,dependency,poetry\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\n{expected_python_classifiers()}\nClassifier: Topic :: Software Development :: Build Tools\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nProject-URL: Documentation, https://python-poetry.org/docs\nProject-URL: Homepage, https://python-poetry.org\nProject-URL: Repository, https://github.com/python-poetry/poetry\nDescription-Content-Type: text/x-rst\n\nMy Package\n==========\n\n\"\"\"\n    if project == \"simple_project\":\n        metadata = metadata.replace(\"License:\", \"License-Expression:\").replace(\n            \"Classifier: License :: OSI Approved :: MIT License\\n\", \"\"\n        )\n    assert dist_info.joinpath(\"METADATA\").read_text(encoding=\"utf-8\") == metadata\n\n    with open(dist_info.joinpath(\"RECORD\"), encoding=\"utf-8\", newline=\"\") as f:\n        reader = csv.reader(f)\n        records = list(reader)\n\n    assert all(len(row) == 3 for row in records)\n    record_entries = {row[0] for row in records}\n    pth_file = Path(\"simple_project.pth\")\n    assert tmp_venv.site_packages.exists(pth_file)\n    assert str(tmp_venv.site_packages.find(pth_file)[0]) in record_entries\n    assert str(tmp_venv._bin_dir.joinpath(\"foo\")) in record_entries\n    assert str(tmp_venv._bin_dir.joinpath(\"baz\")) in record_entries\n    assert str(dist_info.joinpath(\"entry_points.txt\")) in record_entries\n    assert str(dist_info.joinpath(\"WHEEL\")) in record_entries\n    assert str(dist_info.joinpath(\"METADATA\")) in record_entries\n    assert str(dist_info.joinpath(\"licenses/LICENSE\")) in record_entries\n\n    assert str(dist_info.joinpath(\"INSTALLER\")) in record_entries\n    assert str(dist_info.joinpath(\"RECORD\")) in record_entries\n    assert str(dist_info.joinpath(\"direct_url.json\")) in record_entries\n\n    baz_script = f\"\"\"\\\n#!{tmp_venv.python}\nimport sys\nfrom bar import baz\n\nif __name__ == '__main__':\n    sys.exit(baz.boom.bim())\n\"\"\"\n\n    assert tmp_venv._bin_dir.joinpath(\"baz\").read_text(encoding=\"utf-8\") == baz_script\n\n    foo_script = f\"\"\"\\\n#!{tmp_venv.python}\nimport sys\nfrom foo import bar\n\nif __name__ == '__main__':\n    sys.exit(bar())\n\"\"\"\n\n    assert tmp_venv._bin_dir.joinpath(\"foo\").read_text(encoding=\"utf-8\") == foo_script\n\n    fox_script = f\"\"\"\\\n#!{tmp_venv.python}\nimport sys\nfrom fuz.foo import bar\n\nif __name__ == '__main__':\n    sys.exit(bar.baz())\n\"\"\"\n\n    assert tmp_venv._bin_dir.joinpath(\"fox\").read_text(encoding=\"utf-8\") == fox_script\n\n\ndef test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts(\n    mocker: MockerFixture, extended_poetry: Poetry, tmp_path: Path\n) -> None:\n    pip_install = mocker.patch(\"poetry.masonry.builders.editable.pip_install\")\n    env = MockEnv(path=tmp_path / \"foo\")\n    builder = EditableBuilder(extended_poetry, env, NullIO())\n\n    builder.build()\n    pip_install.assert_called_once_with(\n        extended_poetry.pyproject.file.path.parent, env, upgrade=True, editable=True\n    )\n    assert env.executed == []\n\n\n@pytest.mark.network\ndef test_builder_setup_generation_runs_with_pip_editable(\n    fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    # create an isolated copy of the project\n    fixture = fixture_dir(\"extended_project\")\n    extended_project = tmp_path / \"extended_project\"\n\n    shutil.copytree(fixture, extended_project)\n    assert extended_project.exists()\n\n    poetry = Factory().create_poetry(extended_project)\n\n    # we need a venv with pip and setuptools since we are verifying setup.py builds\n    with ephemeral_environment(flags={\"no-pip\": False}) as venv:\n        builder = EditableBuilder(poetry, venv, NullIO())\n        builder.build()\n\n        # is the package installed?\n        repository = InstalledRepository.load(venv)\n        package = repository.package(\"extended-project\", Version.parse(\"1.2.3\"))\n        assert package.name == \"extended-project\"\n\n        # check for the module built by build.py\n        try:\n            output = venv.run_python_script(\n                \"from extended_project import built; print(built.__file__)\"\n            ).strip()\n        except EnvCommandError:\n            pytest.fail(\"Unable to import built module\")\n        else:\n            built_py = Path(output).resolve()\n\n        expected = extended_project / \"extended_project\" / \"built.py\"\n\n        # ensure the package was installed as editable\n        assert built_py == expected.resolve()\n\n\ndef test_builder_installs_proper_files_when_packages_configured(\n    project_with_include: Poetry, tmp_venv: VirtualEnv\n) -> None:\n    builder = EditableBuilder(project_with_include, tmp_venv, NullIO())\n    builder.build()\n\n    pth_file = Path(\"with_include.pth\")\n    assert tmp_venv.site_packages.exists(pth_file)\n\n    pth_file = tmp_venv.site_packages.find(pth_file)[0]\n\n    paths = set()\n    with pth_file.open(encoding=getencoding()) as f:\n        for line in f.readlines():\n            line = line.strip(os.linesep)\n            if line:\n                paths.add(line)\n\n    project_root = project_with_include.file.path.parent.resolve()\n    expected = {project_root.as_posix(), project_root.joinpath(\"src\").as_posix()}\n\n    assert paths.issubset(expected)\n    assert len(paths) == len(expected)\n\n\ndef test_builder_generates_proper_metadata_when_multiple_readme_files(\n    with_multiple_readme_files: Poetry, tmp_venv: VirtualEnv\n) -> None:\n    builder = EditableBuilder(with_multiple_readme_files, tmp_venv, NullIO())\n\n    builder.build()\n\n    dist_info = Path(\"my_package-0.1.dist-info\")\n    assert tmp_venv.site_packages.exists(dist_info)\n\n    dist_info = tmp_venv.site_packages.find(dist_info)[0]\n    assert dist_info.joinpath(\"METADATA\").exists()\n\n    metadata = f\"\"\"\\\nMetadata-Version: {expected_metadata_version()}\nName: my-package\nVersion: 0.1\nSummary: Some description.\nLicense: MIT\nAuthor: Your Name\nAuthor-email: you@example.com\nRequires-Python: >=3.7,<4.0\nClassifier: License :: OSI Approved :: MIT License\n{expected_python_classifiers(\"3.7\")}\nProject-URL: Homepage, https://python-poetry.org\nDescription-Content-Type: text/x-rst\n\nSingle Python\n=============\n\nChangelog\n=========\n\n\"\"\"\n    assert dist_info.joinpath(\"METADATA\").read_text(encoding=\"utf-8\") == metadata\n\n\ndef test_builder_should_execute_build_scripts(\n    mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_path: Path\n) -> None:\n    env = MockEnv(path=tmp_path / \"foo\")\n    mocker.patch(\n        \"poetry.masonry.builders.editable.build_environment\"\n    ).return_value.__enter__.return_value = env\n\n    builder = EditableBuilder(extended_without_setup_poetry, env, NullIO())\n\n    builder.build()\n\n    assert [\n        [\"python\", str(extended_without_setup_poetry.file.path.parent / \"build.py\")]\n    ] == env.executed\n\n\ndef test_builder_catches_bad_scripts_no_colon(\n    bad_scripts_no_colon: Poetry, tmp_venv: VirtualEnv\n) -> None:\n    builder = EditableBuilder(bad_scripts_no_colon, tmp_venv, NullIO())\n    with pytest.raises(ValueError, match=r\"Bad script.*\") as e:\n        builder.build()\n    msg = str(e.value)\n    # We should print out the problematic script entry\n    assert \"bar.bin.foo\" in msg\n    # and some hint about what to do\n    assert \"Hint:\" in msg\n    assert 'foo = \"bar.bin.foo:main\"' in msg\n\n\ndef test_builder_catches_bad_scripts_too_many_colon(\n    bad_scripts_too_many_colon: Poetry, tmp_venv: VirtualEnv\n) -> None:\n    builder = EditableBuilder(bad_scripts_too_many_colon, tmp_venv, NullIO())\n    with pytest.raises(ValueError, match=r\"Bad script.*\") as e:\n        builder.build()\n    msg = str(e.value)\n    # We should print out the problematic script entry\n    assert \"foo::bar\" in msg\n    # and some hint about what is wrong\n    assert \"Too many\" in msg\n"
  },
  {
    "path": "tests/mixology/__init__.py",
    "content": ""
  },
  {
    "path": "tests/mixology/helpers.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.factory import Factory\nfrom poetry.mixology.failure import SolveFailureError\nfrom poetry.mixology.version_solver import VersionSolver\n\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    from packaging.utils import NormalizedName\n    from poetry.core.factory import DependencyConstraint\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.mixology.result import SolverResult\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n\n\ndef add_to_repo(\n    repository: Repository,\n    name: str,\n    version: str,\n    deps: Mapping[str, DependencyConstraint] | None = None,\n    python: str | None = None,\n    yanked: bool = False,\n) -> None:\n    package = Package(name, version, yanked=yanked)\n    if python:\n        package.python_versions = python\n\n    if deps:\n        for dep_name, dep_constraint in deps.items():\n            package.add_dependency(Factory.create_dependency(dep_name, dep_constraint))\n\n    repository.add_package(package)\n\n\ndef check_solver_result(\n    root: ProjectPackage,\n    provider: Provider,\n    result: dict[str, str] | None = None,\n    error: str | None = None,\n    tries: int | None = None,\n    use_latest: list[NormalizedName] | None = None,\n) -> SolverResult | None:\n    solver = VersionSolver(root, provider)\n    with provider.use_latest_for(use_latest or []):\n        try:\n            solution = solver.solve()\n        except SolveFailureError as e:\n            if error:\n                assert str(e) == error\n\n                if tries is not None:\n                    assert solver.solution.attempted_solutions == tries\n\n            return None\n\n        except AssertionError as e:\n            if error:\n                assert str(e) == error\n                return None\n            raise\n\n    packages = {}\n    for package in solution.packages:\n        packages[package.name] = str(package.version)\n\n    assert packages == result\n\n    if tries is not None:\n        assert solution.attempted_solutions == tries\n\n    return solution\n"
  },
  {
    "path": "tests/mixology/test_incompatibility.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.url_dependency import URLDependency\n\nfrom poetry.mixology.incompatibility import Incompatibility\nfrom poetry.mixology.incompatibility_cause import DependencyCauseError\nfrom poetry.mixology.term import Term\n\n\ndef get_url_dependency(name: str, url: str, version: str) -> URLDependency:\n    dependency = URLDependency(name, url)\n    dependency.constraint = version\n    return dependency\n\n\n@pytest.mark.parametrize(\n    (\"dependency1\", \"dependency2\", \"expected\"),\n    [\n        (\n            Dependency(\"foo\", \"1.0\"),\n            Dependency(\"bar\", \"2.0\"),\n            \"foo (1.0) depends on bar (2.0)\",\n        ),\n        (\n            Dependency(\"foo\", \"1.0\"),\n            Dependency(\"bar\", \"^1.0\"),\n            \"foo (1.0) depends on bar (^1.0)\",\n        ),\n        (\n            Dependency(\"foo\", \"1.0\"),\n            get_url_dependency(\"bar\", \"https://example.com/bar.whl\", \"1.1\"),\n            \"foo (1.0) depends on bar (1.1) @ https://example.com/bar.whl\",\n        ),\n        (\n            Dependency(\"foo\", \"1.0\", extras=[\"bar\"]),\n            Dependency(\"foo\", \"1.0\"),\n            \"foo[bar] (1.0) depends on foo (1.0)\",\n        ),\n    ],\n)\ndef test_str_dependency_cause(\n    dependency1: Dependency, dependency2: Dependency, expected: str\n) -> None:\n    incompatibility = Incompatibility(\n        [Term(dependency1, True), Term(dependency2, False)], DependencyCauseError()\n    )\n    assert str(incompatibility) == expected\n"
  },
  {
    "path": "tests/mixology/version_solver/__init__.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\n\npytest.register_assert_rewrite(\"tests.mixology.helpers\")\n"
  },
  {
    "path": "tests/mixology/version_solver/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.puzzle.provider import Provider as BaseProvider\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\n\n\nif TYPE_CHECKING:\n    from tests.helpers import TestRepository\n\n\nclass Provider(BaseProvider):\n    def set_package_python_versions(self, python_versions: str) -> None:\n        self._package.python_versions = python_versions\n        self._python_constraint = self._package.python_constraint\n\n\n@pytest.fixture\ndef repo() -> Repository:\n    return Repository(\"repo\")\n\n\n@pytest.fixture\ndef pool(repo: TestRepository) -> RepositoryPool:\n    pool = RepositoryPool()\n    pool.add_repository(repo)\n\n    return pool\n\n\n@pytest.fixture\ndef root() -> ProjectPackage:\n    return ProjectPackage(\"myapp\", \"0.0.0\")\n\n\n@pytest.fixture\ndef provider(pool: RepositoryPool, root: ProjectPackage) -> Provider:\n    return Provider(root, pool, NullIO())\n"
  },
  {
    "path": "tests/mixology/version_solver/test_backtracking.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.factory import Factory\nfrom tests.mixology.helpers import add_to_repo\nfrom tests.mixology.helpers import check_solver_result\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n\n\ndef test_circular_dependency_on_older_version(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \">=1.0.0\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\")\n    add_to_repo(repo, \"a\", \"2.0.0\", deps={\"b\": \"1.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"a\": \"1.0.0\"})\n\n    check_solver_result(root, provider, {\"a\": \"1.0.0\"}, tries=2)\n\n\ndef test_diamond_dependency_graph(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"2.0.0\", deps={\"c\": \"^1.0.0\"})\n    add_to_repo(repo, \"a\", \"1.0.0\")\n\n    add_to_repo(repo, \"b\", \"2.0.0\", deps={\"c\": \"^3.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"c\": \"^2.0.0\"})\n\n    add_to_repo(repo, \"c\", \"3.0.0\")\n    add_to_repo(repo, \"c\", \"2.0.0\")\n    add_to_repo(repo, \"c\", \"1.0.0\")\n\n    check_solver_result(root, provider, {\"a\": \"1.0.0\", \"b\": \"2.0.0\", \"c\": \"3.0.0\"})\n\n\ndef test_backjumps_after_partial_satisfier(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    # c 2.0.0 is incompatible with y 2.0.0 because it requires x 1.0.0, but that\n    # requirement only exists because of both a and b. The solver should be able\n    # to deduce c 2.0.0's incompatibility and select c 1.0.0 instead.\n    root.add_dependency(Factory.create_dependency(\"c\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"y\", \"^2.0.0\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"x\": \">=1.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"x\": \"<2.0.0\"})\n\n    add_to_repo(repo, \"c\", \"1.0.0\")\n    add_to_repo(repo, \"c\", \"2.0.0\", deps={\"a\": \"*\", \"b\": \"*\"})\n\n    add_to_repo(repo, \"x\", \"0.0.0\")\n    add_to_repo(repo, \"x\", \"1.0.0\", deps={\"y\": \"1.0.0\"})\n    add_to_repo(repo, \"x\", \"2.0.0\")\n\n    add_to_repo(repo, \"y\", \"1.0.0\")\n    add_to_repo(repo, \"y\", \"2.0.0\")\n\n    check_solver_result(root, provider, {\"c\": \"1.0.0\", \"y\": \"2.0.0\"}, tries=4)\n\n\ndef test_rolls_back_leaf_versions_first(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    # The latest versions of a and b disagree on c. An older version of either\n    # will resolve the problem. This test validates that b, which is farther\n    # in the dependency graph from myapp is downgraded first.\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"b\": \"*\"})\n    add_to_repo(repo, \"a\", \"2.0.0\", deps={\"b\": \"*\", \"c\": \"2.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\")\n    add_to_repo(repo, \"b\", \"2.0.0\", deps={\"c\": \"1.0.0\"})\n    add_to_repo(repo, \"c\", \"1.0.0\")\n    add_to_repo(repo, \"c\", \"2.0.0\")\n\n    check_solver_result(root, provider, {\"a\": \"2.0.0\", \"b\": \"1.0.0\", \"c\": \"2.0.0\"})\n\n\ndef test_simple_transitive(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    # Only one version of baz, so foo and bar will have to downgrade\n    # until they reach it\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"2.0.0\", deps={\"bar\": \"2.0.0\"})\n    add_to_repo(repo, \"foo\", \"3.0.0\", deps={\"bar\": \"3.0.0\"})\n\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"baz\": \"*\"})\n    add_to_repo(repo, \"bar\", \"2.0.0\", deps={\"baz\": \"2.0.0\"})\n    add_to_repo(repo, \"bar\", \"3.0.0\", deps={\"baz\": \"3.0.0\"})\n\n    add_to_repo(repo, \"baz\", \"1.0.0\")\n\n    check_solver_result(\n        root, provider, {\"foo\": \"1.0.0\", \"bar\": \"1.0.0\", \"baz\": \"1.0.0\"}, tries=3\n    )\n\n\ndef test_backjump_to_nearer_unsatisfied_package(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    # This ensures it doesn't exhaustively search all versions of b when it's\n    # a-2.0.0 whose dependency on c-2.0.0-nonexistent led to the problem. We\n    # make sure b has more versions than a so that the solver tries a first\n    # since it sorts sibling dependencies by number of versions.\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"c\": \"1.0.0\"})\n    add_to_repo(repo, \"a\", \"2.0.0\", deps={\"c\": \"2.0.0-1\"})\n    add_to_repo(repo, \"b\", \"1.0.0\")\n    add_to_repo(repo, \"b\", \"2.0.0\")\n    add_to_repo(repo, \"b\", \"3.0.0\")\n    add_to_repo(repo, \"c\", \"1.0.0\")\n\n    check_solver_result(\n        root, provider, {\"a\": \"1.0.0\", \"b\": \"3.0.0\", \"c\": \"1.0.0\"}, tries=2\n    )\n\n\ndef test_backjump_past_failed_package_on_disjoint_constraint(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"foo\", \">2.0.0\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"foo\": \"*\"})  # ok\n    add_to_repo(\n        repo, \"a\", \"2.0.0\", deps={\"foo\": \"<1.0.0\"}\n    )  # disjoint with myapp's constraint on foo\n\n    add_to_repo(repo, \"foo\", \"2.0.0\")\n    add_to_repo(repo, \"foo\", \"2.0.1\")\n    add_to_repo(repo, \"foo\", \"2.0.2\")\n    add_to_repo(repo, \"foo\", \"2.0.3\")\n    add_to_repo(repo, \"foo\", \"2.0.4\")\n\n    check_solver_result(root, provider, {\"a\": \"1.0.0\", \"foo\": \"2.0.4\"})\n\n\ndef test_backtracking_performance_level_1(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    \"\"\"\n    This test takes quite long if an unfavorable heuristics is chosen\n    to select the next package to resolve.\n\n    B depends on A, but does not support the latest version of A.\n    B has a lot more versions than A.\n\n    Test for boto3/botocore vs. urllib3 issue in its simple form.\n    \"\"\"\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"1\")\n    add_to_repo(repo, \"a\", \"2\")\n\n    b_max = 500\n    for i in range(1, b_max + 1):\n        add_to_repo(repo, \"b\", str(i), deps={\"a\": \"<=1\"})\n\n    check_solver_result(root, provider, {\"a\": \"1\", \"b\": str(b_max)})\n\n\ndef test_backtracking_performance_level_2(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    \"\"\"\n    Similar to test_backtracking_performance_level_1,\n    but with one more level of dependencies.\n\n    C depends on B depends on A, but B does not support the latest version of A.\n    The root dependency only requires A and C so there is no direct dependency between\n    these two.\n    B and C have a lot more versions than A.\n\n    Test for boto3/botocore vs. urllib3 issue in its more complex form.\n    \"\"\"\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"c\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"1\")\n    add_to_repo(repo, \"a\", \"2\")\n\n    bc_max = 500\n    for i in range(1, bc_max + 1):\n        add_to_repo(repo, \"b\", str(i), deps={\"a\": \"<=1\"})\n    for i in range(1, bc_max + 1):\n        add_to_repo(repo, \"c\", str(i), deps={\"b\": f\"<={i}\"})\n\n    check_solver_result(root, provider, {\"a\": \"1\", \"b\": str(bc_max), \"c\": str(bc_max)})\n"
  },
  {
    "path": "tests/mixology/version_solver/test_basic_graph.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.factory import Factory\nfrom tests.mixology.helpers import add_to_repo\nfrom tests.mixology.helpers import check_solver_result\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n\n\ndef test_simple_dependencies(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \"1.0.0\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"1.0.0\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"aa\": \"1.0.0\", \"ab\": \"1.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"ba\": \"1.0.0\", \"bb\": \"1.0.0\"})\n    add_to_repo(repo, \"aa\", \"1.0.0\")\n    add_to_repo(repo, \"ab\", \"1.0.0\")\n    add_to_repo(repo, \"ba\", \"1.0.0\")\n    add_to_repo(repo, \"bb\", \"1.0.0\")\n\n    check_solver_result(\n        root,\n        provider,\n        {\n            \"a\": \"1.0.0\",\n            \"aa\": \"1.0.0\",\n            \"ab\": \"1.0.0\",\n            \"b\": \"1.0.0\",\n            \"ba\": \"1.0.0\",\n            \"bb\": \"1.0.0\",\n        },\n    )\n\n\ndef test_shared_dependencies_with_overlapping_constraints(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \"1.0.0\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"1.0.0\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"shared\": \">=2.0.0 <4.0.0\"})\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"shared\": \">=3.0.0 <5.0.0\"})\n    add_to_repo(repo, \"shared\", \"2.0.0\")\n    add_to_repo(repo, \"shared\", \"3.0.0\")\n    add_to_repo(repo, \"shared\", \"3.6.9\")\n    add_to_repo(repo, \"shared\", \"4.0.0\")\n    add_to_repo(repo, \"shared\", \"5.0.0\")\n\n    check_solver_result(root, provider, {\"a\": \"1.0.0\", \"b\": \"1.0.0\", \"shared\": \"3.6.9\"})\n\n\ndef test_shared_dependency_where_dependent_version_affects_other_dependencies(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"<=1.0.2\"))\n    root.add_dependency(Factory.create_dependency(\"bar\", \"1.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\")\n    add_to_repo(repo, \"foo\", \"1.0.1\", deps={\"bang\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.2\", deps={\"whoop\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.3\", deps={\"zoop\": \"1.0.0\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"foo\": \"<=1.0.1\"})\n    add_to_repo(repo, \"bang\", \"1.0.0\")\n    add_to_repo(repo, \"whoop\", \"1.0.0\")\n    add_to_repo(repo, \"zoop\", \"1.0.0\")\n\n    check_solver_result(\n        root, provider, {\"foo\": \"1.0.1\", \"bar\": \"1.0.0\", \"bang\": \"1.0.0\"}\n    )\n\n\ndef test_circular_dependency(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"1.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"foo\": \"1.0.0\"})\n\n    check_solver_result(root, provider, {\"foo\": \"1.0.0\", \"bar\": \"1.0.0\"})\n\n\n@pytest.mark.parametrize(\n    \"constraint, versions, yanked_versions, expected\",\n    [\n        (\">=1\", [\"1\", \"2\"], [], \"2\"),\n        (\">=1\", [\"1\", \"2\"], [\"2\"], \"1\"),\n        (\">=1\", [\"1\", \"2\", \"3\"], [\"2\"], \"3\"),\n        (\">=1\", [\"1\", \"2\", \"3\"], [\"2\", \"3\"], \"1\"),\n        (\">1\", [\"1\", \"2\"], [\"2\"], \"error\"),\n        (\">1\", [\"2\"], [\"2\"], \"error\"),\n        (\">=2\", [\"2\"], [\"2\"], \"error\"),\n        (\"==2\", [\"2\"], [\"2\"], \"2\"),\n        (\"==2\", [\"2\", \"2+local\"], [], \"2+local\"),\n        (\"==2\", [\"2\", \"2+local\"], [\"2+local\"], \"2\"),\n    ],\n)\ndef test_yanked_release(\n    root: ProjectPackage,\n    provider: Provider,\n    repo: Repository,\n    constraint: str,\n    versions: list[str],\n    yanked_versions: list[str],\n    expected: str,\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", constraint))\n\n    for version in versions:\n        add_to_repo(repo, \"foo\", version, yanked=version in yanked_versions)\n\n    if expected == \"error\":\n        result = None\n        error = (\n            f\"Because myapp depends on foo ({constraint}) which doesn't match any \"\n            \"versions, version solving failed.\"\n        )\n    else:\n        result = {\"foo\": expected}\n        error = None\n    check_solver_result(root, provider, result, error)\n"
  },
  {
    "path": "tests/mixology/version_solver/test_dependency_cache.py",
    "content": "from __future__ import annotations\n\nfrom copy import deepcopy\nfrom typing import TYPE_CHECKING\nfrom unittest import mock\n\nfrom poetry.factory import Factory\nfrom poetry.mixology.version_solver import DependencyCache\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.mixology.helpers import add_to_repo\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n\n\ndef test_solver_dependency_cache_respects_source_type(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    dependency_pypi = Factory.create_dependency(\"demo\", \">=0.1.0\")\n    dependency_git = Factory.create_dependency(\n        \"demo\", {\"git\": \"https://github.com/demo/demo.git\"}, groups=[\"dev\"]\n    )\n    root.add_dependency(dependency_pypi)\n    root.add_dependency(dependency_git)\n\n    add_to_repo(repo, \"demo\", \"1.0.0\")\n\n    cache = DependencyCache(provider)\n    cache._search_for_cached.cache_clear()\n\n    # ensure cache was never hit for both calls\n    cache.search_for(dependency_pypi, 0)\n    cache.search_for(dependency_git, 0)\n    assert not cache._search_for_cached.cache_info().hits\n\n    # increase test coverage by searching for copies\n    # (when searching for the exact same object, __eq__ is never called)\n    packages_pypi = cache.search_for(deepcopy(dependency_pypi), 0)\n    packages_git = cache.search_for(deepcopy(dependency_git), 0)\n\n    assert cache._search_for_cached.cache_info().hits == 2\n    assert cache._search_for_cached.cache_info().currsize == 2\n\n    assert len(packages_pypi) == len(packages_git) == 1\n    assert packages_pypi != packages_git\n\n    package_pypi = packages_pypi[0]\n    package_git = packages_git[0]\n\n    assert package_pypi.package.name == dependency_pypi.name\n    assert package_pypi.package.version.text == \"1.0.0\"\n\n    assert package_git.package.name == dependency_git.name\n    assert package_git.package.version.text == \"0.1.2\"\n    assert package_git.package.source_type == dependency_git.source_type\n    assert package_git.package.source_url == dependency_git.source_url\n    assert package_git.package.source_resolved_reference == MOCK_DEFAULT_GIT_REVISION\n\n\ndef test_solver_dependency_cache_pulls_from_prior_level_cache(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    dependency_pypi = Factory.create_dependency(\"demo\", \">=0.1.0\")\n    dependency_pypi_constrained = Factory.create_dependency(\"demo\", \">=0.1.0,<2.0.0\")\n    root.add_dependency(dependency_pypi)\n    root.add_dependency(dependency_pypi_constrained)\n    add_to_repo(repo, \"demo\", \"1.0.0\")\n\n    wrapped_provider = mock.Mock(wraps=provider)\n    cache = DependencyCache(wrapped_provider)\n    cache._search_for_cached.cache_clear()\n\n    # On first call, provider.search_for() should be called and the cache\n    # populated.\n    cache.search_for(dependency_pypi, 0)\n    assert len(wrapped_provider.search_for.mock_calls) == 1\n    assert (\"demo\", None, None, None, None) in cache._cache\n    assert (\"demo\", None, None, None, None) in cache._cached_dependencies_by_level[0]\n    assert cache._search_for_cached.cache_info().hits == 0\n    assert cache._search_for_cached.cache_info().misses == 1\n\n    # On second call at level 1, neither provider.search_for() nor\n    # cache._search_for_cached() should have been called again, and the cache\n    # should remain the same.\n    cache.search_for(dependency_pypi, 1)\n    assert len(wrapped_provider.search_for.mock_calls) == 1\n    assert (\"demo\", None, None, None, None) in cache._cache\n    assert (\"demo\", None, None, None, None) in cache._cached_dependencies_by_level[0]\n    assert set(cache._cached_dependencies_by_level.keys()) == {0}\n    assert cache._search_for_cached.cache_info().hits == 1\n    assert cache._search_for_cached.cache_info().misses == 1\n\n    # On third call at level 2 with an updated constraint for the `demo`\n    # package should not call provider.search_for(), but should call\n    # cache._search_for_cached() and update the cache.\n    cache.search_for(dependency_pypi_constrained, 2)\n    assert len(wrapped_provider.search_for.mock_calls) == 1\n    assert (\"demo\", None, None, None, None) in cache._cache\n    assert (\"demo\", None, None, None, None) in cache._cached_dependencies_by_level[0]\n    assert (\"demo\", None, None, None, None) in cache._cached_dependencies_by_level[2]\n    assert set(cache._cached_dependencies_by_level.keys()) == {0, 2}\n    assert cache._search_for_cached.cache_info().hits == 1\n    assert cache._search_for_cached.cache_info().misses == 2\n\n    # Clearing the level 2 and level 1 caches should invalidate the lru_cache\n    # on cache.search_for and wipe out the level 2 cache while preserving the\n    # level 0 cache.\n    cache.clear_level(2)\n    cache.clear_level(1)\n    cache.search_for(dependency_pypi, 0)\n    assert len(wrapped_provider.search_for.mock_calls) == 1\n    assert (\"demo\", None, None, None, None) in cache._cache\n    assert (\"demo\", None, None, None, None) in cache._cached_dependencies_by_level[0]\n    assert set(cache._cached_dependencies_by_level.keys()) == {0}\n    assert cache._search_for_cached.cache_info().hits == 0\n    assert cache._search_for_cached.cache_info().misses == 1\n\n\ndef test_solver_dependency_cache_respects_subdirectories(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    dependency_one = Factory.create_dependency(\n        \"one\",\n        {\n            \"git\": \"https://github.com/demo/subdirectories.git\",\n            \"subdirectory\": \"one\",\n            \"platform\": \"linux\",\n        },\n    )\n    dependency_one_copy = Factory.create_dependency(\n        \"one\",\n        {\n            \"git\": \"https://github.com/demo/subdirectories.git\",\n            \"subdirectory\": \"one-copy\",\n            \"platform\": \"win32\",\n        },\n    )\n\n    root.add_dependency(dependency_one)\n    root.add_dependency(dependency_one_copy)\n\n    cache = DependencyCache(provider)\n    cache._search_for_cached.cache_clear()\n\n    # ensure cache was never hit for both calls\n    cache.search_for(dependency_one, 0)\n    cache.search_for(dependency_one_copy, 0)\n    assert not cache._search_for_cached.cache_info().hits\n\n    # increase test coverage by searching for copies\n    # (when searching for the exact same object, __eq__ is never called)\n    packages_one = cache.search_for(deepcopy(dependency_one), 0)\n    packages_one_copy = cache.search_for(deepcopy(dependency_one_copy), 0)\n\n    assert cache._search_for_cached.cache_info().hits == 2\n    assert cache._search_for_cached.cache_info().currsize == 2\n\n    assert len(packages_one) == len(packages_one_copy) == 1\n\n    package_one = packages_one[0]\n    package_one_copy = packages_one_copy[0]\n\n    assert package_one.package.name == package_one_copy.package.name\n    assert package_one.package.version.text == package_one_copy.package.version.text\n    assert (\n        package_one.package.source_type == package_one_copy.package.source_type == \"git\"\n    )\n    assert (\n        package_one.package.source_resolved_reference\n        == package_one_copy.package.source_resolved_reference\n        == MOCK_DEFAULT_GIT_REVISION\n    )\n    assert (\n        package_one.package.source_subdirectory\n        != package_one_copy.package.source_subdirectory\n    )\n    assert package_one.package.source_subdirectory == \"one\"\n    assert package_one_copy.package.source_subdirectory == \"one-copy\"\n\n    assert package_one.dependency.marker.intersect(\n        package_one_copy.dependency.marker\n    ).is_empty()\n"
  },
  {
    "path": "tests/mixology/version_solver/test_python_constraint.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.factory import Factory\nfrom tests.mixology.helpers import add_to_repo\nfrom tests.mixology.helpers import check_solver_result\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n\n\ndef test_dependency_does_not_match_root_python_constraint(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    provider.set_package_python_versions(\"^3.6\")\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", python=\"<3.5\")\n\n    error = \"\"\"\\\nThe current project's supported Python range (>=3.6,<4.0) is not compatible with some\\\n of the required packages Python requirement:\n  - foo requires Python <3.5, so it will not be installable for Python >=3.6,<4.0\n\nBecause no versions of foo match !=1.0.0\n and foo (1.0.0) requires Python <3.5, foo is forbidden.\nSo, because myapp depends on foo (*), version solving failed.\n\n  <fg=blue;options=bold>* </><fg=default;options=bold>Check your dependencies Python requirement</>: The Python requirement can be specified via the `<fg=default;options=bold>python</>` or `<fg=default;options=bold>markers</>` properties\n\n    For <fg=default;options=bold>foo</>, a possible solution would be to set the `<fg=default;options=bold>python</>` property to <fg=yellow>\"<empty>\"</>\n\n    <fg=blue>https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies</>,\n    <fg=blue>https://python-poetry.org/docs/dependency-specification/#using-environment-markers</>\n\"\"\"\n\n    check_solver_result(root, provider, error=error)\n"
  },
  {
    "path": "tests/mixology/version_solver/test_unsolvable.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.factory import Factory\nfrom poetry.puzzle.provider import IncompatibleConstraintsError\nfrom tests.mixology.helpers import add_to_repo\nfrom tests.mixology.helpers import check_solver_result\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from tests.mixology.version_solver.conftest import Provider\n    from tests.types import FixtureDirGetter\n\n\ndef test_no_version_matching_constraint(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"^1.0\"))\n\n    add_to_repo(repo, \"foo\", \"2.0.0\")\n    add_to_repo(repo, \"foo\", \"2.1.3\")\n\n    check_solver_result(\n        root,\n        provider,\n        error=(\n            \"Because myapp depends on foo (^1.0) \"\n            \"which doesn't match any versions, version solving failed.\"\n        ),\n    )\n\n\ndef test_no_version_that_matches_combined_constraints(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"1.0.0\"))\n    root.add_dependency(Factory.create_dependency(\"bar\", \"1.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"shared\": \">=2.0.0 <3.0.0\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"shared\": \">=2.9.0 <4.0.0\"})\n    add_to_repo(repo, \"shared\", \"2.5.0\")\n    add_to_repo(repo, \"shared\", \"3.5.0\")\n\n    error = \"\"\"\\\nBecause foo (1.0.0) depends on shared (>=2.0.0 <3.0.0)\n and no versions of shared match >=2.9.0,<3.0.0,\\\n foo (1.0.0) requires shared (>=2.0.0,<2.9.0).\nAnd because bar (1.0.0) depends on shared (>=2.9.0 <4.0.0),\\\n bar (1.0.0) is incompatible with foo (1.0.0).\nSo, because myapp depends on both foo (1.0.0) and bar (1.0.0), version solving failed.\\\n\"\"\"\n\n    check_solver_result(root, provider, error=error)\n\n\ndef test_disjoint_constraints(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"1.0.0\"))\n    root.add_dependency(Factory.create_dependency(\"bar\", \"1.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"shared\": \"<=2.0.0\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"shared\": \">3.0.0\"})\n    add_to_repo(repo, \"shared\", \"2.0.0\")\n    add_to_repo(repo, \"shared\", \"4.0.0\")\n\n    error = \"\"\"\\\nBecause foo (1.0.0) depends on shared (<=2.0.0)\n and bar (1.0.0) depends on shared (>3.0.0),\\\n foo (1.0.0) is incompatible with bar (1.0.0).\nSo, because myapp depends on both foo (1.0.0) and bar (1.0.0), version solving failed.\\\n\"\"\"\n\n    check_solver_result(root, provider, error=error)\n    check_solver_result(root, provider, error=error)\n\n\ndef test_disjoint_root_constraints(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"1.0.0\"))\n    root.add_dependency(Factory.create_dependency(\"foo\", \"2.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\")\n    add_to_repo(repo, \"foo\", \"2.0.0\")\n\n    error = \"\"\"\\\nIncompatible constraints in requirements of myapp (0.0.0):\nfoo (==1.0.0)\nfoo (==2.0.0)\"\"\"\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        check_solver_result(root, provider, error=error)\n\n    assert str(e.value) == error\n\n\ndef test_disjoint_root_constraints_path_dependencies(\n    root: ProjectPackage,\n    provider: Provider,\n    repo: Repository,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    provider.set_package_python_versions(\"^3.7\")\n    project_dir = fixture_dir(\"with_conditional_path_deps\")\n    dependency1 = Factory.create_dependency(\"demo\", {\"path\": project_dir / \"demo_one\"})\n    root.add_dependency(dependency1)\n    dependency2 = Factory.create_dependency(\"demo\", {\"path\": project_dir / \"demo_two\"})\n    root.add_dependency(dependency2)\n\n    error = f\"\"\"\\\nIncompatible constraints in requirements of myapp (0.0.0):\ndemo @ {project_dir.as_uri()}/demo_two (1.2.3)\ndemo @ {project_dir.as_uri()}/demo_one (1.2.3)\"\"\"\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        check_solver_result(root, provider, error=error)\n\n    assert str(e.value) == error\n\n\ndef test_no_valid_solution(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"a\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"b\", \"*\"))\n\n    add_to_repo(repo, \"a\", \"1.0.0\", deps={\"b\": \"1.0.0\"})\n    add_to_repo(repo, \"a\", \"2.0.0\", deps={\"b\": \"2.0.0\"})\n\n    add_to_repo(repo, \"b\", \"1.0.0\", deps={\"a\": \"2.0.0\"})\n    add_to_repo(repo, \"b\", \"2.0.0\", deps={\"a\": \"1.0.0\"})\n\n    error = \"\"\"\\\nBecause no versions of b match <1.0.0 || >1.0.0,<2.0.0 || >2.0.0\n and b (1.0.0) depends on a (2.0.0), b (!=2.0.0) requires a (2.0.0).\nAnd because a (2.0.0) depends on b (2.0.0), b is forbidden.\nBecause b (2.0.0) depends on a (1.0.0) which depends on b (1.0.0), b is forbidden.\nThus, b is forbidden.\nSo, because myapp depends on b (*), version solving failed.\"\"\"\n\n    check_solver_result(root, provider, error=error, tries=2)\n\n\ndef test_package_with_the_same_name_gives_clear_error_message(\n    root: ProjectPackage, provider: Provider, repo: Repository\n) -> None:\n    pkg_name = \"a\"\n    root.add_dependency(Factory.create_dependency(pkg_name, \"*\"))\n    add_to_repo(repo, pkg_name, \"1.0.0\", deps={pkg_name: \"1.0.0\"})\n    error = f\"Package '{pkg_name}' is listed as a dependency of itself.\"\n    check_solver_result(root, provider, error=error)\n"
  },
  {
    "path": "tests/mixology/version_solver/test_with_lock.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom cleo.io.null_io import NullIO\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.package import Package\n\nfrom poetry.factory import Factory\nfrom tests.helpers import get_package\nfrom tests.mixology.helpers import add_to_repo\nfrom tests.mixology.helpers import check_solver_result\nfrom tests.mixology.version_solver.conftest import Provider\n\n\nif TYPE_CHECKING:\n    from poetry.core.packages.project_package import ProjectPackage\n\n    from poetry.repositories import Repository\n    from poetry.repositories import RepositoryPool\n\n\ndef test_with_compatible_locked_dependencies(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.1\", deps={\"bar\": \"1.0.1\"})\n    add_to_repo(repo, \"foo\", \"1.0.2\", deps={\"bar\": \"1.0.2\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\")\n    add_to_repo(repo, \"bar\", \"1.0.1\")\n    add_to_repo(repo, \"bar\", \"1.0.2\")\n\n    locked = [get_package(\"foo\", \"1.0.1\"), get_package(\"bar\", \"1.0.1\")]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.1\", \"bar\": \"1.0.1\"},\n    )\n\n\ndef test_with_incompatible_locked_dependencies(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \">1.0.1\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.1\", deps={\"bar\": \"1.0.1\"})\n    add_to_repo(repo, \"foo\", \"1.0.2\", deps={\"bar\": \"1.0.2\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\")\n    add_to_repo(repo, \"bar\", \"1.0.1\")\n    add_to_repo(repo, \"bar\", \"1.0.2\")\n\n    locked = [get_package(\"foo\", \"1.0.1\"), get_package(\"bar\", \"1.0.1\")]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.2\", \"bar\": \"1.0.2\"},\n    )\n\n\ndef test_with_unrelated_locked_dependencies(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.1\", deps={\"bar\": \"1.0.1\"})\n    add_to_repo(repo, \"foo\", \"1.0.2\", deps={\"bar\": \"1.0.2\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\")\n    add_to_repo(repo, \"bar\", \"1.0.1\")\n    add_to_repo(repo, \"bar\", \"1.0.2\")\n    add_to_repo(repo, \"baz\", \"1.0.0\")\n\n    locked = [get_package(\"baz\", \"1.0.1\")]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.2\", \"bar\": \"1.0.2\"},\n    )\n\n\ndef test_unlocks_dependencies_if_necessary_to_ensure_that_a_new_dependency_is_satisfied(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"newdep\", \"2.0.0\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"<2.0.0\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\", deps={\"baz\": \"<2.0.0\"})\n    add_to_repo(repo, \"baz\", \"1.0.0\", deps={\"qux\": \"<2.0.0\"})\n    add_to_repo(repo, \"qux\", \"1.0.0\")\n    add_to_repo(repo, \"foo\", \"2.0.0\", deps={\"bar\": \"<3.0.0\"})\n    add_to_repo(repo, \"bar\", \"2.0.0\", deps={\"baz\": \"<3.0.0\"})\n    add_to_repo(repo, \"baz\", \"2.0.0\", deps={\"qux\": \"<3.0.0\"})\n    add_to_repo(repo, \"qux\", \"2.0.0\")\n    add_to_repo(repo, \"newdep\", \"2.0.0\", deps={\"baz\": \">=1.5.0\"})\n\n    locked = [\n        get_package(\"foo\", \"2.0.0\"),\n        get_package(\"bar\", \"1.0.0\"),\n        get_package(\"baz\", \"1.0.0\"),\n        get_package(\"qux\", \"1.0.0\"),\n    ]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\n            \"foo\": \"2.0.0\",\n            \"bar\": \"2.0.0\",\n            \"baz\": \"2.0.0\",\n            \"qux\": \"1.0.0\",\n            \"newdep\": \"2.0.0\",\n        },\n    )\n\n\ndef test_with_compatible_locked_dependencies_use_latest(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n    root.add_dependency(Factory.create_dependency(\"baz\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1.0.0\", deps={\"bar\": \"1.0.0\"})\n    add_to_repo(repo, \"foo\", \"1.0.1\", deps={\"bar\": \"1.0.1\"})\n    add_to_repo(repo, \"foo\", \"1.0.2\", deps={\"bar\": \"1.0.2\"})\n    add_to_repo(repo, \"bar\", \"1.0.0\")\n    add_to_repo(repo, \"bar\", \"1.0.1\")\n    add_to_repo(repo, \"bar\", \"1.0.2\")\n    add_to_repo(repo, \"baz\", \"1.0.0\")\n    add_to_repo(repo, \"baz\", \"1.0.1\")\n\n    locked = [\n        get_package(\"foo\", \"1.0.1\"),\n        get_package(\"bar\", \"1.0.1\"),\n        get_package(\"baz\", \"1.0.0\"),\n    ]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.2\", \"bar\": \"1.0.2\", \"baz\": \"1.0.0\"},\n        use_latest=[canonicalize_name(\"foo\")],\n    )\n\n\ndef test_with_compatible_locked_dependencies_with_extras(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"^1.0\"))\n\n    package_foo_0 = get_package(\"foo\", \"1.0.0\")\n    package_foo_1 = get_package(\"foo\", \"1.0.1\")\n    bar_extra_dep = Factory.create_dependency(\n        \"bar\", {\"version\": \"^1.0\", \"extras\": \"extra\"}\n    )\n    for package_foo in (package_foo_0, package_foo_1):\n        package_foo.add_dependency(bar_extra_dep)\n        repo.add_package(package_foo)\n\n    bar_deps = {\"baz\": {\"version\": \"^1.0\", \"extras\": [\"extra\"]}}\n    add_to_repo(repo, \"bar\", \"1.0.0\", bar_deps)\n    add_to_repo(repo, \"bar\", \"1.0.1\", bar_deps)\n    add_to_repo(repo, \"baz\", \"1.0.0\")\n    add_to_repo(repo, \"baz\", \"1.0.1\")\n\n    locked = [\n        get_package(\"foo\", \"1.0.0\"),\n        get_package(\"bar\", \"1.0.0\"),\n        get_package(\"baz\", \"1.0.0\"),\n    ]\n    provider = Provider(root, pool, NullIO(), locked=locked)\n\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.0\", \"bar\": \"1.0.0\", \"baz\": \"1.0.0\"},\n    )\n\n\ndef test_with_yanked_package_in_lock(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n    add_to_repo(repo, \"foo\", \"1\")\n    add_to_repo(repo, \"foo\", \"2\", yanked=True)\n\n    # yanked version is kept in lock file\n    locked_foo = get_package(\"foo\", \"2\")\n    assert not locked_foo.yanked\n    provider = Provider(root, pool, NullIO(), locked=[locked_foo])\n    result = check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"2\"},\n    )\n    assert result is not None\n    foo = result.packages[0]\n    assert foo.yanked\n\n    # without considering the lock file, the other version is chosen\n    provider = Provider(root, pool, NullIO())\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1\"},\n    )\n\n\ndef test_no_update_is_respected_for_legacy_repository(\n    root: ProjectPackage, repo: Repository, pool: RepositoryPool\n) -> None:\n    root.add_dependency(Factory.create_dependency(\"foo\", \"^1.0\"))\n\n    foo_100 = Package(\n        \"foo\", \"1.0.0\", source_type=\"legacy\", source_url=\"http://example.com\"\n    )\n    foo_101 = Package(\n        \"foo\", \"1.0.1\", source_type=\"legacy\", source_url=\"http://example.com\"\n    )\n    repo.add_package(foo_100)\n    repo.add_package(foo_101)\n\n    provider = Provider(root, pool, NullIO(), locked=[foo_100])\n    check_solver_result(\n        root,\n        provider,\n        result={\"foo\": \"1.0.0\"},\n    )\n"
  },
  {
    "path": "tests/packages/__init__.py",
    "content": ""
  },
  {
    "path": "tests/packages/test_direct_origin.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom unittest.mock import MagicMock\n\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.packages.direct_origin import DirectOrigin\nfrom poetry.utils.cache import ArtifactCache\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from pytest_mock import MockerFixture\n\n    from tests.types import FixtureDirGetter\n\n\ndef test_direct_origin_get_package_from_file(fixture_dir: FixtureDirGetter) -> None:\n    wheel_path = fixture_dir(\"distributions\") / \"demo-0.1.2-py2.py3-none-any.whl\"\n    package = DirectOrigin.get_package_from_file(wheel_path)\n    assert package.name == \"demo\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.2-py2.py3-none-any.whl\",\n            \"hash\": \"sha256:55dde4e6828081de7a1e429f33180459c333d9da593db62a3d75a8f5e505dde1\",\n            \"size\": 1552,\n        }\n    ]\n\n\ndef test_direct_origin_caches_url_dependency(tmp_path: Path) -> None:\n    artifact_cache = ArtifactCache(cache_dir=tmp_path)\n    direct_origin = DirectOrigin(artifact_cache)\n    url = \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n\n    package = direct_origin.get_package_from_url(url)\n\n    assert package.name == \"demo\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n            \"size\": 1116,\n        }\n    ]\n    assert artifact_cache.get_cached_archive_for_link(Link(url), strict=True)\n\n\ndef test_direct_origin_does_not_download_url_dependency_when_cached(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture\n) -> None:\n    artifact_cache = MagicMock()\n    artifact_cache.get_cached_archive_for_link = MagicMock(\n        return_value=fixture_dir(\"distributions\") / \"demo-0.1.2-py2.py3-none-any.whl\"\n    )\n    direct_origin = DirectOrigin(artifact_cache)\n    url = \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n    download_file = mocker.patch(\n        \"poetry.packages.direct_origin.DirectOrigin._download_file\",\n        side_effect=Exception(\"download_file should not be called\"),\n    )\n\n    package = direct_origin.get_package_from_url(url)\n\n    assert package.name == \"demo\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.2-py2.py3-none-any.whl\",\n            \"hash\": \"sha256:55dde4e6828081de7a1e429f33180459c333d9da593db62a3d75a8f5e505dde1\",\n            \"size\": 1552,\n        }\n    ]\n    artifact_cache.get_cached_archive_for_link.assert_called_once_with(\n        Link(url), strict=True, download_func=download_file\n    )\n"
  },
  {
    "path": "tests/packages/test_locker.py",
    "content": "from __future__ import annotations\n\nimport json\nimport logging\nimport os\nimport re\nimport sys\nimport tempfile\nimport uuid\n\nfrom hashlib import sha256\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Literal\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\nfrom poetry.core.version.markers import AnyMarker\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.__version__ import __version__\nfrom poetry.factory import Factory\nfrom poetry.packages.locker import GENERATED_COMMENT\nfrom poetry.packages.locker import Locker\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\nfrom tests.helpers import get_dependency\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    from pytest import LogCaptureFixture\n    from pytest_mock import MockerFixture\n\n\nDEV_GROUP = canonicalize_name(\"dev\")\n\n\n@pytest.fixture\ndef locker() -> Locker:\n    with tempfile.NamedTemporaryFile() as f:\n        f.close()\n        locker = Locker(Path(f.name), {})\n\n        return locker\n\n\n@pytest.fixture\ndef root() -> ProjectPackage:\n    return ProjectPackage(\"root\", \"1.2.3\")\n\n\n@pytest.fixture\ndef transitive_info() -> TransitivePackageInfo:\n    return TransitivePackageInfo(0, {MAIN_GROUP}, {})\n\n\n@pytest.mark.parametrize(\"is_locked\", [True, False])\ndef test_is_locked(locker: Locker, root: ProjectPackage, is_locked: bool) -> None:\n    if is_locked:\n        locker.set_lock_data(root, {})\n    assert locker.is_locked() is is_locked\n\n\n@pytest.mark.parametrize(\"is_fresh\", [True, False])\ndef test_is_fresh(\n    locker: Locker,\n    root: ProjectPackage,\n    transitive_info: TransitivePackageInfo,\n    is_fresh: bool,\n) -> None:\n    locker.set_lock_data(root, {})\n    if not is_fresh:\n        locker.set_pyproject_data(\n            {\"tool\": {\"poetry\": {\"dependencies\": {\"tomli\": \"*\"}}}}\n        )\n    assert locker.is_fresh() is is_fresh\n\n\n@pytest.mark.parametrize(\n    (\"kind\", \"version\", \"expected\"),\n    [\n        (\"valid\", \"2.3.0\", True),\n        (\"legacy\", \"2.3.0\", False),\n        (\"outdated\", \"2.3.0\", False),\n        (\"valid\", \"2.2.1\", True),\n        (\"legacy\", \"2.2.1\", True),\n        (\"outdated\", \"2.3.0\", False),\n    ],\n)\ndef test_is_fresh_dependency_groups(\n    locker: Locker,\n    root: ProjectPackage,\n    transitive_info: TransitivePackageInfo,\n    kind: Literal[\"valid\", \"legacy\", \"outdated\"],\n    version: str,\n    expected: bool,\n) -> None:\n    locker.set_lock_data(root, {})\n    locker.set_pyproject_data({\"dependency-groups\": {\"foo\": []}})\n    if kind == \"valid\":\n        locked_hash = locker._get_content_hash()\n    elif kind == \"legacy\":\n        locked_hash = locker._get_content_hash(with_dependency_groups=False)\n        assert locked_hash != locker._get_content_hash()\n    else:\n        locked_hash = \"123456\"\n\n    lock_content = locker.lock.read_text(encoding=\"utf-8\")\n    lock_content = re.sub(r\"Poetry [^ ]+\", f\"Poetry {version}\", lock_content)\n    lock_content = re.sub(\n        r'content-hash = \"[^\"]+\"', f'content-hash = \"{locked_hash}\"', lock_content\n    )\n    locker.lock.write_text(lock_content, encoding=\"utf-8\")\n\n    assert locker.is_fresh() is expected\n\n\n@pytest.mark.parametrize(\"lock_version\", [None, \"2.0\", \"2.1\"])\ndef test_is_locked_group_and_markers(\n    locker: Locker, root: ProjectPackage, lock_version: str | None\n) -> None:\n    if lock_version:\n        locker.set_lock_data(root, {})\n        with locker.lock.open(\"r\", encoding=\"utf-8\") as f:\n            content = f.read()\n        content = content.replace(locker._VERSION, lock_version)\n        with locker.lock.open(\"w\", encoding=\"utf-8\") as f:\n            f.write(content)\n    assert locker.is_locked_groups_and_markers() is (lock_version == \"2.1\")\n\n\ndef test_lock_file_data_is_ordered(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n    package_a.files = [{\"file\": \"foo\", \"hash\": \"456\"}, {\"file\": \"bar\", \"hash\": \"123\"}]\n    package_a2 = get_package(\"A\", \"2.0.0\")\n    package_a2.files = [{\"file\": \"baz\", \"hash\": \"345\"}]\n    package_git = Package(\n        \"git-package\",\n        \"1.2.3\",\n        source_type=\"git\",\n        source_url=\"https://github.com/python-poetry/poetry.git\",\n        source_reference=\"develop\",\n        source_resolved_reference=\"123456\",\n    )\n    package_git_with_subdirectory = Package(\n        \"git-package-subdir\",\n        \"1.2.3\",\n        source_type=\"git\",\n        source_url=\"https://github.com/python-poetry/poetry.git\",\n        source_reference=\"develop\",\n        source_resolved_reference=\"123456\",\n        source_subdirectory=\"subdir\",\n    )\n    package_url_linux = Package(\n        \"url-package\",\n        \"1.0\",\n        source_type=\"url\",\n        source_url=\"https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_64.whl\",\n    )\n    package_url_win32 = Package(\n        \"url-package\",\n        \"1.0\",\n        source_type=\"url\",\n        source_url=\"https://example.org/url-package-1.0-cp39-win_amd64.whl\",\n    )\n    packages = {\n        package_a2: transitive_info,\n        package_a: transitive_info,\n        get_package(\"B\", \"1.2\"): transitive_info,\n        package_git: transitive_info,\n        package_git_with_subdirectory: transitive_info,\n        package_url_win32: transitive_info,\n        package_url_linux: transitive_info,\n    }\n\n    locker.set_lock_data(root, packages)\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = [\n    {{file = \"bar\", hash = \"123\"}},\n    {{file = \"foo\", hash = \"456\"}},\n]\n\n[package.dependencies]\nB = \"^1.0\"\n\n[[package]]\nname = \"A\"\nversion = \"2.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = [\n    {{file = \"baz\", hash = \"345\"}},\n]\n\n[[package]]\nname = \"B\"\nversion = \"1.2\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"git-package\"\nversion = \"1.2.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\ndevelop = false\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/python-poetry/poetry.git\"\nreference = \"develop\"\nresolved_reference = \"123456\"\n\n[[package]]\nname = \"git-package-subdir\"\nversion = \"1.2.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\ndevelop = false\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/python-poetry/poetry.git\"\nreference = \"develop\"\nresolved_reference = \"123456\"\nsubdirectory = \"subdir\"\n\n[[package]]\nname = \"url-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"url\"\nurl = \"https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_64.whl\"\n\n[[package]]\nname = \"url-package\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"url\"\nurl = \"https://example.org/url-package-1.0-cp39-win_amd64.whl\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_properly_loads_extras(locker: Locker) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"cachecontrol\"\nversion = \"0.12.5\"\ndescription = \"httplib2 caching for requests\"\noptional = false\npython-versions = \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nmsgpack = \"*\"\nrequests = \"*\"\n\n[package.dependencies.lockfile]\noptional = true\nversion = \">=0.9\"\n\n[package.extras]\nfilecache = [\"lockfile (>=0.9)\"]\nredis = [\"redis (>=2.10.5)\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"~2.7 || ^3.4\"\ncontent-hash = \"c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    packages = locker.locked_repository().packages\n\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert len(package.requires) == 3\n    assert len(package.extras) == 2\n\n    lockfile_dep = package.extras[canonicalize_name(\"filecache\")][0]\n    assert lockfile_dep.name == \"lockfile\"\n\n\ndef test_locker_properly_loads_nested_extras(locker: Locker) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"a\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nb = {{version = \"^1.0\", optional = true, extras = \"c\"}}\n\n[package.extras]\nb = [\"b[c] (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"b\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nc = {{version = \"^1.0\", optional = true}}\n\n[package.extras]\nc = [\"c (>=1.0,<2.0)\"]\n\n[[package]]\nname = \"c\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    repository = locker.locked_repository()\n    assert len(repository.packages) == 3\n\n    packages = repository.find_packages(get_dependency(\"a\", \"1.0\"))\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert len(package.requires) == 1\n    assert len(package.extras) == 1\n\n    dependency_b = package.extras[canonicalize_name(\"b\")][0]\n    assert dependency_b.name == \"b\"\n    assert dependency_b.extras == frozenset({\"c\"})\n\n    packages = repository.find_packages(dependency_b)\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert len(package.requires) == 1\n    assert len(package.extras) == 1\n\n    dependency_c = package.extras[canonicalize_name(\"c\")][0]\n    assert dependency_c.name == \"c\"\n    assert dependency_c.extras == frozenset()\n\n    packages = repository.find_packages(dependency_c)\n    assert len(packages) == 1\n\n\ndef test_locker_properly_loads_extras_legacy(locker: Locker) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"a\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nb = {{version = \"^1.0\", optional = true}}\n\n[package.extras]\nb = [\"b (^1.0)\"]\n\n[[package]]\nname = \"b\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\npython-versions = \"*\"\nlock-version = \"2.1\"\ncontent-hash = \"123456789\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    repository = locker.locked_repository()\n    assert len(repository.packages) == 2\n\n    packages = repository.find_packages(get_dependency(\"a\", \"1.0\"))\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert len(package.requires) == 1\n    assert len(package.extras) == 1\n\n    dependency_b = package.extras[canonicalize_name(\"b\")][0]\n    assert dependency_b.name == \"b\"\n\n\ndef test_locker_properly_loads_subdir(locker: Locker) -> None:\n    content = \"\"\"\\\n[[package]]\nname = \"git-package-subdir\"\nversion = \"1.2.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\nfiles = []\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/python-poetry/poetry.git\"\nreference = \"develop\"\nresolved_reference = \"123456\"\nsubdirectory = \"subdir\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    repository = locker.locked_repository()\n    assert len(repository.packages) == 1\n\n    packages = repository.find_packages(get_dependency(\"git-package-subdir\", \"1.2.3\"))\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert package.source_subdirectory == \"subdir\"\n\n\n@pytest.mark.parametrize(\n    (\"groups\", \"marker\", \"expected\"),\n    [\n        # only main - without marker\n        ([\"main\"], None, {\"main\": \"*\"}),\n        # only main - with marker\n        (\n            [\"main\"],\n            repr('python_version == \"3.9\"'),\n            {\"main\": 'python_version == \"3.9\"'},\n        ),\n        # two groups - common marker\n        (\n            [\"main\", \"dev\"],\n            repr('python_version == \"3.9\"'),\n            {\"main\": 'python_version == \"3.9\"', \"dev\": 'python_version == \"3.9\"'},\n        ),\n        # two groups - separate marker\n        (\n            [\"main\", \"dev\"],\n            (\n                '{\"main\" = \\'python_version == \"3.9\"\\','\n                ' \"dev\" = \\'sys_platform == \"linux\"\\'}'\n            ),\n            {\"main\": 'python_version == \"3.9\"', \"dev\": 'sys_platform == \"linux\"'},\n        ),\n        # two groups - one without marker\n        (\n            [\"main\", \"dev\"],\n            '{\"main\" = \\'python_version == \"3.9\"\\'}',\n            {\"main\": 'python_version == \"3.9\"', \"dev\": \"*\"},\n        ),\n        (\n            # unnormalized group - common marker\n            [\"main\", \"DEV\"],\n            repr('python_version == \"3.9\"'),\n            {\"main\": 'python_version == \"3.9\"', \"dev\": 'python_version == \"3.9\"'},\n        ),\n        (\n            # unnormalized group - separate marker\n            [\"main\", \"DEV\"],\n            (\n                '{\"main\" = \\'python_version == \"3.9\"\\','\n                ' \"DEV\" = \\'sys_platform == \"linux\"\\'}'\n            ),\n            {\"main\": 'python_version == \"3.9\"', \"dev\": 'sys_platform == \"linux\"'},\n        ),\n    ],\n)\ndef test_locker_properly_loads_groups_and_markers(\n    locker: Locker, groups: list[str], marker: str, expected: dict[str, str]\n) -> None:\n    content = rf\"\"\"\n[[package]]\nname = \"a\"\nversion = \"1.0\"\noptional = false\npython-versions = \"*\"\ngroups = {groups}\n{\"markers = \" + marker if marker else \"\"}\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    packages = locker.locked_packages()\n\n    a = get_package(\"a\", \"1.0\")\n    assert len(packages) == 1\n    assert packages[a].groups == {canonicalize_name(g) for g in groups}\n    assert packages[a].markers == {g: parse_marker(m) for g, m in expected.items()}\n\n\ndef test_locker_properly_assigns_metadata_files(locker: Locker) -> None:\n    \"\"\"\n    For multiple constraints dependencies, there is only one common entry in\n    metadata.files. However, we must not assign all the files to each of the packages\n    because this can result in duplicated and outdated entries when running\n    `poetry lock` and hash check failures when running `poetry install`.\n    \"\"\"\n    content = \"\"\"\\\n[[package]]\nname = \"demo\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\n\n[[package]]\nname = \"demo\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/demo/demo.git\"\nreference = \"main\"\nresolved_reference = \"123456\"\n\n[[package]]\nname = \"demo\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\n\n[package.source]\ntype = \"directory\"\nurl = \"./folder\"\n\n[[package]]\nname = \"demo\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\n\n[package.source]\ntype = \"file\"\nurl = \"./demo-1.0-cp39-win_amd64.whl\"\n\n[[package]]\nname = \"demo\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\ndevelop = false\n\n[package.source]\ntype = \"url\"\nurl = \"https://example.com/demo-1.0-cp38-win_amd64.whl\"\n\n[metadata]\nlock-version = \"1.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\n[metadata.files]\n# metadata.files are only tracked for non-direct origin and file dependencies\ndemo = [\n    {file = \"demo-1.0-cp39-win_amd64.whl\", hash = \"sha256\"},\n    {file = \"demo-1.0.tar.gz\", hash = \"sha256\"},\n    {file = \"demo-1.0-py3-none-any.whl\", hash = \"sha256\"},\n]\n\"\"\"\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    repository = locker.locked_repository()\n    assert len(repository.packages) == 5\n    assert {package.source_type for package in repository.packages} == {\n        None,\n        \"git\",\n        \"directory\",\n        \"file\",\n        \"url\",\n    }\n    for package in repository.packages:\n        if package.source_type is None:\n            # non-direct origin package contains all files\n            # with the current lockfile format we have no chance to determine\n            # which files are correct, so we keep all for hash check\n            # correct files are set later in Provider.complete_package()\n            assert package.files == [\n                {\"file\": \"demo-1.0-cp39-win_amd64.whl\", \"hash\": \"sha256\"},\n                {\"file\": \"demo-1.0.tar.gz\", \"hash\": \"sha256\"},\n                {\"file\": \"demo-1.0-py3-none-any.whl\", \"hash\": \"sha256\"},\n            ]\n        elif package.source_type == \"file\":\n            assert package.files == [\n                {\"file\": \"demo-1.0-cp39-win_amd64.whl\", \"hash\": \"sha256\"}\n            ]\n        else:\n            package.files = []\n\n\ndef test_locker_dumps_packages_with_null_description(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.description = None  # type: ignore[assignment]\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_does_not_dump_file_urls(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.files = [\n        {\n            \"file\": \"a-1.0.whl\",\n            \"hash\": \"sha256:abcdef1234567890\",\n            \"url\": \"https://example.org/a-1.0.whl\",\n        },\n    ]\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = [\n    {{file = \"a-1.0.whl\", hash = \"sha256:abcdef1234567890\"}},\n]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_lock_file_should_not_have_mixed_types(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0.0\"))\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \">=1.0.0\", \"optional\": True})\n    )\n    package_a.requires[-1].activate()\n    package_a.extras = {canonicalize_name(\"foo\"): [get_dependency(\"B\", \">=1.0.0\")]}\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = [\n    {{version = \"^1.0.0\"}},\n    {{version = \">=1.0.0\", optional = true}},\n]\n\n[package.extras]\nfoo = [\"B (>=1.0.0)\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    assert content == expected\n\n\ndef test_reading_lock_file_should_raise_an_error_on_invalid_data(\n    locker: Locker,\n) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nfoo = [\"bar\"]\n\n[package.extras]\nfoo = [\"bar\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n    with locker.lock.open(\"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    with pytest.raises(RuntimeError) as e:\n        _ = locker.lock_data\n\n    assert \"Unable to read the lock file\" in str(e.value)\n\n\ndef test_reading_lock_file_should_raise_an_error_on_missing_metadata(\n    locker: Locker,\n) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"legacy\"\nurl = \"https://foo.bar\"\nreference = \"legacy\"\n\"\"\"\n    with locker.lock.open(\"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    with pytest.raises(RuntimeError) as e:\n        _ = locker.lock_data\n\n    assert (\n        \"The lock file does not have a metadata entry.\\nRegenerate the lock file with\"\n        \" the `poetry lock` command.\" in str(e.value)\n    )\n\n\ndef test_locking_legacy_repository_package_should_include_source_section(\n    root: ProjectPackage, locker: Locker, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = Package(\n        \"A\",\n        \"1.0.0\",\n        source_type=\"legacy\",\n        source_url=\"https://foo.bar\",\n        source_reference=\"legacy\",\n    )\n    packages = {package_a: transitive_info}\n\n    locker.set_lock_data(root, packages)\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"legacy\"\nurl = \"https://foo.bar\"\nreference = \"legacy\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_should_emit_warnings_if_lock_version_is_newer_but_allowed(\n    locker: Locker, caplog: LogCaptureFixture\n) -> None:\n    version = \".\".join(Version.parse(Locker._VERSION).next_minor().text.split(\".\")[:2])\n    content = f\"\"\"\\\n[metadata]\nlock-version = \"{version}\"\npython-versions = \"~2.7 || ^3.4\"\ncontent-hash = \"c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77\"\n\"\"\"\n    caplog.set_level(logging.WARNING, logger=\"poetry.packages.locker\")\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    _ = locker.lock_data\n\n    assert len(caplog.records) == 1\n\n    record = caplog.records[0]\n    assert record.levelname == \"WARNING\"\n\n    expected = \"\"\"\\\nThe lock file might not be compatible with the current version of Poetry.\nUpgrade Poetry to ensure the lock file is read properly or, alternatively, \\\nregenerate the lock file with the `poetry lock` command.\\\n\"\"\"\n    assert record.message == expected\n\n\ndef test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed(\n    locker: Locker, caplog: LogCaptureFixture\n) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[metadata]\nlock-version = \"3.0\"\npython-versions = \"~2.7 || ^3.4\"\ncontent-hash = \"c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77\"\n\"\"\"\n    caplog.set_level(logging.WARNING, logger=\"poetry.packages.locker\")\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    with pytest.raises(RuntimeError, match=r\"^The lock file is not compatible\"):\n        _ = locker.lock_data\n\n\ndef test_locker_should_raise_an_error_if_no_lock_version(\n    locker: Locker, caplog: LogCaptureFixture\n) -> None:\n    \"\"\"Lock file prior Poetry 1.1 have no lock file version.\"\"\"\n    content = \"\"\"\\\n[metadata]\npython-versions = \"~2.7 || ^3.4\"\ncontent-hash = \"c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77\"\n\"\"\"\n    caplog.set_level(logging.WARNING, logger=\"poetry.packages.locker\")\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    with pytest.raises(RuntimeError, match=r\"^The lock file is not compatible\"):\n        _ = locker.lock_data\n\n\ndef test_root_extras_dependencies_are_ordered(\n    locker: Locker, root: ProjectPackage, fixture_base: Path\n) -> None:\n    Factory.create_dependency(\"B\", \"1.0.0\", root_dir=fixture_base)\n    Factory.create_dependency(\"C\", \"1.0.0\", root_dir=fixture_base)\n    package_first = Factory.create_dependency(\"first\", \"1.0.0\", root_dir=fixture_base)\n    package_second = Factory.create_dependency(\"second\", \"1.0.0\", root_dir=fixture_base)\n    package_third = Factory.create_dependency(\"third\", \"1.0.0\", root_dir=fixture_base)\n\n    root.extras = {\n        canonicalize_name(\"C\"): [package_third, package_second, package_first],\n        canonicalize_name(\"B\"): [package_first, package_second, package_third],\n    }\n    locker.set_lock_data(root, {})\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\npackage = []\n\n[extras]\nb = [\"first\", \"second\", \"third\"]\nc = [\"first\", \"second\", \"third\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    assert content == expected\n\n\ndef test_extras_dependencies_are_ordered(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"^1.0.0\", \"optional\": True, \"extras\": [\"c\", \"a\", \"b\"]}\n        )\n    )\n    package_a.requires[-1].activate()\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = {{version = \"^1.0.0\", extras = [\"a\", \"b\", \"c\"], optional = true}}\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    assert content == expected\n\n\ndef test_locker_should_neither_emit_warnings_nor_raise_error_for_lower_compatible_versions(\n    locker: Locker, caplog: LogCaptureFixture\n) -> None:\n    older_version = \"1.1\"\n    content = f\"\"\"\\\n[metadata]\nlock-version = \"{older_version}\"\npython-versions = \"~2.7 || ^3.4\"\ncontent-hash = \"c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77\"\n\n[metadata.files]\n\"\"\"\n    caplog.set_level(logging.WARNING, logger=\"poetry.packages.locker\")\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    _ = locker.lock_data\n\n    assert len(caplog.records) == 0\n\n\ndef test_locker_dumps_groups_and_markers(\n    locker: Locker,\n    root: ProjectPackage,\n    fixture_base: Path,\n    transitive_info: TransitivePackageInfo,\n) -> None:\n    packages = {\n        get_package(\"A\", \"1.0\"): TransitivePackageInfo(\n            0, {MAIN_GROUP}, {MAIN_GROUP: AnyMarker()}\n        ),\n        get_package(\"B\", \"1.0\"): TransitivePackageInfo(\n            0, {MAIN_GROUP}, {MAIN_GROUP: parse_marker('sys_platform == \"win32\"')}\n        ),\n        get_package(\"C\", \"1.0\"): TransitivePackageInfo(\n            0,\n            {MAIN_GROUP, DEV_GROUP},\n            {MAIN_GROUP: AnyMarker(), DEV_GROUP: AnyMarker()},\n        ),\n        get_package(\"D\", \"1.0\"): TransitivePackageInfo(\n            0,\n            {MAIN_GROUP, DEV_GROUP},\n            {\n                MAIN_GROUP: parse_marker('sys_platform == \"win32\"'),\n                DEV_GROUP: parse_marker('sys_platform == \"win32\"'),\n            },\n        ),\n        get_package(\"E\", \"1.0\"): TransitivePackageInfo(\n            0,\n            {MAIN_GROUP, DEV_GROUP},\n            {\n                MAIN_GROUP: parse_marker('sys_platform == \"win32\"'),\n                DEV_GROUP: parse_marker('sys_platform == \"linux\"'),\n            },\n        ),\n        get_package(\"F\", \"1.0\"): TransitivePackageInfo(\n            0,\n            {MAIN_GROUP, DEV_GROUP},\n            {\n                MAIN_GROUP: parse_marker('sys_platform == \"win32\"'),\n                DEV_GROUP: AnyMarker(),\n            },\n        ),\n    }\n\n    locker.set_lock_data(root, packages)\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[[package]]\nname = \"B\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nmarkers = \"sys_platform == \\\\\"win32\\\\\"\"\nfiles = []\n\n[[package]]\nname = \"C\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\", \"dev\"]\nfiles = []\n\n[[package]]\nname = \"D\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\", \"dev\"]\nmarkers = \"sys_platform == \\\\\"win32\\\\\"\"\nfiles = []\n\n[[package]]\nname = \"E\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\", \"dev\"]\nfiles = []\nmarkers = {{main = \"sys_platform == \\\\\"win32\\\\\"\", dev = \"sys_platform == \\\\\"linux\\\\\"\"}}\n\n[[package]]\nname = \"F\"\nversion = \"1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\", \"dev\"]\nfiles = []\nmarkers = {{main = \"sys_platform == \\\\\"win32\\\\\"\"}}\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_dumps_dependency_information_correctly(\n    locker: Locker,\n    root: ProjectPackage,\n    fixture_base: Path,\n    transitive_info: TransitivePackageInfo,\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"path\": \"project_with_extras\", \"develop\": True}, root_dir=fixture_base\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"C\",\n            {\"path\": \"directory/project_with_transitive_directory_dependencies\"},\n            root_dir=fixture_base,\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"D\", {\"path\": \"distributions/demo-0.1.0.tar.gz\"}, root_dir=fixture_base\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"E\", {\"url\": \"https://files.pythonhosted.org/poetry-1.2.0.tar.gz\"}\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"F\", {\"git\": \"https://github.com/python-poetry/poetry.git\", \"branch\": \"foo\"}\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"G\",\n            {\n                \"git\": \"https://github.com/python-poetry/poetry.git\",\n                \"subdirectory\": \"bar\",\n            },\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"H\", {\"git\": \"https://github.com/python-poetry/poetry.git\", \"tag\": \"baz\"}\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"I\", {\"git\": \"https://github.com/python-poetry/poetry.git\", \"rev\": \"spam\"}\n        )\n    )\n\n    packages = {package_a: transitive_info}\n\n    locker.set_lock_data(root, packages)\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = {{path = \"project_with_extras\", develop = true}}\nC = {{path = \"directory/project_with_transitive_directory_dependencies\"}}\nD = {{path = \"distributions/demo-0.1.0.tar.gz\"}}\nE = {{url = \"https://files.pythonhosted.org/poetry-1.2.0.tar.gz\"}}\nF = {{git = \"https://github.com/python-poetry/poetry.git\", branch = \"foo\"}}\nG = {{git = \"https://github.com/python-poetry/poetry.git\", subdirectory = \"bar\"}}\nH = {{git = \"https://github.com/python-poetry/poetry.git\", tag = \"baz\"}}\nI = {{git = \"https://github.com/python-poetry/poetry.git\", rev = \"spam\"}}\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_dumps_subdir(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    package_git_with_subdirectory = Package(\n        \"git-package-subdir\",\n        \"1.2.3\",\n        source_type=\"git\",\n        source_url=\"https://github.com/python-poetry/poetry.git\",\n        source_reference=\"develop\",\n        source_resolved_reference=\"123456\",\n        source_subdirectory=\"subdir\",\n    )\n\n    locker.set_lock_data(root, {package_git_with_subdirectory: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"git-package-subdir\"\nversion = \"1.2.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\ndevelop = false\n\n[package.source]\ntype = \"git\"\nurl = \"https://github.com/python-poetry/poetry.git\"\nreference = \"develop\"\nresolved_reference = \"123456\"\nsubdirectory = \"subdir\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_dumps_dependency_extras_in_correct_order(\n    locker: Locker,\n    root: ProjectPackage,\n    fixture_base: Path,\n    transitive_info: TransitivePackageInfo,\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n    Factory.create_dependency(\"B\", \"1.0.0\", root_dir=fixture_base)\n    Factory.create_dependency(\"C\", \"1.0.0\", root_dir=fixture_base)\n    package_first = Factory.create_dependency(\"first\", \"1.0.0\", root_dir=fixture_base)\n    package_second = Factory.create_dependency(\"second\", \"1.0.0\", root_dir=fixture_base)\n    package_third = Factory.create_dependency(\"third\", \"1.0.0\", root_dir=fixture_base)\n\n    package_a.extras = {\n        canonicalize_name(\"C\"): [package_third, package_second, package_first],\n        canonicalize_name(\"B\"): [package_first, package_second, package_third],\n    }\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nb = [\"first (==1.0.0)\", \"second (==1.0.0)\", \"third (==1.0.0)\"]\nc = [\"first (==1.0.0)\", \"second (==1.0.0)\", \"third (==1.0.0)\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_dumps_extras_with_constraints(\n    locker: Locker,\n    root: ProjectPackage,\n    fixture_base: Path,\n    transitive_info: TransitivePackageInfo,\n) -> None:\n    package_a = get_package(\"A\", \"1.0.0\")\n\n    package_httpx_new = Factory.create_dependency(\n        \"httpx\",\n        {\"version\": \"2.0.0\", \"python\": \">=3.7\", \"extras\": [\"brotli\"]},\n        root_dir=fixture_base,\n    )\n\n    package_httpx_old = Factory.create_dependency(\n        \"httpx\", {\"version\": \"1.0.0\", \"python\": \"<3.7\"}, root_dir=fixture_base\n    )\n\n    package_a.extras = {\n        canonicalize_name(\"http\"): [package_httpx_new, package_httpx_old],\n    }\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nhttp = [\"httpx (==1.0.0) ; python_version < \\\\\"3.7\\\\\"\", \"httpx[brotli] (==2.0.0) ; python_version >= \\\\\"3.7\\\\\"\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    assert content == expected\n\n\ndef test_locker_properly_loads_extras_with_constraints(locker: Locker) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.extras]\nhttp = [\"httpx (==1.0.0) ; python_version < \\\\\"3.7\\\\\"\", \"httpx[brotli] (==2.0.0) ; python_version >= \\\\\"3.7\\\\\"\"]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    packages = locker.locked_repository().packages\n\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert len(package.extras) == 1\n\n    httpx_deps = package.extras[canonicalize_name(\"http\")]\n    assert len(httpx_deps) == 2\n\n    assert httpx_deps[0].constraint == Version.parse(\"1.0.0\")\n    assert str(httpx_deps[0].python_constraint) == \"<3.7\"\n\n    assert httpx_deps[1].constraint == Version.parse(\"2.0.0\")\n    assert str(httpx_deps[1].python_constraint) == \">=3.7\"\n    assert httpx_deps[1].extras == {\"brotli\"}\n\n\ndef test_locked_repository_uses_root_dir_of_package(\n    locker: Locker, mocker: MockerFixture\n) -> None:\n    content = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"lib-a\"\nversion = \"0.1.0\"\ndescription = \"\"\noptional = false\npython-versions = \"^2.7.9\"\ngroups = [\"main\"]\ndevelop = true\nfile = []\n\n[package.dependencies]\nlib-b = {{path = \"../libB\", develop = true}}\n\n[package.source]\ntype = \"directory\"\nurl = \"lib/libA\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n\n    create_dependency_patch = mocker.patch(\n        \"poetry.factory.Factory.create_dependency\", autospec=True\n    )\n    locker.locked_repository()\n\n    create_dependency_patch.assert_called_once_with(\n        \"lib-b\", {\"develop\": True, \"path\": \"../libB\"}, root_dir=mocker.ANY\n    )\n    call_kwargs = create_dependency_patch.call_args[1]\n    root_dir = call_kwargs[\"root_dir\"]\n    assert root_dir.match(\"*/lib/libA\")\n    # relative_to raises an exception if not relative - is_relative_to comes in py3.9\n    assert root_dir.relative_to(locker.lock.parent.resolve()) is not None\n\n\n@pytest.mark.parametrize(\n    (\"local_config\", \"legacy\"),\n    [\n        ({}, True),\n        ({\"dependencies\": [uuid.uuid4().hex]}, True),\n        ({\"dependencies\": [uuid.uuid4().hex], \"source\": [uuid.uuid4().hex]}, True),\n        ({\"dependencies\": [uuid.uuid4().hex], \"extras\": [uuid.uuid4().hex]}, True),\n        (\n            {\n                \"dependencies\": [uuid.uuid4().hex],\n                \"dev-dependencies\": [uuid.uuid4().hex],\n            },\n            True,\n        ),\n        (\n            {\n                \"dependencies\": [uuid.uuid4().hex],\n                \"dev-dependencies\": None,\n            },\n            True,\n        ),\n        ({\"dependencies\": [uuid.uuid4().hex], \"group\": [uuid.uuid4().hex]}, False),\n    ],\n)\ndef test_content_hash_with_legacy_is_compatible(\n    local_config: dict[str, list[str]], legacy: bool, locker: Locker\n) -> None:\n    \"\"\"Legacy generation if there is no group.\"\"\"\n\n    def _get_legacy_content_hash() -> str:\n        relevant_content = {}\n        for key in locker._legacy_keys:\n            relevant_content[key] = local_config.get(key)\n\n        content_hash = sha256(\n            json.dumps(relevant_content, sort_keys=True).encode()\n        ).hexdigest()\n\n        return content_hash\n\n    locker = locker.__class__(\n        lock=locker.lock,\n        pyproject_data={\"tool\": {\"poetry\": local_config}},\n    )\n\n    old_content_hash = _get_legacy_content_hash()\n    content_hash = locker._get_content_hash()\n\n    if legacy:\n        assert content_hash == old_content_hash\n    else:\n        assert content_hash != old_content_hash\n\n\n@pytest.mark.parametrize(\n    (\"project\", \"legacy\"),\n    [\n        ({\"name\": \"foo\"}, True),  # irrelevant key\n        ({\"requires-python\": \">=3.9\"}, False),\n        ({\"dependencies\": [\"bar\"]}, False),\n        ({\"dependencies\": []}, False),  # relevant even if empty\n        ({\"optional-dependencies\": \"...\"}, False),\n    ],\n)\n@pytest.mark.parametrize(\n    \"local_config\",\n    [\n        {},  # empty\n        {\"dependencies\": [uuid.uuid4().hex]},  # only legacy\n        {\n            \"dependencies\": [uuid.uuid4().hex],\n            \"group\": [uuid.uuid4().hex],\n        },  # legacy and new\n    ],\n)\ndef test_content_hash_with_project_section(\n    project: dict[str, Any],\n    local_config: dict[str, list[str]],\n    legacy: bool,\n    locker: Locker,\n) -> None:\n    \"\"\"Legacy generation if there is no project section.\"\"\"\n\n    def _get_legacy_content_hash() -> str:\n        relevant_content = {}\n        for key in locker._relevant_keys:\n            data = local_config.get(key)\n\n            if data is None and key not in locker._legacy_keys:\n                continue\n\n            relevant_content[key] = data\n\n        return sha256(json.dumps(relevant_content, sort_keys=True).encode()).hexdigest()\n\n    locker = locker.__class__(\n        lock=locker.lock,\n        pyproject_data={\"project\": project, \"tool\": {\"poetry\": local_config}},\n    )\n\n    old_content_hash = _get_legacy_content_hash()\n    content_hash = locker._get_content_hash()\n\n    if legacy:\n        assert content_hash == old_content_hash\n    else:\n        assert content_hash != old_content_hash\n\n\n@pytest.mark.parametrize(\n    (\"groups\", \"legacy\"),\n    [\n        ({}, True),\n        ({\"foo\": []}, False),\n    ],\n)\n@pytest.mark.parametrize(\n    \"project\",\n    [\n        {\"name\": \"foo\"},  # irrelevant key\n        {\"requires-python\": \">=3.9\"},  # relevant key\n    ],\n)\n@pytest.mark.parametrize(\n    \"local_config\",\n    [\n        {},  # empty\n        {\"dependencies\": [uuid.uuid4().hex]},  # only legacy\n        {\n            \"dependencies\": [uuid.uuid4().hex],\n            \"group\": [uuid.uuid4().hex],\n        },  # legacy and new\n    ],\n)\ndef test_content_hash_with_dependency_groups_section(\n    groups: dict[str, Any],\n    project: dict[str, Any],\n    local_config: dict[str, Any],\n    legacy: bool,\n    locker: Locker,\n) -> None:\n    \"\"\"Legacy generation if there is no dependency-groups section.\"\"\"\n\n    def _get_legacy_content_hash() -> str:\n        project_content = project\n        tool_poetry_content = local_config\n\n        relevant_project_content = {}\n        for key in locker._relevant_project_keys:\n            data = project_content.get(key)\n            if data is not None:\n                relevant_project_content[key] = data\n\n        relevant_poetry_content: dict[str, Any] = {}\n        for key in locker._relevant_keys:\n            data = tool_poetry_content.get(key)\n\n            if data is None and (\n                # Special handling for legacy keys is just for backwards compatibility,\n                # and thereby not required if there is relevant content in [project].\n                key not in locker._legacy_keys or relevant_project_content\n            ):\n                continue\n\n            relevant_poetry_content[key] = data\n\n        if relevant_project_content:\n            relevant_content = {\n                \"project\": relevant_project_content,\n                \"tool\": {\"poetry\": relevant_poetry_content},\n            }\n        else:\n            # For backwards compatibility, we have to put the relevant content\n            # of the [tool.poetry] section at top level!\n            relevant_content = relevant_poetry_content\n\n        return sha256(json.dumps(relevant_content, sort_keys=True).encode()).hexdigest()\n\n    locker = locker.__class__(\n        lock=locker.lock,\n        pyproject_data={\n            \"project\": project,\n            \"dependency-groups\": groups,\n            \"tool\": {\"poetry\": local_config},\n        },\n    )\n\n    old_content_hash = _get_legacy_content_hash()\n    content_hash = locker._get_content_hash()\n\n    if legacy:\n        assert content_hash == old_content_hash\n    else:\n        assert content_hash != old_content_hash\n\n\ndef test_lock_file_resolves_file_url_symlinks(\n    root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    \"\"\"\n    Create directories and file structure as follows:\n\n    d1/\n    d1/testsymlink -> d1/d2/d3\n    d1/d2/d3/lock_file\n    d1/d4/source_file\n\n    Using the testsymlink as the Locker.lock file path should correctly resolve to\n    the real physical path of the source_file when calculating the relative path\n    from the lock_file, i.e. \"../../d4/source_file\" instead of the unresolved path\n    from the symlink itself which would have been \"../d4/source_file\"\n\n    See https://github.com/python-poetry/poetry/issues/5849\n    \"\"\"\n    with tempfile.TemporaryDirectory() as d1:\n        symlink_path = Path(d1).joinpath(\"testsymlink\")\n        with (\n            tempfile.TemporaryDirectory(dir=d1) as d2,\n            tempfile.TemporaryDirectory(dir=d1) as d4,\n            tempfile.TemporaryDirectory(dir=d2) as d3,\n            tempfile.NamedTemporaryFile(dir=d4) as source_file,\n            tempfile.NamedTemporaryFile(dir=d3) as lock_file,\n        ):\n            lock_file.close()\n            try:\n                os.symlink(Path(d3), symlink_path)\n            except OSError:\n                if sys.platform == \"win32\":\n                    # os.symlink requires either administrative privileges or developer\n                    # mode on Win10, throwing an OSError if neither is active.\n                    # Test is not possible in that case.\n                    return\n                raise\n            locker = Locker(symlink_path / lock_file.name, {})\n\n            package_local = Package(\n                \"local-package\",\n                \"1.2.3\",\n                source_type=\"file\",\n                source_url=source_file.name,\n                source_reference=\"develop\",\n                source_resolved_reference=\"123456\",\n            )\n            packages = {\n                package_local: transitive_info,\n            }\n\n            locker.set_lock_data(root, packages)\n\n            with locker.lock.open(encoding=\"utf-8\") as f:\n                content = f.read()\n\n            expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"local-package\"\nversion = \"1.2.3\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.source]\ntype = \"file\"\nurl = \"{\n                Path(\n                    os.path.relpath(\n                        Path(source_file.name).resolve().as_posix(),\n                        Path(Path(lock_file.name).parent).resolve().as_posix(),\n                    )\n                ).as_posix()\n            }\"\nreference = \"develop\"\nresolved_reference = \"123456\"\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n            assert content == expected\n\n\ndef test_lockfile_is_not_rewritten_if_only_poetry_version_changed(\n    locker: Locker, root: ProjectPackage\n) -> None:\n    generated_comment_old_version = GENERATED_COMMENT.replace(__version__, \"1.3.2\")\n    assert generated_comment_old_version != GENERATED_COMMENT\n    old_content = f\"\"\"\\\n# {generated_comment_old_version}\npackage = []\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with open(locker.lock, \"w\", encoding=\"utf-8\") as f:\n        f.write(old_content)\n\n    assert not locker.set_lock_data(root, {})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    assert content == old_content\n\n\ndef test_lockfile_keep_eol(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    sep = \"\\n\" if os.linesep == \"\\r\\n\" else \"\\r\\n\"\n\n    with open(locker.lock, \"wb\") as f:\n        f.write((sep * 10).encode())\n\n    packages = {Package(\"test\", version=\"0.0.1\"): transitive_info}\n\n    assert locker.set_lock_data(root, packages)\n\n    with locker.lock.open(encoding=\"utf-8\", newline=\"\") as f:\n        line, *_ = f.read().splitlines(keepends=True)\n\n    if sep == \"\\r\\n\":\n        assert line.endswith(\"\\r\\n\")\n    else:\n        assert not line.endswith(\"\\r\\n\")\n\n\ndef test_lock_file_dependency_constraints_are_ordered_deterministically(\n    locker: Locker, root: ProjectPackage, transitive_info: TransitivePackageInfo\n) -> None:\n    \"\"\"Dependency constraints for the same package should be sorted by\n    name and marker to ensure deterministic lock file output\n    regardless of the order they are added.\"\"\"\n    package_a = get_package(\"A\", \"1.0.0\")\n    # Add dependencies on B in non-sorted order (by marker and version)\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\",\n            {\"version\": \">=2.0\", \"markers\": 'sys_platform == \"win32\"'},\n        )\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\",\n            {\"version\": \">=1.0\", \"markers\": 'sys_platform == \"linux\"'},\n        )\n    )\n\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    expected = f\"\"\"\\\n# {GENERATED_COMMENT}\n\n[[package]]\nname = \"A\"\nversion = \"1.0.0\"\ndescription = \"\"\noptional = false\npython-versions = \"*\"\ngroups = [\"main\"]\nfiles = []\n\n[package.dependencies]\nB = [\n    {{version = \">=1.0\", markers = \"sys_platform == \\\\\"linux\\\\\"\"}},\n    {{version = \">=2.0\", markers = \"sys_platform == \\\\\"win32\\\\\"\"}},\n]\n\n[metadata]\nlock-version = \"2.1\"\npython-versions = \"*\"\ncontent-hash = \"115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8\"\n\"\"\"\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content = f.read()\n\n    assert content == expected\n\n    # Run again to verify idempotency\n    locker.set_lock_data(root, {package_a: transitive_info})\n\n    with locker.lock.open(encoding=\"utf-8\") as f:\n        content2 = f.read()\n\n    assert content2 == expected\n"
  },
  {
    "path": "tests/packages/test_transitive_package_info.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\n\n\nDEV_GROUP = canonicalize_name(\"dev\")\n\n\n@pytest.mark.parametrize(\n    \"groups, expected\",\n    [\n        (set(), \"<empty>\"),\n        ({\"main\"}, 'sys_platform == \"linux\"'),\n        ({\"dev\"}, 'python_version < \"3.9\"'),\n        ({\"main\", \"dev\"}, 'sys_platform == \"linux\" or python_version < \"3.9\"'),\n        ({\"foo\"}, \"<empty>\"),\n        ({\"main\", \"foo\", \"dev\"}, 'sys_platform == \"linux\" or python_version < \"3.9\"'),\n    ],\n)\ndef test_get_marker(groups: set[str], expected: str) -> None:\n    info = TransitivePackageInfo(\n        depth=0,\n        groups={MAIN_GROUP, DEV_GROUP},\n        markers={\n            MAIN_GROUP: parse_marker('sys_platform ==\"linux\"'),\n            DEV_GROUP: parse_marker('python_version < \"3.9\"'),\n        },\n    )\n    assert str(info.get_marker([canonicalize_name(g) for g in groups])) == expected\n"
  },
  {
    "path": "tests/plugins/__init__.py",
    "content": ""
  },
  {
    "path": "tests/plugins/test_plugin_manager.py",
    "content": "from __future__ import annotations\n\nimport shutil\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import ClassVar\nfrom typing import Protocol\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom cleo.io.outputs.output import Verbosity\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.file_dependency import FileDependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.factory import Factory\nfrom poetry.installation.wheel_installer import WheelInstaller\nfrom poetry.packages.locker import Locker\nfrom poetry.plugins import ApplicationPlugin\nfrom poetry.plugins import Plugin\nfrom poetry.plugins.plugin_manager import PluginManager\nfrom poetry.plugins.plugin_manager import ProjectPluginCache\nfrom poetry.poetry import Poetry\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom tests.helpers import mock_metadata_entry_points\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n    from pytest_mock import MockerFixture\n\n    from poetry.console.commands.command import Command\n    from poetry.utils.env import Env\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n\n\nclass ManagerFactory(Protocol):\n    def __call__(self, group: str = Plugin.group) -> PluginManager: ...\n\n\nclass MyPlugin(Plugin):\n    def activate(self, poetry: Poetry, io: IO) -> None:\n        io.write_line(\"Setting readmes\")\n        poetry.package.readmes = (Path(\"README.md\"),)\n\n\nclass MyCommandPlugin(ApplicationPlugin):\n    commands: ClassVar[list[type[Command]]] = []\n\n\nclass InvalidPlugin:\n    group = \"poetry.plugin\"\n\n    def activate(self, poetry: Poetry, io: IO) -> None:\n        io.write_line(\"Updating version\")\n        poetry.package.version = Version.parse(\"9.9.9\")\n\n\n@pytest.fixture\ndef repo() -> Repository:\n    repo = Repository(\"repo\")\n    repo.add_package(Package(\"my-other-plugin\", \"1.0\"))\n    for version in (\"1.0\", \"2.0\"):\n        package = Package(\"my-application-plugin\", version)\n        package.add_dependency(Dependency(\"some-lib\", version))\n        repo.add_package(package)\n        repo.add_package(Package(\"some-lib\", version))\n    return repo\n\n\n@pytest.fixture\ndef pool(repo: Repository) -> RepositoryPool:\n    pool = RepositoryPool()\n    pool.add_repository(repo)\n\n    return pool\n\n\n@pytest.fixture\ndef poetry(fixture_dir: FixtureDirGetter, config: Config) -> Poetry:\n    project_path = fixture_dir(\"simple_project\")\n    poetry = Poetry(\n        project_path / \"pyproject.toml\",\n        {},\n        ProjectPackage(\"simple-project\", \"1.2.3\"),\n        Locker(project_path / \"poetry.lock\", {}),\n        config,\n    )\n\n    return poetry\n\n\n@pytest.fixture\ndef poetry_with_plugins(\n    fixture_dir: FixtureDirGetter, pool: RepositoryPool, tmp_path: Path\n) -> Poetry:\n    orig_path = fixture_dir(\"project_plugins\")\n    project_path = tmp_path / \"project\"\n    project_path.mkdir()\n    shutil.copy(orig_path / \"pyproject.toml\", project_path / \"pyproject.toml\")\n    poetry = Factory().create_poetry(project_path)\n    poetry.set_pool(pool)\n    return poetry\n\n\n@pytest.fixture()\ndef io() -> BufferedIO:\n    return BufferedIO()\n\n\n@pytest.fixture(autouse=True)\ndef mock_sys_path(mocker: MockerFixture) -> None:\n    sys_path_copy = sys.path.copy()\n    mocker.patch(\"poetry.plugins.plugin_manager.sys.path\", new=sys_path_copy)\n\n\n@pytest.fixture()\ndef manager_factory(poetry: Poetry, io: BufferedIO) -> ManagerFactory:\n    def _manager(group: str = Plugin.group) -> PluginManager:\n        return PluginManager(group)\n\n    return _manager\n\n\n@pytest.fixture\ndef with_my_plugin(mocker: MockerFixture) -> None:\n    mock_metadata_entry_points(mocker, MyPlugin)\n\n\n@pytest.fixture\ndef with_invalid_plugin(mocker: MockerFixture) -> None:\n    mock_metadata_entry_points(mocker, InvalidPlugin)\n\n\ndef test_load_plugins_and_activate(\n    manager_factory: ManagerFactory,\n    poetry: Poetry,\n    io: BufferedIO,\n    with_my_plugin: None,\n) -> None:\n    manager = manager_factory()\n    manager.load_plugins()\n    manager.activate(poetry, io)\n\n    assert poetry.package.readmes == (Path(\"README.md\"),)\n    assert io.fetch_output() == \"Setting readmes\\n\"\n\n\ndef test_load_plugins_with_invalid_plugin(\n    manager_factory: ManagerFactory,\n    poetry: Poetry,\n    io: BufferedIO,\n    with_invalid_plugin: None,\n) -> None:\n    manager = manager_factory()\n\n    with pytest.raises(ValueError):\n        manager.load_plugins()\n\n\ndef test_add_project_plugin_path(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    dist_info_1 = \"my_application_plugin-1.0.dist-info\"\n    dist_info_2 = \"my_application_plugin-2.0.dist-info\"\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    shutil.copytree(\n        fixture_dir(\"project_plugins\") / dist_info_1, cache._path / dist_info_1\n    )\n    shutil.copytree(\n        fixture_dir(\"project_plugins\") / dist_info_2, system_env.purelib / dist_info_2\n    )\n\n    assert {\n        f\"{p.name} {p.version}\" for p in InstalledRepository.load(system_env).packages\n    } == {\"my-application-plugin 2.0\"}\n\n    PluginManager.add_project_plugin_path(poetry_with_plugins.pyproject_path.parent)\n\n    assert {\n        f\"{p.name} {p.version}\" for p in InstalledRepository.load(system_env).packages\n    } == {\"my-application-plugin 1.0\"}\n\n\ndef test_add_project_plugin_path_addsitedir_called(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"Test that addsitedir is called when plugin path exists.\"\"\"\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    cache._path.mkdir(parents=True, exist_ok=True)\n\n    mock_addsitedir = mocker.patch(\"poetry.plugins.plugin_manager.addsitedir\")\n\n    PluginManager.add_project_plugin_path(poetry_with_plugins.pyproject_path.parent)\n\n    # sys.path is mocked, so we can check it was modified\n    assert str(cache._path) in sys.path\n    assert sys.path[0] == str(cache._path)\n    mock_addsitedir.assert_called_once_with(str(cache._path))\n\n\ndef test_add_project_plugin_path_no_addsitedir_when_path_missing(\n    poetry_with_plugins: Poetry,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"Test that addsitedir is not called when plugin path doesn't exist.\"\"\"\n    cache = ProjectPluginCache(poetry_with_plugins, BufferedIO())\n    # Ensure the plugin path does not exist\n    if cache._path.exists():\n        shutil.rmtree(cache._path)\n\n    mock_addsitedir = mocker.patch(\"poetry.plugins.plugin_manager.addsitedir\")\n    initial_sys_path = sys.path.copy()\n\n    PluginManager.add_project_plugin_path(poetry_with_plugins.pyproject_path.parent)\n\n    assert sys.path == initial_sys_path\n    mock_addsitedir.assert_not_called()\n\n\ndef test_add_project_plugin_path_no_pyproject(\n    tmp_path: Path,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"Test that no action is taken when pyproject.toml is missing.\"\"\"\n    mock_addsitedir = mocker.patch(\"poetry.plugins.plugin_manager.addsitedir\")\n    initial_sys_path = sys.path.copy()\n\n    # Call with a directory that has no pyproject.toml\n    PluginManager.add_project_plugin_path(tmp_path)\n\n    assert sys.path == initial_sys_path\n    mock_addsitedir.assert_not_called()\n\n\ndef test_ensure_plugins_no_plugins_no_output(poetry: Poetry, io: BufferedIO) -> None:\n    PluginManager.ensure_project_plugins(poetry, io)\n\n    assert not (poetry.pyproject_path.parent / ProjectPluginCache.PATH).exists()\n    assert io.fetch_output() == \"\"\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_no_plugins_existing_cache_is_removed(\n    poetry: Poetry, io: BufferedIO\n) -> None:\n    plugin_path = poetry.pyproject_path.parent / ProjectPluginCache.PATH\n    plugin_path.mkdir(parents=True)\n\n    PluginManager.ensure_project_plugins(poetry, io)\n\n    assert not plugin_path.exists()\n    assert io.fetch_output() == (\n        \"No project plugins defined. Removing the project's plugin cache\\n\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"debug_out\", [False, True])\ndef test_ensure_plugins_no_output_if_fresh(\n    poetry_with_plugins: Poetry, io: BufferedIO, debug_out: bool\n) -> None:\n    io.set_verbosity(Verbosity.DEBUG if debug_out else Verbosity.NORMAL)\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    cache._write_config()\n\n    cache.ensure_plugins()\n\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"The project's plugin cache is up to date.\\n\\n\" if debug_out else \"\"\n    )\n    assert io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"debug_out\", [False, True])\ndef test_ensure_plugins_ignore_irrelevant_markers(\n    poetry_with_plugins: Poetry, io: BufferedIO, debug_out: bool\n) -> None:\n    io.set_verbosity(Verbosity.DEBUG if debug_out else Verbosity.NORMAL)\n    poetry_with_plugins.local_config[\"requires-plugins\"] = {\n        \"irrelevant\": {\"version\": \"1.0\", \"markers\": \"python_version < '3'\"}\n    }\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n\n    cache.ensure_plugins()\n\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"No relevant project plugins for Poetry's environment defined.\\n\\n\"\n        if debug_out\n        else \"\"\n    )\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_remove_outdated(\n    poetry_with_plugins: Poetry, io: BufferedIO, fixture_dir: FixtureDirGetter\n) -> None:\n    # Test with irrelevant plugins because this is the first return\n    # where it is relevant that an existing cache is removed.\n    poetry_with_plugins.local_config[\"requires-plugins\"] = {\n        \"irrelevant\": {\"version\": \"1.0\", \"markers\": \"python_version < '3'\"}\n    }\n    fixture_path = fixture_dir(\"project_plugins\")\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    cache._path.mkdir(parents=True)\n    dist_info = \"my_application_plugin-1.0.dist-info\"\n    shutil.copytree(fixture_path / dist_info, cache._path / dist_info)\n    cache._config_file.touch()\n\n    cache.ensure_plugins()\n\n    assert cache._config_file.exists()\n    assert not (cache._path / dist_info).exists()\n    assert io.fetch_output() == (\n        \"Removing the project's plugin cache because it is outdated\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_ignore_already_installed_in_system_env(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    fixture_path = fixture_dir(\"project_plugins\")\n    for dist_info in (\n        \"my_application_plugin-2.0.dist-info\",\n        \"my_other_plugin-1.0.dist-info\",\n    ):\n        shutil.copytree(fixture_path / dist_info, system_env.purelib / dist_info)\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n\n    cache.ensure_plugins()\n\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"Ensuring that the Poetry plugins required by the project are available...\\n\"\n        \"All required plugins have already been installed in Poetry's environment.\\n\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_install_missing_plugins(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    install_spy = mocker.spy(cache, \"_install\")\n    execute_mock = mocker.patch(\n        \"poetry.plugins.plugin_manager.Installer._execute\", return_value=0\n    )\n\n    cache.ensure_plugins()\n\n    install_spy.assert_called_once_with(\n        [\n            Dependency(\"my-application-plugin\", \">=2.0\"),\n            Dependency(\"my-other-plugin\", \">=1.0\"),\n        ],\n        system_env,\n        [],\n    )\n    execute_mock.assert_called_once()\n    assert [repr(op) for op in execute_mock.call_args.args[0] if not op.skipped] == [\n        \"<Install some-lib (2.0)>\",\n        \"<Install my-application-plugin (2.0)>\",\n        \"<Install my-other-plugin (1.0)>\",\n    ]\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"Ensuring that the Poetry plugins required by the project are available...\\n\"\n        \"The following Poetry plugins are required by the project\"\n        \" but are not installed in Poetry's environment:\\n\"\n        \"  - my-application-plugin (>=2.0)\\n\"\n        \"  - my-other-plugin (>=1.0)\\n\"\n        \"Installing Poetry plugins only for the current project...\\n\"\n        \"Updating dependencies\\n\"\n        \"Resolving dependencies...\\n\\n\"\n        \"Writing lock file\\n\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_install_only_missing_plugins(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    fixture_path = fixture_dir(\"project_plugins\")\n    for dist_info in (\n        \"my_application_plugin-2.0.dist-info\",\n        \"some_lib-2.0.dist-info\",\n    ):\n        shutil.copytree(fixture_path / dist_info, system_env.purelib / dist_info)\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    install_spy = mocker.spy(cache, \"_install\")\n    execute_mock = mocker.patch(\n        \"poetry.plugins.plugin_manager.Installer._execute\", return_value=0\n    )\n\n    cache.ensure_plugins()\n\n    install_spy.assert_called_once_with(\n        [Dependency(\"my-other-plugin\", \">=1.0\")],\n        system_env,\n        [Package(\"my-application-plugin\", \"2.0\"), Package(\"some-lib\", \"2.0\")],\n    )\n    execute_mock.assert_called_once()\n    assert [repr(op) for op in execute_mock.call_args.args[0] if not op.skipped] == [\n        \"<Install my-other-plugin (1.0)>\"\n    ]\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"Ensuring that the Poetry plugins required by the project are available...\\n\"\n        \"The following Poetry plugins are required by the project\"\n        \" but are not installed in Poetry's environment:\\n\"\n        \"  - my-other-plugin (>=1.0)\\n\"\n        \"Installing Poetry plugins only for the current project...\\n\"\n        \"Updating dependencies\\n\"\n        \"Resolving dependencies...\\n\\n\"\n        \"Writing lock file\\n\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"debug_out\", [False, True])\ndef test_ensure_plugins_install_overwrite_wrong_version_plugins(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n    debug_out: bool,\n) -> None:\n    io.set_verbosity(Verbosity.DEBUG if debug_out else Verbosity.NORMAL)\n    fixture_path = fixture_dir(\"project_plugins\")\n    for dist_info in (\n        \"my_application_plugin-1.0.dist-info\",\n        \"some_lib-2.0.dist-info\",\n    ):\n        shutil.copytree(fixture_path / dist_info, system_env.purelib / dist_info)\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    install_spy = mocker.spy(cache, \"_install\")\n    execute_mock = mocker.patch(\n        \"poetry.plugins.plugin_manager.Installer._execute\", return_value=0\n    )\n\n    cache.ensure_plugins()\n\n    install_spy.assert_called_once_with(\n        [\n            Dependency(\"my-application-plugin\", \">=2.0\"),\n            Dependency(\"my-other-plugin\", \">=1.0\"),\n        ],\n        system_env,\n        [Package(\"some-lib\", \"2.0\")],\n    )\n    execute_mock.assert_called_once()\n    assert [repr(op) for op in execute_mock.call_args.args[0] if not op.skipped] == [\n        \"<Install my-application-plugin (2.0)>\",\n        \"<Install my-other-plugin (1.0)>\",\n    ]\n    assert cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    start = (\n        \"Ensuring that the Poetry plugins required by the project are available...\\n\"\n    )\n    opt = (\n        \"The following Poetry plugins are required by the project\"\n        \" but are not satisfied by the installed versions:\\n\"\n        \"  - my-application-plugin (>=2.0)\\n\"\n        \"    installed: my-application-plugin (1.0)\\n\"\n    )\n    end = (\n        \"The following Poetry plugins are required by the project\"\n        \" but are not installed in Poetry's environment:\\n\"\n        \"  - my-application-plugin (>=2.0)\\n\"\n        \"  - my-other-plugin (>=1.0)\\n\"\n        \"Installing Poetry plugins only for the current project...\\n\"\n    )\n    expected = (start + opt + end) if debug_out else (start + end)\n    assert io.fetch_output().startswith(expected)\n    assert io.fetch_error() == \"\"\n\n\ndef test_ensure_plugins_pins_other_installed_packages(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    fixture_path = fixture_dir(\"project_plugins\")\n    for dist_info in (\n        \"my_application_plugin-1.0.dist-info\",\n        \"some_lib-1.0.dist-info\",\n    ):\n        shutil.copytree(fixture_path / dist_info, system_env.purelib / dist_info)\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n    install_spy = mocker.spy(cache, \"_install\")\n    execute_mock = mocker.patch(\n        \"poetry.plugins.plugin_manager.Installer._execute\", return_value=0\n    )\n\n    with pytest.raises(SolverProblemError):\n        cache.ensure_plugins()\n\n    install_spy.assert_called_once_with(\n        [\n            Dependency(\"my-application-plugin\", \">=2.0\"),\n            Dependency(\"my-other-plugin\", \">=1.0\"),\n        ],\n        system_env,\n        # pinned because it might be a dependency of another plugin or Poetry itself\n        [Package(\"some-lib\", \"1.0\")],\n    )\n    execute_mock.assert_not_called()\n    assert not cache._config_file.exists()\n    assert (\n        cache._gitignore_file.exists()\n        and cache._gitignore_file.read_text(encoding=\"utf-8\") == \"*\"\n    )\n    assert io.fetch_output() == (\n        \"Ensuring that the Poetry plugins required by the project are available...\\n\"\n        \"The following Poetry plugins are required by the project\"\n        \" but are not installed in Poetry's environment:\\n\"\n        \"  - my-application-plugin (>=2.0)\\n\"\n        \"  - my-other-plugin (>=1.0)\\n\"\n        \"Installing Poetry plugins only for the current project...\\n\"\n        \"Updating dependencies\\n\"\n        \"Resolving dependencies...\\n\"\n    )\n    assert io.fetch_error() == \"\"\n\n\n@pytest.mark.parametrize(\"other_version\", [False, True])\ndef test_project_plugins_are_installed_in_project_folder(\n    poetry_with_plugins: Poetry,\n    io: BufferedIO,\n    system_env: Env,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    other_version: bool,\n) -> None:\n    orig_purelib = system_env.purelib\n    orig_platlib = system_env.platlib\n\n    # make sure that the path dependency is on the same drive (for Windows tests in CI)\n    orig_wheel_path = (\n        fixture_dir(\"wheel_with_no_requires_dist\") / \"demo-0.1.0-py2.py3-none-any.whl\"\n    )\n    wheel_path = tmp_path / orig_wheel_path.name\n    shutil.copy(orig_wheel_path, wheel_path)\n\n    if other_version:\n        WheelInstaller(system_env).install(wheel_path)\n        dist_info = orig_purelib / \"demo-0.1.0.dist-info\"\n        metadata = dist_info / \"METADATA\"\n        metadata.write_text(\n            metadata.read_text(encoding=\"utf-8\").replace(\"0.1.0\", \"0.1.2\"),\n            encoding=\"utf-8\",\n        )\n        dist_info.rename(orig_purelib / \"demo-0.1.2.dist-info\")\n\n    cache = ProjectPluginCache(poetry_with_plugins, io)\n\n    # just use a file dependency so that we do not have to set up a repository\n    cache._install([FileDependency(\"demo\", wheel_path)], system_env, [])\n\n    project_site_packages = [p.name for p in cache._path.iterdir()]\n    assert \"demo\" in project_site_packages\n    assert \"demo-0.1.0.dist-info\" in project_site_packages\n\n    orig_site_packages = [p.name for p in orig_purelib.iterdir()]\n    if other_version:\n        assert \"demo\" in orig_site_packages\n        assert \"demo-0.1.2.dist-info\" in orig_site_packages\n        assert \"demo-0.1.0.dist-info\" not in orig_site_packages\n    else:\n        assert not any(p.startswith(\"demo\") for p in orig_site_packages)\n    if orig_platlib != orig_purelib:\n        assert not any(p.name.startswith(\"demo\") for p in orig_platlib.iterdir())\n"
  },
  {
    "path": "tests/publishing/__init__.py",
    "content": ""
  },
  {
    "path": "tests/publishing/test_hash_manager.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom poetry.publishing.hash_manager import HashManager\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from pytest_mock import MockerFixture\n\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture\ndef distributions_dir(fixture_dir: FixtureDirGetter) -> Path:\n    return fixture_dir(\"distributions\")\n\n\n@pytest.mark.parametrize(\n    \"file, hashes\",\n    (\n        (\n            \"demo-0.1.0.tar.gz\",\n            (\n                \"d1912c917363a64e127318655f7d1fe7\",\n                \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\",\n                \"cb638093d63df647e70b03e963bedc31e021cb088695e29101b69f525e3d5fef\",\n            ),\n        ),\n        (\n            \"demo-0.1.2-py2.py3-none-any.whl\",\n            (\n                \"53b4e10d2bfa81a4206221c4b87843d9\",\n                \"55dde4e6828081de7a1e429f33180459c333d9da593db62a3d75a8f5e505dde1\",\n                \"b35b9aab064e88fffe42309550ebe425907fb42ccb3b1d173b7d6b7509f38eac\",\n            ),\n        ),\n    ),\n)\ndef test_file_hashes_returns_proper_hashes_for_file(\n    file: str, hashes: tuple[str, ...], distributions_dir: Path\n) -> None:\n    manager = HashManager()\n    manager.hash(distributions_dir / file)\n    file_hashes = manager.hexdigest()\n    assert file_hashes == hashes\n\n\ndef test_file_hashes_returns_none_for_md5_with_fips(\n    mocker: MockerFixture, distributions_dir: Path\n) -> None:\n    # disable md5\n    def fips_md5(*args: Any, **kwargs: Any) -> Any:\n        raise ValueError(\"Disabled by FIPS\")\n\n    mocker.patch(\"hashlib.md5\", new=fips_md5)\n\n    manager = HashManager()\n    manager.hash(distributions_dir / \"demo-0.1.0.tar.gz\")\n    file_hashes = manager.hexdigest()\n\n    assert file_hashes.md5 is None\n\n\ndef test_file_hashes_returns_none_for_blake2_with_fips(\n    mocker: MockerFixture, distributions_dir: Path\n) -> None:\n    # disable md5\n    def fips_blake2b(*args: Any, **kwargs: Any) -> Any:\n        raise ValueError(\"Disabled by FIPS\")\n\n    mocker.patch(\"hashlib.blake2b\", new=fips_blake2b)\n\n    manager = HashManager()\n    manager.hash(distributions_dir / \"demo-0.1.0.tar.gz\")\n    file_hashes = manager.hexdigest()\n\n    assert file_hashes.blake2_256 is None\n"
  },
  {
    "path": "tests/publishing/test_publisher.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom cleo.io.null_io import NullIO\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.factory import Factory\nfrom poetry.publishing.publisher import Publisher\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n\n\ndef test_publish_publishes_to_pypi_by_default(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, config: Config\n) -> None:\n    uploader_auth = mocker.patch(\"poetry.publishing.uploader.Uploader.auth\")\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n    poetry._config = config\n    poetry.config.merge(\n        {\"http-basic\": {\"pypi\": {\"username\": \"foo\", \"password\": \"bar\"}}}\n    )\n    publisher = Publisher(poetry, NullIO())\n\n    publisher.publish(None, None, None)\n\n    assert uploader_auth.call_args == [(\"foo\", \"bar\")]\n    assert uploader_upload.call_args == [\n        (\"https://upload.pypi.org/legacy/\",),\n        {\"cert\": True, \"client_cert\": None, \"dry_run\": False, \"skip_existing\": False},\n    ]\n\n\n@pytest.mark.parametrize(\"fixture_name\", [\"sample_project\", \"with_source\"])\ndef test_publish_can_publish_to_given_repository(\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n    config: Config,\n    fixture_name: str,\n) -> None:\n    uploader_version = \"1.2.3+test\"\n    mocker.patch(\"poetry.publishing.uploader.Uploader.version\", uploader_version)\n    uploader_auth = mocker.patch(\"poetry.publishing.uploader.Uploader.auth\")\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n\n    config.merge(\n        {\n            \"repositories\": {\"foo\": {\"url\": \"http://foo.bar\"}},\n            \"http-basic\": {\"foo\": {\"username\": \"foo\", \"password\": \"bar\"}},\n        }\n    )\n\n    mocker.patch(\"poetry.config.config.Config.create\", return_value=config)\n    poetry = Factory().create_poetry(fixture_dir(fixture_name))\n    # Normally both versions are equal, but we want to check that the correct version\n    # is displayed in the output if they are different.\n    assert poetry.package.version != uploader_version\n\n    io = BufferedIO()\n    publisher = Publisher(poetry, io)\n\n    publisher.publish(\"foo\", None, None)\n\n    assert uploader_auth.call_args == [(\"foo\", \"bar\")]\n    assert uploader_upload.call_args == [\n        (\"http://foo.bar\",),\n        {\"cert\": True, \"client_cert\": None, \"dry_run\": False, \"skip_existing\": False},\n    ]\n    project_name = canonicalize_name(fixture_name)\n    assert f\"Publishing {project_name} ({uploader_version}) to foo\" in io.fetch_output()\n\n\ndef test_publish_raises_error_for_undefined_repository(\n    fixture_dir: FixtureDirGetter, config: Config\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n    poetry._config = config\n    poetry.config.merge(\n        {\"http-basic\": {\"my-repo\": {\"username\": \"foo\", \"password\": \"bar\"}}}\n    )\n    publisher = Publisher(poetry, NullIO())\n\n    with pytest.raises(RuntimeError):\n        publisher.publish(\"my-repo\", None, None)\n\n\ndef assert_publish_uses_token_if_it_exists(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, config: Config | None = None\n) -> None:\n    uploader_auth = mocker.patch(\"poetry.publishing.uploader.Uploader.auth\")\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n\n    if config:\n        poetry._config = config\n\n    publisher = Publisher(poetry, NullIO())\n    publisher.publish(None, None, None)\n\n    assert uploader_auth.call_args == [(\"__token__\", \"my-token\")]\n    assert uploader_upload.call_args == [\n        (\"https://upload.pypi.org/legacy/\",),\n        {\"cert\": True, \"client_cert\": None, \"dry_run\": False, \"skip_existing\": False},\n    ]\n\n\ndef test_publish_uses_token_if_it_exists(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, config: Config\n) -> None:\n    config.merge({\"pypi-token\": {\"pypi\": \"my-token\"}})\n    assert_publish_uses_token_if_it_exists(fixture_dir, mocker, config)\n\n\ndef test_publish_uses_env_token_if_it_exists(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, environ: None\n) -> None:\n    os.environ[\"POETRY_PYPI_TOKEN_PYPI\"] = \"my-token\"\n    assert_publish_uses_token_if_it_exists(fixture_dir, mocker)\n\n\ndef test_publish_uses_cert(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, config: Config\n) -> None:\n    cert = \"path/to/ca.pem\"\n    uploader_auth = mocker.patch(\"poetry.publishing.uploader.Uploader.auth\")\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n    poetry._config = config\n    poetry.config.merge(\n        {\n            \"repositories\": {\"foo\": {\"url\": \"https://foo.bar\"}},\n            \"http-basic\": {\"foo\": {\"username\": \"foo\", \"password\": \"bar\"}},\n            \"certificates\": {\"foo\": {\"cert\": cert}},\n        }\n    )\n    publisher = Publisher(poetry, NullIO())\n\n    publisher.publish(\"foo\", None, None)\n\n    assert uploader_auth.call_args == [(\"foo\", \"bar\")]\n    assert uploader_upload.call_args == [\n        (\"https://foo.bar\",),\n        {\n            \"cert\": Path(cert),\n            \"client_cert\": None,\n            \"dry_run\": False,\n            \"skip_existing\": False,\n        },\n    ]\n\n\ndef test_publish_uses_client_cert(\n    fixture_dir: FixtureDirGetter, mocker: MockerFixture, config: Config\n) -> None:\n    client_cert = \"path/to/client.pem\"\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n    poetry._config = config\n    poetry.config.merge(\n        {\n            \"repositories\": {\"foo\": {\"url\": \"https://foo.bar\"}},\n            \"certificates\": {\"foo\": {\"client-cert\": client_cert}},\n        }\n    )\n    publisher = Publisher(poetry, NullIO())\n\n    publisher.publish(\"foo\", None, None)\n\n    assert uploader_upload.call_args == [\n        (\"https://foo.bar\",),\n        {\n            \"cert\": True,\n            \"client_cert\": Path(client_cert),\n            \"dry_run\": False,\n            \"skip_existing\": False,\n        },\n    ]\n\n\ndef test_publish_read_from_environment_variable(\n    fixture_dir: FixtureDirGetter,\n    environ: None,\n    mocker: MockerFixture,\n    config: Config,\n) -> None:\n    os.environ[\"POETRY_REPOSITORIES_FOO_URL\"] = \"https://foo.bar\"\n    os.environ[\"POETRY_HTTP_BASIC_FOO_USERNAME\"] = \"bar\"\n    os.environ[\"POETRY_HTTP_BASIC_FOO_PASSWORD\"] = \"baz\"\n    uploader_auth = mocker.patch(\"poetry.publishing.uploader.Uploader.auth\")\n    uploader_upload = mocker.patch(\"poetry.publishing.uploader.Uploader.upload\")\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n    publisher = Publisher(poetry, NullIO())\n\n    publisher.publish(\"foo\", None, None)\n\n    assert uploader_auth.call_args == [(\"bar\", \"baz\")]\n    assert uploader_upload.call_args == [\n        (\"https://foo.bar\",),\n        {\"cert\": True, \"client_cert\": None, \"dry_run\": False, \"skip_existing\": False},\n    ]\n"
  },
  {
    "path": "tests/publishing/test_uploader.py",
    "content": "from __future__ import annotations\n\nimport shutil\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\n\nfrom poetry.factory import Factory\nfrom poetry.publishing.uploader import Uploader\nfrom poetry.publishing.uploader import UploadError\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    import responses\n\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture\ndef poetry(fixture_dir: FixtureDirGetter) -> Poetry:\n    return Factory().create_poetry(fixture_dir(\"simple_project\"))\n\n\n@pytest.fixture\ndef uploader(poetry: Poetry) -> Uploader:\n    return Uploader(poetry, NullIO())\n\n\n@pytest.mark.parametrize(\n    (\"files\", \"expected_files\", \"expected_version\"),\n    [\n        ([], [], \"\"),\n        (\n            [\"simple_project-1.2.3.tar.gz\", \"simple_project-1.2.3-py3-none-any.whl\"],\n            [\"simple_project-1.2.3.tar.gz\", \"simple_project-1.2.3-py3-none-any.whl\"],\n            \"1.2.3\",\n        ),\n        (  # other names are ignored\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"other_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n                \"other_project-1.2.3-py3-none-any.whl\",\n            ],\n            [\"simple_project-1.2.3.tar.gz\", \"simple_project-1.2.3-py3-none-any.whl\"],\n            \"1.2.3\",\n        ),\n        (  # older versions are ignored\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.4.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n                \"simple_project-1.2.4-py3-none-any.whl\",\n            ],\n            [\"simple_project-1.2.4.tar.gz\", \"simple_project-1.2.4-py3-none-any.whl\"],\n            \"1.2.4\",\n        ),\n        (  # older versions are ignored - only new sdist\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.4.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n            ],\n            [\"simple_project-1.2.4.tar.gz\"],\n            \"1.2.4\",\n        ),\n        (  # older versions are ignored - only new wheel\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n                \"simple_project-1.2.4-py3-none-any.whl\",\n            ],\n            [\"simple_project-1.2.4-py3-none-any.whl\"],\n            \"1.2.4\",\n        ),\n        (  # older versions are ignored - local version\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.3+hash1.tar.gz\",\n                \"simple_project-1.2.3+hash2.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n                \"simple_project-1.2.3+hash1-py3-none-any.whl\",\n                \"simple_project-1.2.3+hash2-py3-none-any.whl\",\n            ],\n            [\n                \"simple_project-1.2.3+hash2.tar.gz\",\n                \"simple_project-1.2.3+hash2-py3-none-any.whl\",\n            ],\n            \"1.2.3+hash2\",\n        ),\n        (  # older versions are ignore - pre-release\n            [\n                \"simple_project-1.2.3rc1.tar.gz\",\n                \"simple_project-1.2.3rc1-py3-none-any.whl\",\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n            ],\n            [\n                \"simple_project-1.2.3.tar.gz\",\n                \"simple_project-1.2.3-py3-none-any.whl\",\n            ],\n            \"1.2.3\",\n        ),\n    ],\n)\ndef test_uploader_files_only_latest(\n    poetry: Poetry,\n    tmp_path: Path,\n    files: list[str],\n    expected_files: list[str],\n    expected_version: str,\n) -> None:\n    for file in files:\n        (tmp_path / file).touch()\n    uploader = Uploader(poetry, NullIO(), dist_dir=tmp_path)\n\n    assert uploader.files == [tmp_path / f for f in expected_files]\n    assert uploader.version == expected_version\n\n\ndef test_uploader_properly_handles_400_errors(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    http.post(\"https://foo.com\", status=400, body=\"Bad request\")\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert str(e.value) == \"HTTP Error 400: Bad Request | b'Bad request'\"\n\n\ndef test_uploader_properly_handles_403_errors(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    http.post(\"https://foo.com\", status=403, body=\"Unauthorized\")\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert str(e.value) == \"HTTP Error 403: Forbidden | b'Unauthorized'\"\n\n\ndef test_uploader_properly_handles_nonstandard_errors(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    # content based off a true story.\n    # Message changed to protect the ~~innocent~~ guilty.\n    content = (\n        b'{\\n \"errors\": [ {\\n '\n        b'\"status\": 400,'\n        b'\"message\": \"I cant let you do that, dave\"\\n'\n        b\"} ]\\n}\"\n    )\n    http.post(\"https://foo.com\", status=400, body=content)\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert str(e.value) == f\"HTTP Error 400: Bad Request | {content!r}\"\n\n\n@pytest.mark.parametrize(\n    (\"status\", \"code\"),\n    [\n        (308, \"Permanent Redirect\"),\n        (307, \"Temporary Redirect\"),\n        (304, \"Not Modified\"),\n        (303, \"See Other\"),\n        (302, \"Found\"),\n        (301, \"Moved Permanently\"),\n        (300, \"Multiple Choices\"),\n    ],\n)\ndef test_uploader_properly_handles_redirects(\n    http: responses.RequestsMock, uploader: Uploader, status: int, code: str\n) -> None:\n    http.post(\"https://foo.com\", status=status)\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert (\n        str(e.value)\n        == \"Redirects are not supported. Is the URL missing a trailing slash?\"\n    )\n\n\ndef test_uploader_properly_handles_301_redirects(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    http.post(\"https://foo.com\", status=301, body=\"Redirect\")\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert (\n        str(e.value)\n        == \"Redirects are not supported. Is the URL missing a trailing slash?\"\n    )\n\n\ndef test_uploader_registers_with_sdist_for_appropriate_400_errors(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    http.post(\"https://foo.com\", status=400, body=\"No package was ever registered\")\n\n    with pytest.raises(UploadError):\n        uploader.upload(\"https://foo.com\")\n\n    assert len(http.calls) == 2\n    bodies = [c.request.body or b\"\" for c in http.calls]\n    assert b'name=\":action\"\\r\\n\\r\\nfile_upload\\r\\n' in bodies[0]\n    assert b'name=\":action\"\\r\\n\\r\\nsubmit\\r\\n' in bodies[1]\n    assert b\"sdist\" in bodies[0]\n    assert b\"sdist\" in bodies[1]\n    assert b\"bdist_wheel\" not in bodies[0]\n    assert b\"bdist_wheel\" not in bodies[1]\n\n\ndef test_uploader_register_uses_wheel_if_no_sdist(\n    http: responses.RequestsMock, poetry: Poetry, tmp_path: Path\n) -> None:\n    dist_dir = tmp_path / \"dist\"\n    dist_dir.mkdir()\n    shutil.copy(\n        poetry.file.path.parent / \"dist\" / \"simple_project-1.2.3-py2.py3-none-any.whl\",\n        dist_dir,\n    )\n\n    uploader = Uploader(poetry, NullIO(), dist_dir=dist_dir)\n\n    http.post(\"https://foo.com\", status=400, body=\"No package was ever registered\")\n\n    with pytest.raises(UploadError):\n        uploader.upload(\"https://foo.com\")\n\n    assert len(http.calls) == 2\n    bodies = [c.request.body or b\"\" for c in http.calls]\n    assert b'name=\":action\"\\r\\n\\r\\nfile_upload\\r\\n' in bodies[0]\n    assert b'name=\":action\"\\r\\n\\r\\nsubmit\\r\\n' in bodies[1]\n    assert b\"sdist\" not in bodies[0]\n    assert b\"sdist\" not in bodies[1]\n    assert b\"bdist_wheel\" in bodies[0]\n    assert b\"bdist_wheel\" in bodies[1]\n\n\n@pytest.mark.parametrize(\n    \"status, body\",\n    [\n        (409, \"\"),\n        (400, \"File already exists\"),\n        (400, \"Repository does not allow updating assets\"),\n        (400, \"cannot be updated\"),\n        (403, \"Not enough permissions to overwrite artifact\"),\n        (400, \"file name has already been taken\"),\n    ],\n)\ndef test_uploader_skips_existing(\n    http: responses.RequestsMock, uploader: Uploader, status: int, body: str\n) -> None:\n    http.post(\"https://foo.com\", status=status, body=body)\n\n    # should not raise\n    uploader.upload(\"https://foo.com\", skip_existing=True)\n\n\ndef test_uploader_skip_existing_bubbles_unskippable_errors(\n    http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    http.post(\"https://foo.com\", status=403, body=\"Unauthorized\")\n\n    with pytest.raises(UploadError):\n        uploader.upload(\"https://foo.com\", skip_existing=True)\n\n\ndef test_uploader_properly_handles_file_not_existing(\n    mocker: MockerFixture, http: responses.RequestsMock, uploader: Uploader\n) -> None:\n    mocker.patch(\"pathlib.Path.is_file\", return_value=False)\n\n    with pytest.raises(UploadError) as e:\n        uploader.upload(\"https://foo.com\")\n\n    assert f\"Archive ({uploader.files[0]}) does not exist\" == str(e.value)\n\n\ndef test_uploader_post_data_wheel(fixture_dir: FixtureDirGetter) -> None:\n    file = (\n        fixture_dir(\"simple_project\")\n        / \"dist\"\n        / \"simple_project-1.2.3-py2.py3-none-any.whl\"\n    )\n    assert Uploader.post_data(file) == {\n        \"md5_digest\": \"fb4a5266406b9cf34ceaa88d1c8b7a01\",\n        \"sha256_digest\": \"fc365a242d4de8b8661babc088f44b3df25e9e0017ef5dd7140dfe50f9323e16\",\n        \"blake2_256_digest\": \"2e006d1fbfef0ed38fbded1ec1614dc4fd66f81061fe290528e2744dbc25ce31\",\n        \"filetype\": \"bdist_wheel\",\n        \"pyversion\": \"py2.py3\",\n        \"metadata_version\": \"2.1\",\n        \"name\": \"simple-project\",\n        \"version\": \"1.2.3\",\n        \"summary\": \"Some description.\",\n        \"author\": \"Sébastien Eustace\",\n        \"author_email\": \"sebastien@eustace.io\",\n        \"license\": \"MIT\",\n        \"classifiers\": [\n            \"License :: OSI Approved :: MIT License\",\n            \"Programming Language :: Python :: 2\",\n            \"Programming Language :: Python :: 2.7\",\n            \"Programming Language :: Python :: 3\",\n            \"Programming Language :: Python :: 3.6\",\n            \"Programming Language :: Python :: 3.7\",\n            \"Topic :: Software Development :: Build Tools\",\n            \"Topic :: Software Development :: Libraries :: Python Modules\",\n        ],\n        \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*\",\n        \"description\": \"My Package\\n==========\\n\\n\",\n        \"description_content_type\": \"text/x-rst\",\n        \"keywords\": \"packaging, dependency, poetry\",\n        \"home_page\": \"https://poetry.eustace.io\",\n        \"project_urls\": [\n            \"Documentation, https://poetry.eustace.io/docs\",\n            \"Repository, https://github.com/sdispater/poetry\",\n        ],\n    }\n\n\ndef test_uploader_post_data_sdist(fixture_dir: FixtureDirGetter) -> None:\n    file = fixture_dir(\"simple_project\") / \"dist\" / \"simple_project-1.2.3.tar.gz\"\n    assert Uploader.post_data(file) == {\n        \"md5_digest\": \"e611cbb8f31258243d90f7681dfda68a\",\n        \"sha256_digest\": \"c4a72becabca29ec2a64bf8c820bbe204d2268f53e102501ea5605bc1c1675d1\",\n        \"blake2_256_digest\": \"d3df22f4944f6acd02105e7e2df61ef63c7b0f4337a12df549ebc2805a13c2be\",\n        \"filetype\": \"sdist\",\n        \"pyversion\": \"source\",\n        \"metadata_version\": \"2.1\",\n        \"name\": \"simple-project\",\n        \"version\": \"1.2.3\",\n        \"summary\": \"Some description.\",\n        \"author\": \"Sébastien Eustace\",\n        \"author_email\": \"sebastien@eustace.io\",\n        \"classifiers\": [\n            \"License :: OSI Approved :: MIT License\",\n            \"Programming Language :: Python :: 2\",\n            \"Programming Language :: Python :: 2.7\",\n            \"Programming Language :: Python :: 3\",\n            \"Programming Language :: Python :: 3.6\",\n            \"Programming Language :: Python :: 3.7\",\n            \"Topic :: Software Development :: Build Tools\",\n            \"Topic :: Software Development :: Libraries :: Python Modules\",\n        ],\n        \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*\",\n        \"keywords\": \"packaging, dependency, poetry\",\n        \"home_page\": \"https://poetry.eustace.io\",\n        \"project_urls\": [\n            \"Documentation, https://poetry.eustace.io/docs\",\n            \"Repository, https://github.com/sdispater/poetry\",\n        ],\n    }\n"
  },
  {
    "path": "tests/puzzle/__init__.py",
    "content": ""
  },
  {
    "path": "tests/puzzle/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom poetry.core.packages.project_package import ProjectPackage\n\nfrom poetry.puzzle import Solver\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import mock_clone\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture) -> None:\n    # Patch git module to not actually clone projects\n    mocker.patch(\"poetry.vcs.git.Git.clone\", new=mock_clone)\n    p = mocker.patch(\"poetry.vcs.git.Git.get_revision\")\n    p.return_value = MOCK_DEFAULT_GIT_REVISION\n\n\n@pytest.fixture\ndef io() -> NullIO:\n    return NullIO()\n\n\n@pytest.fixture\ndef package() -> ProjectPackage:\n    return ProjectPackage(\"root\", \"1.0\")\n\n\n@pytest.fixture\ndef repo() -> Repository:\n    return Repository(\"repo\")\n\n\n@pytest.fixture\ndef pool(repo: Repository) -> RepositoryPool:\n    return RepositoryPool([repo])\n\n\n@pytest.fixture\ndef solver(package: ProjectPackage, pool: RepositoryPool, io: NullIO) -> Solver:\n    return Solver(package, pool, [], [], io)\n"
  },
  {
    "path": "tests/puzzle/test_provider.py",
    "content": "from __future__ import annotations\n\nimport shutil\n\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom cleo.io.null_io import NullIO\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.directory_dependency import DirectoryDependency\nfrom poetry.core.packages.file_dependency import FileDependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\nfrom poetry.core.packages.url_dependency import URLDependency\nfrom poetry.core.packages.vcs_dependency import VCSDependency\n\nfrom poetry.factory import Factory\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.packages import DependencyPackage\nfrom poetry.puzzle.provider import IncompatibleConstraintsError\nfrom poetry.puzzle.provider import Provider\nfrom poetry.repositories.cached_repository import CachedRepository\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.repository import Repository\nfrom poetry.repositories.repository_pool import Priority\nfrom poetry.repositories.repository_pool import RepositoryPool\nfrom poetry.utils.env import EnvCommandError\nfrom poetry.utils.env import MockEnv as BaseMockEnv\nfrom tests.helpers import get_dependency\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from poetry.core.constraints.version import Version\n    from pytest_mock import MockerFixture\n\n    from tests.types import FixtureDirGetter\n\n\nSOME_URL = \"https://example.com/path.tar.gz\"\n\n\nclass MockEnv(BaseMockEnv):\n    def run(self, bin: str, *args: str, **kwargs: Any) -> str:\n        raise EnvCommandError(CalledProcessError(1, \"python\", output=\"\"))\n\n\nclass MockCachedRepository(CachedRepository):\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def package(self, name: str, version: Version) -> Package:\n        package = super().package(name, version)\n        package._source_reference = self.name\n        return package\n\n\n@pytest.fixture\ndef root() -> ProjectPackage:\n    return ProjectPackage(\"root\", \"1.2.3\")\n\n\n@pytest.fixture\ndef repository() -> Repository:\n    return Repository(\"repo\")\n\n\n@pytest.fixture\ndef pool(repository: Repository) -> RepositoryPool:\n    pool = RepositoryPool()\n    pool.add_repository(repository)\n\n    return pool\n\n\n@pytest.fixture\ndef provider(root: ProjectPackage, pool: RepositoryPool) -> Provider:\n    return Provider(root, pool, NullIO())\n\n\n@pytest.fixture\ndef release_info() -> PackageInfo:\n    return PackageInfo(\n        name=\"mylib\",\n        version=\"1.0\",\n        summary=\"\",\n        requires_dist=[],\n        requires_python=\">=3.9\",\n        files=[\n            {\n                \"file\": \"mylib-1.0-py3-none-any.whl\",\n                \"hash\": \"sha256:dummyhashvalue1234567890abcdef\",\n            },\n            {\n                \"file\": \"mylib-1.0.tar.gz\",\n                \"hash\": \"sha256:anotherdummyhashvalueabcdef1234567890\",\n            },\n        ],\n        cache_version=str(CachedRepository.CACHE_VERSION),\n    )\n\n\n@pytest.fixture\ndef release_info_complete() -> PackageInfo:\n    return PackageInfo(\n        name=\"mylib\",\n        version=\"1.0\",\n        summary=\"\",\n        requires_dist=[],\n        requires_python=\">=3.9\",\n        files=[\n            {\n                \"file\": \"mylib-1.0-py3-none-any.whl\",\n                \"hash\": \"sha256:dummyhashvalue1234567890abcdef\",\n                \"url\": \"https://example.org/mylib-1.0-py3-none-any.whl\",\n                \"size\": 12345,\n                \"upload_time\": \"2024-01-01T12:00:00Z\",\n            },\n            {\n                \"file\": \"mylib-1.0.tar.gz\",\n                \"hash\": \"sha256:anotherdummyhashvalueabcdef1234567890\",\n                \"url\": \"https://example.org/mylib-1.0.tar.gz\",\n                \"size\": 12346,\n                \"upload_time\": \"2024-01-01T12:00:01Z\",\n            },\n        ],\n        cache_version=str(CachedRepository.CACHE_VERSION),\n    )\n\n\n@pytest.fixture\ndef outdated_release_info() -> PackageInfo:\n    return PackageInfo(\n        name=\"mylib\",\n        version=\"1.0\",\n        summary=\"\",\n        requires_dist=[],\n        requires_python=\">=3.9\",\n        files=[\n            {\n                \"file\": \"mylib-1.0-py3-none-any.whl\",\n                \"hash\": \"sha256:dummyhashvalue1234567890abcdef\",\n            }\n        ],\n        cache_version=str(CachedRepository.CACHE_VERSION),\n    )\n\n\n@pytest.mark.parametrize(\n    \"dependency, expected\",\n    [\n        (Dependency(\"foo\", \"<2\"), [Package(\"foo\", \"1\")]),\n        (Dependency(\"foo\", \"<2\", extras=[\"bar\"]), [Package(\"foo\", \"1\")]),\n        (Dependency(\"foo\", \">=1\"), [Package(\"foo\", \"2\"), Package(\"foo\", \"1\")]),\n        (Dependency(\"foo\", \">=1a\"), [Package(\"foo\", \"2\"), Package(\"foo\", \"1\")]),\n        (\n            Dependency(\"foo\", \">=1\", allows_prereleases=True),\n            [\n                Package(\"foo\", \"3a\"),\n                Package(\"foo\", \"2\"),\n                Package(\"foo\", \"2a\"),\n                Package(\"foo\", \"1\"),\n            ],\n        ),\n    ],\n)\ndef test_search_for(\n    provider: Provider,\n    repository: Repository,\n    dependency: Dependency,\n    expected: list[Package],\n) -> None:\n    foo1 = Package(\"foo\", \"1\")\n    foo2a = Package(\"foo\", \"2a\")\n    foo2 = Package(\"foo\", \"2\")\n    foo3a = Package(\"foo\", \"3a\")\n    repository.add_package(foo1)\n    repository.add_package(foo2a)\n    repository.add_package(foo2)\n    repository.add_package(foo3a)\n\n    assert provider.search_for(dependency) == expected\n\n\n@pytest.mark.parametrize(\n    \"dependency, direct_origin_dependency, expected_before, expected_after\",\n    [\n        (\n            Dependency(\"foo\", \">=1\"),\n            URLDependency(\"foo\", SOME_URL),\n            [Package(\"foo\", \"3\")],\n            [Package(\"foo\", \"2a\", source_type=\"url\", source_url=SOME_URL)],\n        ),\n        (\n            Dependency(\"foo\", \">=2\"),\n            URLDependency(\"foo\", SOME_URL),\n            [Package(\"foo\", \"3\")],\n            [Package(\"foo\", \"3\")],\n        ),\n        (\n            Dependency(\"foo\", \">=1\", extras=[\"bar\"]),\n            URLDependency(\"foo\", SOME_URL),\n            [Package(\"foo\", \"3\")],\n            [Package(\"foo\", \"2a\", source_type=\"url\", source_url=SOME_URL)],\n        ),\n        (\n            Dependency(\"foo\", \">=1\"),\n            URLDependency(\"foo\", SOME_URL, extras=[\"baz\"]),\n            [Package(\"foo\", \"3\")],\n            [Package(\"foo\", \"2a\", source_type=\"url\", source_url=SOME_URL)],\n        ),\n        (\n            Dependency(\"foo\", \">=1\", extras=[\"bar\"]),\n            URLDependency(\"foo\", SOME_URL, extras=[\"baz\"]),\n            [Package(\"foo\", \"3\")],\n            [Package(\"foo\", \"2a\", source_type=\"url\", source_url=SOME_URL)],\n        ),\n    ],\n)\ndef test_search_for_direct_origin_and_extras(\n    provider: Provider,\n    repository: Repository,\n    mocker: MockerFixture,\n    dependency: Dependency,\n    direct_origin_dependency: Dependency,\n    expected_before: list[Package],\n    expected_after: list[Package],\n) -> None:\n    foo2a_direct_origin = Package(\"foo\", \"2a\", source_type=\"url\", source_url=SOME_URL)\n    mocker.patch(\n        \"poetry.puzzle.provider.Provider.search_for_direct_origin_dependency\",\n        return_value=foo2a_direct_origin,\n    )\n    foo2a = Package(\"foo\", \"2a\")\n    foo3 = Package(\"foo\", \"3\")\n    repository.add_package(foo2a)\n    repository.add_package(foo3)\n\n    assert provider.search_for(dependency) == expected_before\n    assert provider.search_for(direct_origin_dependency) == [foo2a_direct_origin]\n    assert provider.search_for(dependency) == expected_after\n\n\n@pytest.mark.parametrize(\"value\", [True, False])\ndef test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool) -> None:\n    dependency = VCSDependency(\n        \"demo\", \"git\", \"https://github.com/demo/demo.git\", develop=value\n    )\n    package = provider.search_for_direct_origin_dependency(dependency)\n    assert package.develop == value\n\n\ndef test_search_for_vcs_setup_egg_info(provider: Provider) -> None:\n    dependency = VCSDependency(\"demo\", \"git\", \"https://github.com/demo/demo.git\")\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_vcs_setup_egg_info_with_extras(provider: Provider) -> None:\n    dependency = VCSDependency(\n        \"demo\", \"git\", \"https://github.com/demo/demo.git\", extras=[\"foo\"]\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_vcs_read_setup(provider: Provider, mocker: MockerFixture) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=MockEnv())\n\n    dependency = VCSDependency(\"demo\", \"git\", \"https://github.com/demo/demo.git\")\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_vcs_read_setup_with_extras(\n    provider: Provider, mocker: MockerFixture\n) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=MockEnv())\n\n    dependency = VCSDependency(\n        \"demo\", \"git\", \"https://github.com/demo/demo.git\", extras=[\"foo\"]\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n\n\ndef test_search_for_vcs_read_setup_raises_error_if_no_version(\n    provider: Provider, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"poetry.inspection.info.get_pep517_metadata\",\n        return_value=PackageInfo(name=\"demo\", version=None),\n    )\n\n    dependency = VCSDependency(\"demo\", \"git\", \"https://github.com/demo/no-version.git\")\n\n    with pytest.raises(RuntimeError):\n        provider.search_for_direct_origin_dependency(dependency)\n\n\n@pytest.mark.parametrize(\"directory\", [\"demo\", \"non-canonical-name\"])\ndef test_search_for_directory_setup_egg_info(\n    provider: Provider, directory: str, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / directory, tmp_path / \"project\"\n    )\n    dependency = DirectoryDependency(\"demo\", path)\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_directory_setup_egg_info_with_extras(\n    provider: Provider, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\", tmp_path / \"project\"\n    )\n\n    dependency = DirectoryDependency(\"demo\", path, extras=[\"foo\"])\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\n@pytest.mark.parametrize(\"directory\", [\"demo\", \"non-canonical-name\"])\ndef test_search_for_directory_setup_with_base(\n    provider: Provider, directory: str, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / directory, tmp_path / \"project\"\n    )\n\n    dependency = DirectoryDependency(\"demo\", path, base=path)\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n    assert package.root_dir == path\n\n\ndef test_search_for_directory_setup_read_setup(\n    provider: Provider,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=MockEnv())\n\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\", tmp_path / \"project\"\n    )\n    dependency = DirectoryDependency(\"demo\", path)\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_directory_setup_read_setup_with_extras(\n    provider: Provider,\n    mocker: MockerFixture,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    mocker.patch(\"poetry.utils.env.EnvManager.get\", return_value=MockEnv())\n\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\", tmp_path / \"project\"\n    )\n\n    dependency = DirectoryDependency(\"demo\", path, extras=[\"foo\"])\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n\n    required = {r for r in package.requires if not r.is_optional()}\n    optional = {r for r in package.requires if r.is_optional()}\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {get_dependency(\"tomlkit\"), get_dependency(\"cleo\")}\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_directory_setup_read_setup_with_no_dependencies(\n    provider: Provider, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"no-dependencies\",\n        tmp_path / \"project\",\n    )\n\n    dependency = DirectoryDependency(\"demo\", path)\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.2\"\n    assert package.requires == []\n    assert package.extras == {}\n\n\ndef test_search_for_directory_poetry(\n    provider: Provider, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(fixture_dir(\"project_with_extras\"), tmp_path / \"project\")\n\n    dependency = DirectoryDependency(\"project-with-extras\", path)\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"project-with-extras\"\n    assert package.version.text == \"1.2.3\"\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert not required\n    assert optional == {\n        get_dependency(\"cachy\", \">=0.2.0\"),\n        get_dependency(\"pendulum\", \">=1.4.4\"),\n    }\n    extras_a = canonicalize_name(\"extras-a\")\n    extras_b = canonicalize_name(\"extras-b\")\n    assert set(package.extras) == {extras_a, extras_b}\n    assert set(package.extras[extras_a]) == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert set(package.extras[extras_b]) == {get_dependency(\"cachy\", \">=0.2.0\")}\n\n\ndef test_search_for_directory_poetry_with_extras(\n    provider: Provider, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    path = shutil.copytree(fixture_dir(\"project_with_extras\"), tmp_path / \"project\")\n\n    dependency = DirectoryDependency(\"project-with-extras\", path, extras=[\"extras_a\"])\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"project-with-extras\"\n    assert package.version.text == \"1.2.3\"\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert not required\n    assert optional == {\n        get_dependency(\"cachy\", \">=0.2.0\"),\n        get_dependency(\"pendulum\", \">=1.4.4\"),\n    }\n    extras_a = canonicalize_name(\"extras-a\")\n    extras_b = canonicalize_name(\"extras-b\")\n    assert set(package.extras) == {extras_a, extras_b}\n    assert set(package.extras[extras_a]) == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert set(package.extras[extras_b]) == {get_dependency(\"cachy\", \">=0.2.0\")}\n\n\ndef test_search_for_file_sdist(\n    provider: Provider, fixture_dir: FixtureDirGetter\n) -> None:\n    dependency = FileDependency(\n        \"demo\",\n        fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\",\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.0\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.0.tar.gz\",\n            \"hash\": \"sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\",\n            \"size\": 1003,\n        }\n    ]\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {\n        get_dependency(\"cleo\"),\n        get_dependency(\"tomlkit\"),\n    }\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_file_sdist_with_extras(\n    provider: Provider, fixture_dir: FixtureDirGetter\n) -> None:\n    dependency = FileDependency(\n        \"demo\",\n        fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\",\n        extras=[\"foo\"],\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.0\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.0.tar.gz\",\n            \"hash\": \"sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\",\n            \"size\": 1003,\n        }\n    ]\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {\n        get_dependency(\"cleo\"),\n        get_dependency(\"tomlkit\"),\n    }\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_file_wheel(\n    provider: Provider, fixture_dir: FixtureDirGetter\n) -> None:\n    dependency = FileDependency(\n        \"demo\",\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\",\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.0\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n            \"size\": 1116,\n        }\n    ]\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {\n        get_dependency(\"cleo\"),\n        get_dependency(\"tomlkit\"),\n    }\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_search_for_file_wheel_with_extras(\n    provider: Provider, fixture_dir: FixtureDirGetter\n) -> None:\n    dependency = FileDependency(\n        \"demo\",\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\",\n        extras=[\"foo\"],\n    )\n\n    package = provider.search_for_direct_origin_dependency(dependency)\n\n    assert package.name == \"demo\"\n    assert package.version.text == \"0.1.0\"\n    assert package.files == [\n        {\n            \"file\": \"demo-0.1.0-py2.py3-none-any.whl\",\n            \"hash\": \"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n            \"size\": 1116,\n        }\n    ]\n\n    required = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()\n    }\n    optional = {\n        r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()\n    }\n    assert required == {get_dependency(\"pendulum\", \">=1.4.4\")}\n    assert optional == {\n        get_dependency(\"cleo\"),\n        get_dependency(\"tomlkit\"),\n    }\n    assert package.extras == {\n        \"foo\": [get_dependency(\"cleo\")],\n        \"bar\": [get_dependency(\"tomlkit\")],\n    }\n\n\ndef test_complete_package_merges_same_source_and_no_source(\n    provider: Provider, root: ProjectPackage\n) -> None:\n    foo_no_source_1 = get_dependency(\"foo\", \">=1\")\n    foo_source_1 = get_dependency(\"foo\", \"!=1.1.*\")\n    foo_source_1.source_name = \"source\"\n    foo_source_2 = get_dependency(\"foo\", \"!=1.2.*\")\n    foo_source_2.source_name = \"source\"\n    foo_no_source_2 = get_dependency(\"foo\", \"<2\")\n\n    root.add_dependency(foo_no_source_1)\n    root.add_dependency(foo_source_1)\n    root.add_dependency(foo_source_2)\n    root.add_dependency(foo_no_source_2)\n\n    complete_package = provider.complete_package(\n        DependencyPackage(root.to_dependency(), root)\n    )\n\n    requires = complete_package.package.all_requires\n    assert len(requires) == 1\n    assert requires[0].source_name == \"source\"\n    assert str(requires[0].constraint) in {\n        \">=1,<1.1 || >=1.3,<2\",\n        \">=1,<1.1.dev0 || >=1.3.dev0,<2\",\n        \">=1,<1.1.0 || >=1.3.0,<2\",\n        \">=1,<1.1.0.dev0 || >=1.3.0.dev0,<2\",\n    }\n\n\ndef test_complete_package_does_not_merge_different_source_names(\n    provider: Provider, root: ProjectPackage\n) -> None:\n    foo_source_1 = get_dependency(\"foo\")\n    foo_source_1.source_name = \"source_1\"\n    foo_source_2 = get_dependency(\"foo\")\n    foo_source_2.source_name = \"source_2\"\n\n    root.add_dependency(foo_source_1)\n    root.add_dependency(foo_source_2)\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    expected = \"\"\"\\\nIncompatible constraints in requirements of root (1.2.3):\nfoo ; source=source_2\nfoo ; source=source_1\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_complete_package_merges_same_source_type_and_no_source(\n    provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter\n) -> None:\n    project_dir = fixture_dir(\"with_conditional_path_deps\")\n    path = (project_dir / \"demo_one\").as_posix()\n\n    root.add_dependency(Factory.create_dependency(\"demo\", \">=1.0\"))\n    root.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n    root.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))  # duplicate\n    root.add_dependency(Factory.create_dependency(\"demo\", \"<2.0\"))\n\n    complete_package = provider.complete_package(\n        DependencyPackage(root.to_dependency(), root)\n    )\n\n    requires = complete_package.package.all_requires\n    assert len(requires) == 1\n    assert requires[0].source_url == path\n    assert str(requires[0].constraint) == \"1.2.3\"\n\n\ndef test_complete_package_does_not_merge_different_source_types(\n    provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter\n) -> None:\n    project_dir = fixture_dir(\"with_conditional_path_deps\")\n    for folder in [\"demo_one\", \"demo_two\"]:\n        path = (project_dir / folder).as_posix()\n        root.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    expected = f\"\"\"\\\nIncompatible constraints in requirements of root (1.2.3):\ndemo @ {project_dir.as_uri()}/demo_two (1.2.3)\ndemo @ {project_dir.as_uri()}/demo_one (1.2.3)\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_complete_package_does_not_merge_different_source_type_and_name(\n    provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter\n) -> None:\n    project_dir = fixture_dir(\"with_conditional_path_deps\")\n    path = (project_dir / \"demo_one\").as_posix()\n\n    dep_with_source_name = Factory.create_dependency(\"demo\", \">=1.0\")\n    dep_with_source_name.source_name = \"source\"\n    root.add_dependency(dep_with_source_name)\n    root.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    expected = f\"\"\"\\\nIncompatible constraints in requirements of root (1.2.3):\ndemo @ {project_dir.as_uri()}/demo_one (1.2.3)\ndemo (>=1.0) ; source=source\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_complete_package_does_not_merge_different_subdirectories(\n    provider: Provider, root: ProjectPackage\n) -> None:\n    dependency_one = Factory.create_dependency(\n        \"one\",\n        {\n            \"git\": \"https://github.com/demo/subdirectories.git\",\n            \"subdirectory\": \"one\",\n        },\n    )\n    dependency_one_copy = Factory.create_dependency(\n        \"one\",\n        {\n            \"git\": \"https://github.com/demo/subdirectories.git\",\n            \"subdirectory\": \"one-copy\",\n        },\n    )\n\n    root.add_dependency(dependency_one)\n    root.add_dependency(dependency_one_copy)\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    expected = \"\"\"\\\nIncompatible constraints in requirements of root (1.2.3):\none @ git+https://github.com/demo/subdirectories.git#subdirectory=one-copy (1.0.0)\none @ git+https://github.com/demo/subdirectories.git#subdirectory=one (1.0.0)\"\"\"\n\n    assert str(e.value) == expected\n\n\n@pytest.mark.parametrize(\"source_name\", [None, \"repo\"])\ndef test_complete_package_with_extras_preserves_source_name(\n    provider: Provider, repository: Repository, source_name: str | None\n) -> None:\n    package_a = Package(\"A\", \"1.0\")\n    package_b = Package(\"B\", \"1.0\")\n    dep = get_dependency(\"B\", \"^1.0\", optional=True)\n    package_a.add_dependency(dep)\n    package_a.extras = {canonicalize_name(\"foo\"): [dep]}\n    repository.add_package(package_a)\n    repository.add_package(package_b)\n\n    dependency = Dependency(\"A\", \"1.0\", extras=[\"foo\"])\n    if source_name:\n        dependency.source_name = source_name\n\n    complete_package = provider.complete_package(\n        DependencyPackage(dependency, package_a)\n    )\n\n    requires = complete_package.package.all_requires\n    assert len(requires) == 2\n    assert requires[0].name == \"a\"\n    assert requires[0].source_name == source_name\n    assert requires[1].name == \"b\"\n    assert requires[1].source_name is None\n\n\n@pytest.mark.parametrize(\"with_extra\", [False, True])\ndef test_complete_package_fetches_optional_vcs_dependency_only_if_requested(\n    provider: Provider, repository: Repository, mocker: MockerFixture, with_extra: bool\n) -> None:\n    optional_vcs_dependency = Factory.create_dependency(\n        \"demo\", {\"git\": \"https://github.com/demo/demo.git\", \"optional\": True}\n    )\n    package = Package(\"A\", \"1.0\", features=[\"foo\"] if with_extra else [])\n    package.add_dependency(optional_vcs_dependency)\n    package.extras = {canonicalize_name(\"foo\"): [optional_vcs_dependency]}\n    repository.add_package(package)\n\n    spy = mocker.spy(provider, \"_search_for_vcs\")\n\n    provider.complete_package(DependencyPackage(package.to_dependency(), package))\n\n    if with_extra:\n        spy.assert_called()\n    else:\n        spy.assert_not_called()\n\n\ndef test_complete_package_finds_locked_package_in_explicit_source(\n    root: ProjectPackage, pool: RepositoryPool\n) -> None:\n    package = Package(\"a\", \"1.0\", source_reference=\"explicit\")\n    explicit_repo = Repository(\"explicit\")\n    explicit_repo.add_package(package)\n    pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)\n\n    root_dependency = get_dependency(\"a\", \">0\")\n    root_dependency.source_name = \"explicit\"\n    root.add_dependency(root_dependency)\n    locked_package = Package(\"a\", \"1.0\", source_reference=\"explicit\")\n    provider = Provider(root, pool, NullIO(), locked=[locked_package])\n    provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    # transitive dependency without explicit source\n    dependency = get_dependency(\"a\", \">=1\")\n\n    locked = provider.get_locked(dependency)\n    assert locked is not None\n    provider.complete_package(locked)  # must not fail\n\n\ndef test_complete_package_finds_locked_package_in_other_source(\n    root: ProjectPackage, repository: Repository, pool: RepositoryPool\n) -> None:\n    package = Package(\"a\", \"1.0\")\n    repository.add_package(package)\n    explicit_repo = Repository(\"explicit\")\n    pool.add_repository(explicit_repo)\n\n    root_dependency = get_dependency(\"a\", \">0\")  # no explicit source\n    root.add_dependency(root_dependency)\n    locked_package = Package(\"a\", \"1.0\", source_reference=\"explicit\")  # explicit source\n    provider = Provider(root, pool, NullIO(), locked=[locked_package])\n    provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    # transitive dependency without explicit source\n    dependency = get_dependency(\"a\", \">=1\")\n\n    locked = provider.get_locked(dependency)\n    assert locked is not None\n    provider.complete_package(locked)  # must not fail\n\n\ndef test_complete_package_raises_packagenotfound_if_locked_source_not_available(\n    root: ProjectPackage, pool: RepositoryPool, provider: Provider\n) -> None:\n    locked_package = Package(\"a\", \"1.0\", source_reference=\"outdated\")\n    provider = Provider(root, pool, NullIO(), locked=[locked_package])\n    provider.complete_package(DependencyPackage(root.to_dependency(), root))\n\n    # transitive dependency without explicit source\n    dependency = get_dependency(\"a\", \">=1\")\n\n    locked = provider.get_locked(dependency)\n    assert locked is not None\n    with pytest.raises(PackageNotFoundError):\n        provider.complete_package(locked)\n\n\ndef test_complete_package_outdated_cache(\n    root: ProjectPackage,\n    release_info: PackageInfo,\n    outdated_release_info: PackageInfo,\n    mocker: MockerFixture,\n) -> None:\n    repo = MockCachedRepository(\"repo\")\n    pool = RepositoryPool()\n    pool.add_repository(repo)\n    provider = Provider(root, pool, NullIO())\n    pool_refresh_spy = mocker.spy(provider.pool, \"refresh\")\n\n    assert release_info.name is not None\n    assert release_info.version is not None\n    package = Package(release_info.name, release_info.version)\n    package.files = release_info.files  # up-to-date files from lock\n\n    # trigger caching of outdated info\n    repo._get_release_info = lambda name, version: outdated_release_info.asdict()  # type: ignore[method-assign]\n    assert len(repo.package(package.name, package.version).files) == 1\n\n    # additional files uploaded -> refresh needed\n    repo._get_release_info = lambda name, version: release_info.asdict()  # type: ignore[method-assign]\n    complete_package = provider.complete_package(\n        DependencyPackage(package.to_dependency(), package)\n    )\n    assert len(complete_package.package.files) == 2\n    assert pool_refresh_spy.call_count == 1\n\n    # cache should have been updated -> no additional refresh needed\n    complete_package = provider.complete_package(\n        DependencyPackage(package.to_dependency(), package)\n    )\n    assert len(complete_package.package.files) == 2\n    assert pool_refresh_spy.call_count == 1\n\n\ndef test_complete_package_no_refresh_on_url_size_upload_info(\n    root: ProjectPackage,\n    release_info: PackageInfo,\n    release_info_complete: PackageInfo,\n    mocker: MockerFixture,\n) -> None:\n    repo = MockCachedRepository(\"repo\")\n    pool = RepositoryPool()\n    pool.add_repository(repo)\n    provider = Provider(root, pool, NullIO())\n    pool_refresh_spy = mocker.spy(provider.pool, \"refresh\")\n\n    assert release_info.name is not None\n    assert release_info.version is not None\n    package = Package(release_info.name, release_info.version)\n    package.files = release_info.files  # up-to-date files from lock\n    assert \"url\" not in package.files[0]\n\n    # trigger caching complete info\n    repo._get_release_info = lambda name, version: release_info_complete.asdict()  # type: ignore[method-assign]\n    assert len(repo.package(package.name, package.version).files) == 2\n\n    # Only additional information in cache, names and hashes from lock file are the same\n    # -> no refresh needed\n    complete_package = provider.complete_package(\n        DependencyPackage(package.to_dependency(), package)\n    )\n    assert len(complete_package.package.files) == 2\n    assert \"url\" in complete_package.package.files[0]\n    assert pool_refresh_spy.call_count == 0\n\n\ndef test_source_dependency_is_satisfied_by_direct_origin(\n    provider: Provider, repository: Repository\n) -> None:\n    direct_origin_package = Package(\"foo\", \"1.1\", source_type=\"url\")\n    repository.add_package(Package(\"foo\", \"1.0\"))\n    provider._direct_origin_packages = {\"foo\": direct_origin_package}\n    dep = Dependency(\"foo\", \">=1\")\n\n    assert provider.search_for(dep) == [direct_origin_package]\n\n\ndef test_explicit_source_dependency_is_not_satisfied_by_direct_origin(\n    provider: Provider, repository: Repository\n) -> None:\n    repo_package = Package(\"foo\", \"1.0\")\n    repository.add_package(repo_package)\n    provider._direct_origin_packages = {\"foo\": Package(\"foo\", \"1.1\", source_type=\"url\")}\n    dep = Dependency(\"foo\", \">=1\")\n    dep.source_name = repository.name\n\n    assert provider.search_for(dep) == [repo_package]\n\n\ndef test_source_dependency_is_not_satisfied_by_incompatible_direct_origin(\n    provider: Provider, repository: Repository\n) -> None:\n    repo_package = Package(\"foo\", \"2.0\")\n    repository.add_package(repo_package)\n    provider._direct_origin_packages = {\"foo\": Package(\"foo\", \"1.0\", source_type=\"url\")}\n    dep = Dependency(\"foo\", \">=2\")\n    dep.source_name = repository.name\n\n    assert provider.search_for(dep) == [repo_package]\n"
  },
  {
    "path": "tests/puzzle/test_solver.py",
    "content": "from __future__ import annotations\n\nimport re\nimport shutil\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Literal\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.dependency_group import DependencyGroup\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.vcs_dependency import VCSDependency\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.factory import Factory\nfrom poetry.installation.operations import Update\nfrom poetry.packages import DependencyPackage\nfrom poetry.puzzle import Solver\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.puzzle.provider import IncompatibleConstraintsError\nfrom poetry.repositories.repository import Repository\nfrom poetry.repositories.repository_pool import Priority\nfrom poetry.repositories.repository_pool import RepositoryPool\nfrom poetry.utils.env import MockEnv\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\nfrom tests.helpers import get_dependency\nfrom tests.helpers import get_package\n\n\nif TYPE_CHECKING:\n    import responses\n\n    from cleo.io.null_io import NullIO\n    from poetry.core.packages.project_package import ProjectPackage\n    from pytest_mock import MockerFixture\n\n    from poetry.installation.operations.operation import Operation\n    from poetry.puzzle.provider import Provider\n    from poetry.puzzle.transaction import Transaction\n    from poetry.repositories.legacy_repository import LegacyRepository\n    from poetry.repositories.pypi_repository import PyPiRepository\n    from tests.types import FixtureDirGetter\n    from tests.types import PackageFactory\n\nDEFAULT_SOURCE_REF = (\n    VCSDependency(\"poetry\", \"git\", \"git@github.com:python-poetry/poetry.git\").branch\n    or \"HEAD\"\n)\n\n\n@pytest.fixture\ndef legacy_repository(legacy_repository_html: LegacyRepository) -> LegacyRepository:\n    \"\"\"\n    Override fixture to only test with the html version of the legacy repository\n    because the json version has the same packages as the PyPI repository and thus\n    cause different results in the tests that rely on differences.\n    \"\"\"\n    return legacy_repository_html\n\n\ndef set_package_python_versions(provider: Provider, python_versions: str) -> None:\n    provider._package.python_versions = python_versions\n    provider._package_python_constraint = provider._package.python_constraint\n\n\ndef check_solver_result(\n    transaction: Transaction,\n    expected: list[dict[str, Any]],\n    synchronize: bool = False,\n) -> list[Operation]:\n    for e in expected:\n        if \"skipped\" not in e:\n            e[\"skipped\"] = False\n\n    result = []\n    ops = transaction.calculate_operations(synchronize=synchronize)\n    for op in ops:\n        if op.job_type == \"update\":\n            assert isinstance(op, Update)\n            result.append(\n                {\n                    \"job\": \"update\",\n                    \"from\": op.initial_package,\n                    \"to\": op.target_package,\n                    \"skipped\": op.skipped,\n                }\n            )\n        else:\n            job = \"install\"\n            if op.job_type == \"uninstall\":\n                job = \"remove\"\n\n            result.append({\"job\": job, \"package\": op.package, \"skipped\": op.skipped})\n\n    assert result == expected\n\n    return ops\n\n\ndef test_solver_install_single(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    repo.add_package(package_a)\n\n    transaction = solver.solve([get_dependency(\"A\").name])\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": package_a}])\n\n\ndef test_solver_remove_if_no_longer_locked(\n    package: ProjectPackage, pool: RepositoryPool, io: NullIO\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n\n    solver = Solver(package, pool, [package_a], [package_a], io)\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"remove\", \"package\": package_a}])\n\n\ndef test_remove_non_installed(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    repo.add_package(package_a)\n\n    solver = Solver(package, pool, [], [package_a], io)\n    transaction = solver.solve([])\n\n    check_solver_result(transaction, [])\n\n\ndef test_install_non_existing_package_fail(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"B\", \"1\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    repo.add_package(package_a)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_install_unpublished_package_fails(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"B\", \"1\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \"1.0\"))\n\n    repo.add_package(package_a)\n\n    # Even though B is installed, it is unpublished and cannot be used during solving.\n    solver = Solver(package, pool, [package_b], [], io)\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_with_deps(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    new_package_b = get_package(\"B\", \"1.1\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(new_package_b)\n\n    package_a.add_dependency(get_dependency(\"B\", \"<1.1\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_install_honours_not_equal(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    new_package_b11 = get_package(\"B\", \"1.1\")\n    new_package_b12 = get_package(\"B\", \"1.2\")\n    new_package_b13 = get_package(\"B\", \"1.3\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(new_package_b11)\n    repo.add_package(new_package_b12)\n    repo.add_package(new_package_b13)\n\n    package_a.add_dependency(get_dependency(\"B\", \"<=1.3,!=1.3,!=1.2\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": new_package_b11},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_install_with_deps_in_order(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    package_b.add_dependency(get_dependency(\"A\", \">=1.0\"))\n    package_b.add_dependency(get_dependency(\"C\", \">=1.0\"))\n\n    package_c.add_dependency(get_dependency(\"A\", \">=1.0\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_install_installed(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    repo.add_package(package_a)\n\n    solver = Solver(package, pool, [package_a], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction, [{\"job\": \"install\", \"package\": package_a, \"skipped\": True}]\n    )\n\n\ndef test_update_installed(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    new_package_a = get_package(\"A\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(new_package_a)\n\n    solver = Solver(package, pool, [get_package(\"A\", \"1.0\")], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction, [{\"job\": \"update\", \"from\": package_a, \"to\": new_package_a}]\n    )\n\n\ndef test_update_with_use_latest(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    new_package_a = get_package(\"A\", \"1.1\")\n    package_b = get_package(\"B\", \"1.0\")\n    new_package_b = get_package(\"B\", \"1.1\")\n    repo.add_package(package_a)\n    repo.add_package(new_package_a)\n    repo.add_package(package_b)\n    repo.add_package(new_package_b)\n\n    installed = [get_package(\"A\", \"1.0\")]\n    locked = [package_a, package_b]\n\n    solver = Solver(package, pool, installed, locked, io)\n    transaction = solver.solve(use_latest=[package_b.name])\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a, \"skipped\": True},\n            {\"job\": \"install\", \"package\": new_package_b},\n        ],\n    )\n\n\ndef test_solver_sets_groups(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\", groups=[\"dev\"]))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"~1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    _ = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_respects_root_package_python_versions(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~3.4\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.python_versions = \"^3.3\"\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.python_versions = \"^3.4\"\n    package_c11 = get_package(\"C\", \"1.1\")\n    package_c11.python_versions = \"^3.6\"\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_c11)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_fails_if_mismatch_root_python_versions(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.4\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.python_versions = \"^3.6\"\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.python_versions = \"~3.3\"\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"~1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_ignores_python_restricted_if_mismatch_root_package_python_versions(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~3.8\")\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"1.0\", \"python\": \"<3.8\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"1.0\", \"markers\": \"python_version < '3.8'\"}\n        )\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [])\n\n\ndef test_solver_solves_optional_and_compatible_packages(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~3.4\")\n    package.extras = {canonicalize_name(\"foo\"): [get_dependency(\"B\")]}\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"python\": \"^3.4\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"optional\": True})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.python_versions = \"^3.3\"\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.python_versions = \"^3.4\"\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_does_not_return_extras_if_not_requested(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    package_b.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\", \"^1.0\")]}\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_returns_extras_if_requested(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"extras\": [\"foo\"]})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    dep = get_dependency(\"C\", \"^1.0\", optional=True)\n    dep.marker = parse_marker(\"extra == 'foo'\")\n    package_b.extras = {canonicalize_name(\"foo\"): [dep]}\n    package_b.add_dependency(dep)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n    assert ops[-1].package.marker.is_any()\n    assert ops[0].package.marker.is_any()\n\n\n@pytest.mark.parametrize(\"num_groups\", [0, 1, 2])\ndef test_solver_returns_extras_if_requested_in_multiple_groups(\n    solver: Solver, repo: Repository, package: ProjectPackage, num_groups: int\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    if num_groups:\n        package.add_dependency(\n            Factory.create_dependency(\n                \"B\",\n                {\"version\": \"*\", \"extras\": [\"foo\"]},\n                groups=[f\"group{i}\" for i in range(num_groups)],\n            )\n        )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    dep = get_dependency(\"C\", \"^1.0\", optional=True)\n    dep.marker = parse_marker(\"extra == 'foo'\")\n    package_b.extras = {canonicalize_name(\"foo\"): [dep]}\n    package_b.add_dependency(dep)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    expected = [\n        {\"job\": \"install\", \"package\": package_a},\n        {\"job\": \"install\", \"package\": package_b},\n    ]\n    if num_groups:\n        expected = [{\"job\": \"install\", \"package\": package_c}, *expected]\n\n    ops = check_solver_result(transaction, expected)\n\n    assert ops[-1].package.marker.is_any()\n    assert ops[0].package.marker.is_any()\n\n\n@pytest.mark.parametrize(\n    (\"enabled_extras\", \"expected_packages\"),\n    [\n        ([], [\"a\"]),\n        ([\"all\"], [\"download-package\", \"install-package\", \"a\"]),\n        ([\"nested\"], [\"download-package\", \"install-package\", \"a\"]),\n        ([\"cyclic\"], [\"download-package\", \"install-package\", \"a\"]),\n        ([\"install\", \"download\"], [\"download-package\", \"install-package\", \"a\"]),\n        ([\"install\"], [\"install-package\", \"a\"]),\n        ([\"download\"], [\"download-package\", \"a\"]),\n        # test to ensure target extra dependencies with markers are respected\n        ([\"py\"], [\"py310-package\", \"a\"]),\n    ],\n)\n@pytest.mark.parametrize(\"merge_extras\", [True, False])\n@pytest.mark.parametrize(\"top_level_dependency\", [True, False])\ndef test_solver_resolves_self_referential_extras(\n    enabled_extras: list[str],\n    expected_packages: list[Literal[\"a\", \"b\", \"download-package\", \"install-package\"]],\n    top_level_dependency: bool,\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    create_package: PackageFactory,\n    merge_extras: bool,\n) -> None:\n    dependency = (\n        create_package(\n            \"A\",\n            str(package.version),\n            extras={\n                \"download\": [\"download-package\"],\n                \"download2\": [\"download-package\"],  # same package as download\n                \"install\": [\"install-package\"],\n                \"py38\": [\"py38-package ; python_version == '3.8'\"],\n                \"py310\": [\"py310-package ; python_version > '3.8'\"],\n                \"all\": [\"a[download,download2,install]\"],\n                \"py\": [\"a[py38,py310]\"],\n                \"nested\": [\"a[all]\"],\n                \"cyclic\": [\"a[cyclic2]\", \"download-package\"],\n                \"cyclic2\": [\"a[cyclic]\", \"install-package\"],\n            },\n            merge_extras=merge_extras,\n        )\n        .to_dependency()\n        .with_features(enabled_extras)\n    )\n\n    if not top_level_dependency:\n        dependency = create_package(\n            \"B\", \"1.0\", dependencies=[dependency]\n        ).to_dependency()\n        # we do not use append() here to avoid flaky tests\n        expected_packages = [*expected_packages, \"b\"]\n\n    package.add_dependency(dependency)\n\n    # Solving the dependency graph\n    with solver.use_environment(MockEnv((3, 10, 0))):\n        transaction = solver.solve()\n\n    # Verifying the results\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": repo.package(name, package.version)}\n            for name in expected_packages\n        ],\n    )\n\n\ndef test_solver_resolves_self_referential_extras_markers(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    create_package: PackageFactory,\n) -> None:\n    package.python_versions = \".\".join([str(i) for i in sys.version_info[:3]])\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"all\"]})\n    )\n\n    create_package(\n        \"A\",\n        str(package.version),\n        extras={\n            \"download\": [\"download-package\"],\n            \"install\": [\"install-package\"],\n            \"all\": [\"a[download,install] ; python_version < '3.9'\"],\n        },\n    )\n\n    # Solving the dependency graph\n    with solver.use_environment(MockEnv((3, 10, 0))):\n        transaction = solver.solve()\n\n    # Verifying the results\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": repo.package(name, package.version)}\n            # FIXME: At the time of writing this test case, the markers from self-ref extras are not\n            #  correctly propagated into the dependency specs. For example, given this case,\n            #  the package \"install-package\" should have a final marker of\n            #  \"extra == 'install' or extra == 'all' and python_version < '3.9'\".\n            #  Once fixed, this should only install package \"a\".\n            for name in [\"download-package\", \"install-package\", \"a\"]\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"enabled_extra\", [\"one\", \"two\", None])\ndef test_solver_returns_extras_only_requested(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    enabled_extra: str | None,\n) -> None:\n    extras = [enabled_extra] if enabled_extra is not None else []\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"extras\": extras})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c10 = get_package(\"C\", \"1.0\")\n    package_c20 = get_package(\"C\", \"2.0\")\n\n    dep10 = get_dependency(\"C\", \"1.0\", optional=True)\n    dep10._in_extras = [canonicalize_name(\"one\")]\n    dep10.marker = parse_marker(\"extra == 'one'\")\n\n    dep20 = get_dependency(\"C\", \"2.0\", optional=True)\n    dep20._in_extras = [canonicalize_name(\"two\")]\n    dep20.marker = parse_marker(\"extra == 'two'\")\n\n    package_b.extras = {\n        canonicalize_name(\"one\"): [dep10],\n        canonicalize_name(\"two\"): [dep20],\n    }\n\n    package_b.add_dependency(dep10)\n    package_b.add_dependency(dep20)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c10)\n    repo.add_package(package_c20)\n\n    transaction = solver.solve()\n\n    expected = [\n        {\"job\": \"install\", \"package\": package_a},\n        {\"job\": \"install\", \"package\": package_b},\n    ]\n\n    if enabled_extra is not None:\n        expected.insert(\n            0,\n            {\n                \"job\": \"install\",\n                \"package\": package_c10 if enabled_extra == \"one\" else package_c20,\n            },\n        )\n\n    ops = check_solver_result(\n        transaction,\n        expected,\n    )\n\n    assert ops[-1].package.marker.is_any()\n    assert ops[0].package.marker.is_any()\n\n\n@pytest.mark.parametrize(\"enabled_extra\", [\"one\", \"two\", None])\ndef test_solver_returns_extras_when_multiple_extras_use_same_dependency(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    enabled_extra: bool | None,\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    dep = get_dependency(\"C\", \"*\", optional=True)\n    dep._in_extras = [canonicalize_name(\"one\"), canonicalize_name(\"two\")]\n\n    package_b.extras = {\n        canonicalize_name(\"one\"): [dep],\n        canonicalize_name(\"two\"): [dep],\n    }\n\n    package_b.add_dependency(dep)\n\n    extras = [enabled_extra] if enabled_extra is not None else []\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"extras\": extras})\n    )\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    expected = [\n        {\"job\": \"install\", \"package\": package_b},\n        {\"job\": \"install\", \"package\": package_a},\n    ]\n\n    if enabled_extra is not None:\n        expected.insert(0, {\"job\": \"install\", \"package\": package_c})\n\n    ops = check_solver_result(\n        transaction,\n        expected,\n    )\n\n    assert ops[-1].package.marker.is_any()\n    assert ops[0].package.marker.is_any()\n\n\ndef test_solver_locks_all_extras_when_multiple_extras_require_same_dependency(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n) -> None:\n    \"\"\"\n    - root depends on A[extra-b1] and C\n    - C depends on A[extra-b2]\n    - B is required by both extras\n    -> the locked dependency A on B must have both extra markers\n    \"\"\"\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n\n    dep_b1 = get_dependency(\"B\", \"*\", optional=True)\n    dep_b1.marker = parse_marker(\"extra == 'extra-b1'\")\n\n    dep_b2 = get_dependency(\"B\", \"*\", optional=True)\n    dep_b2.marker = parse_marker(\"extra == 'extra-b2'\")\n\n    package_a.extras = {\n        canonicalize_name(\"extra-b1\"): [dep_b1],\n        canonicalize_name(\"extra-b2\"): [dep_b2],\n    }\n    package_a.add_dependency(dep_b1)\n    package_a.add_dependency(dep_b2)\n\n    package.add_dependency(\n        get_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"extra-b1\"]})\n    )\n    package.add_dependency(get_dependency(\"C\", \"*\"))\n    package_c.add_dependency(\n        get_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"extra-b2\"]})\n    )\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    expected = [\n        {\"job\": \"install\", \"package\": package_b},\n        {\"job\": \"install\", \"package\": package_a},\n        {\"job\": \"install\", \"package\": package_c},\n    ]\n\n    ops = check_solver_result(transaction, expected)\n    locked_a_requires = ops[1].package.requires\n    assert len(locked_a_requires) == 2\n    assert {str(r.marker) for r in locked_a_requires} == {\n        'extra == \"extra-b1\"',\n        'extra == \"extra-b2\"',\n    }\n\n\n@pytest.mark.parametrize(\"enabled_extra\", [\"one\", \"two\", None])\ndef test_solver_returns_extras_only_requested_nested(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    enabled_extra: str | None,\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c10 = get_package(\"C\", \"1.0\")\n    package_c20 = get_package(\"C\", \"2.0\")\n\n    dep10 = get_dependency(\"C\", \"1.0\", optional=True)\n    dep10._in_extras = [canonicalize_name(\"one\")]\n    dep10.marker = parse_marker(\"extra == 'one'\")\n\n    dep20 = get_dependency(\"C\", \"2.0\", optional=True)\n    dep20._in_extras = [canonicalize_name(\"two\")]\n    dep20.marker = parse_marker(\"extra == 'two'\")\n\n    package_b.extras = {\n        canonicalize_name(\"one\"): [dep10],\n        canonicalize_name(\"two\"): [dep20],\n    }\n\n    package_b.add_dependency(dep10)\n    package_b.add_dependency(dep20)\n\n    extras = [enabled_extra] if enabled_extra is not None else []\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"extras\": extras})\n    )\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c10)\n    repo.add_package(package_c20)\n\n    transaction = solver.solve()\n\n    expected = [\n        {\"job\": \"install\", \"package\": package_b},\n        {\"job\": \"install\", \"package\": package_a},\n    ]\n\n    if enabled_extra is not None:\n        expected.insert(\n            0,\n            {\n                \"job\": \"install\",\n                \"package\": package_c10 if enabled_extra == \"one\" else package_c20,\n            },\n        )\n\n    ops = check_solver_result(transaction, expected)\n\n    assert ops[-1].package.marker.is_any()\n    assert ops[0].package.marker.is_any()\n\n\ndef test_solver_finds_extras_next_to_non_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    # Root depends on A[foo]\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"foo\"]})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_d = get_package(\"D\", \"1.0\")\n\n    # A depends on B; A[foo] depends on B[bar].\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"*\", \"extras\": [\"bar\"], \"markers\": \"extra == 'foo'\"}\n        )\n    )\n    package_a.extras = {canonicalize_name(\"foo\"): [get_dependency(\"B\", \"*\")]}\n\n    # B depends on C; B[bar] depends on D.\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n    package_b.add_dependency(\n        Factory.create_dependency(\"D\", {\"version\": \"*\", \"markers\": 'extra == \"bar\"'})\n    )\n    package_b.extras = {canonicalize_name(\"bar\"): [get_dependency(\"D\", \"*\")]}\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_merge_extras_into_base_package_multiple_repos_fixes_5727(\n    solver: Solver, repo: Repository, pool: RepositoryPool, package: ProjectPackage\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"source\": \"legacy\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"B\", {\"version\": \"*\"}))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.extras = {canonicalize_name(\"foo\"): []}\n\n    repo.add_package(package_a)\n\n    package_b = Package(\"B\", \"1.0\", source_type=\"legacy\")\n    package_b.add_dependency(package_a.with_features([\"foo\"]).to_dependency())\n\n    package_a = Package(\"A\", \"1.0\", source_type=\"legacy\", source_reference=\"legacy\")\n    package_a.extras = {canonicalize_name(\"foo\"): []}\n\n    repo = Repository(\"legacy\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    pool.add_repository(repo)\n\n    transaction = solver.solve()\n\n    ops = transaction.calculate_operations(synchronize=True)\n\n    assert len(ops[0].package.requires) == 0, \"a should not require itself\"\n\n\ndef test_solver_returns_extras_if_excluded_by_markers_without_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"foo\"]})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n\n    # mandatory dependency with marker\n    dep = get_dependency(\"B\", \"^1.0\")\n    dep.marker = parse_marker(\"sys_platform != 'linux'\")\n    package_a.add_dependency(dep)\n\n    # optional dependency with same constraint and no marker except for extra\n    dep = get_dependency(\"B\", \"^1.0\", optional=True)\n    dep.marker = parse_marker(\"extra == 'foo'\")\n    package_a.extras = {canonicalize_name(\"foo\"): [dep]}\n    package_a.add_dependency(dep)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n    assert (\n        str(ops[1].package.requires[0].marker)\n        == 'sys_platform != \"linux\" or extra == \"foo\"'\n    )\n\n\ndef test_solver_returns_prereleases_if_requested(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"*\", \"allow-prereleases\": True})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_c_dev = get_package(\"C\", \"1.1-beta.1\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_c_dev)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_c_dev},\n        ],\n    )\n\n\ndef test_solver_does_not_return_prereleases_if_not_requested(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_c_dev = get_package(\"C\", \"1.1-beta.1\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_c_dev)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_c},\n        ],\n    )\n\n\ndef test_solver_sub_dependencies_with_requirements(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_d = get_package(\"D\", \"1.0\")\n\n    package_c.add_dependency(\n        Factory.create_dependency(\"D\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_a.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n    package_b.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n    op = ops[1]\n    assert op.package.marker.is_any()\n\n\ndef test_solver_sub_dependencies_with_requirements_complex(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"<5.0\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<5.0\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_d = get_package(\"D\", \"1.0\")\n    package_e = get_package(\"E\", \"1.0\")\n    package_f = get_package(\"F\", \"1.0\")\n\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"D\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_b.add_dependency(\n        Factory.create_dependency(\"E\", {\"version\": \"^1.0\", \"platform\": \"win32\"})\n    )\n    package_b.add_dependency(\n        Factory.create_dependency(\"F\", {\"version\": \"^1.0\", \"python\": \"<5.0\"})\n    )\n    package_c.add_dependency(\n        Factory.create_dependency(\"F\", {\"version\": \"^1.0\", \"python\": \"<4.0\"})\n    )\n    package_d.add_dependency(Factory.create_dependency(\"F\", \"*\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n    repo.add_package(package_e)\n    repo.add_package(package_f)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_e},\n            {\"job\": \"install\", \"package\": package_f},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_c},\n        ],\n    )\n\n\ndef test_solver_sub_dependencies_with_not_supported_python_version(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.5\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.python_versions = \"<2.0\"\n\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<2.0\"})\n    )\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": package_a}])\n\n\ndef test_solver_sub_dependencies_with_not_supported_python_version_transitive(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.4\")\n\n    package.add_dependency(\n        Factory.create_dependency(\"httpx\", {\"version\": \"^0.17.1\", \"python\": \"^3.6\"})\n    )\n\n    httpx = get_package(\"httpx\", \"0.17.1\")\n    httpx.python_versions = \">=3.6\"\n\n    httpcore = get_package(\"httpcore\", \"0.12.3\")\n    httpcore.python_versions = \">=3.6\"\n\n    sniffio_1_1_0 = get_package(\"sniffio\", \"1.1.0\")\n    sniffio_1_1_0.python_versions = \">=3.5\"\n\n    sniffio = get_package(\"sniffio\", \"1.2.0\")\n    sniffio.python_versions = \">=3.5\"\n\n    httpx.add_dependency(\n        Factory.create_dependency(\"httpcore\", {\"version\": \">=0.12.1,<0.13\"})\n    )\n    httpx.add_dependency(Factory.create_dependency(\"sniffio\", {\"version\": \"*\"}))\n    httpcore.add_dependency(Factory.create_dependency(\"sniffio\", {\"version\": \"==1.*\"}))\n\n    repo.add_package(httpx)\n    repo.add_package(httpcore)\n    repo.add_package(sniffio)\n    repo.add_package(sniffio_1_1_0)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": sniffio, \"skipped\": False},\n            {\"job\": \"install\", \"package\": httpcore, \"skipped\": False},\n            {\"job\": \"install\", \"package\": httpx, \"skipped\": False},\n        ],\n    )\n\n\ndef test_solver_with_dependency_in_both_main_and_dev_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.5\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"*\", \"extras\": [\"foo\"]}, groups=[\"dev\"]\n        )\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\")]}\n    package_a.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    )\n    package_a.add_dependency(Factory.create_dependency(\"B\", {\"version\": \"^1.0\"}))\n\n    package_b = get_package(\"B\", \"1.0\")\n\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    package_d = get_package(\"D\", \"1.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    transaction = solver.solve()\n\n    _ = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_dependent(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"E\", \"*\"))\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"*\", \"extras\": [\"foo\"]}, groups=[\"dev\"]\n        )\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.extras = {canonicalize_name(\"foo\"): [get_dependency(\"C\")]}\n    package_a.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"optional\": True})\n    )\n    package_a.add_dependency(Factory.create_dependency(\"B\", {\"version\": \"^1.0\"}))\n\n    package_b = get_package(\"B\", \"1.0\")\n\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    package_d = get_package(\"D\", \"1.0\")\n\n    package_e = get_package(\"E\", \"1.0\")\n    package_e.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n    repo.add_package(package_e)\n\n    transaction = solver.solve()\n\n    _ = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_e},\n        ],\n    )\n\n\ndef test_solver_with_dependency_and_prerelease_sub_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \">=1.0.0.dev2\"))\n\n    repo.add_package(package_a)\n    repo.add_package(get_package(\"B\", \"0.9.0\"))\n    repo.add_package(get_package(\"B\", \"1.0.0.dev1\"))\n    repo.add_package(get_package(\"B\", \"1.0.0.dev2\"))\n    repo.add_package(get_package(\"B\", \"1.0.0.dev3\"))\n    package_b = get_package(\"B\", \"1.0.0.dev4\")\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_with_dependency_and_prerelease_sub_dependencies_increasing_constraints(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    mocker: MockerFixture,\n) -> None:\n    \"\"\"Regression test to ensure the solver eventually uses pre-release\n    dependencies if the package is progressively constrained enough.\n\n    This is different from test_solver_with_dependency_and_prerelease_sub_dependencies\n    above because it also has a wildcard dependency on B at the root level.\n    This causes the solver to first narrow B's candidate versions down to\n    {0.9.0} at an early level, then eventually down to the empty set once A's\n    dependencies are processed at a later level.\n\n    Once the candidate version set is narrowed down to the empty set, the\n    solver should re-evaluate available candidate versions from the source, but\n    include pre-release versions this time as there are no other options.\n    \"\"\"\n    # Note: The order matters here; B must be added before A or the solver\n    # evaluates A first and we don't encounter the issue. This is a bit\n    # fragile, but the mock call assertions ensure this ordering is maintained.\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \">0.9.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(get_package(\"B\", \"0.9.0\"))\n    package_b = get_package(\"B\", \"1.0.0.dev4\")\n    repo.add_package(package_b)\n\n    search_for_spy = mocker.spy(solver._provider, \"search_for\")\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n    # The assertions below aren't really the point of this test, but are just\n    # being used to ensure the dependency resolution ordering remains the same.\n    search_calls = [\n        call.args[0]\n        for call in search_for_spy.mock_calls\n        if call.args[0].name in (\"a\", \"b\")\n    ]\n    assert search_calls == [\n        Dependency(\"a\", \"*\"),\n        Dependency(\"b\", \"*\"),\n        Dependency(\"b\", \">0.9.0\"),\n    ]\n\n\ndef test_solver_circular_dependency(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n\n    package_c = get_package(\"C\", \"1.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    transaction = solver.solve()\n\n    _ = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_circular_dependency_chain(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.add_dependency(Factory.create_dependency(\"D\", \"^1.0\"))\n\n    package_d = get_package(\"D\", \"1.0\")\n    package_d.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    transaction = solver.solve()\n\n    _ = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_dense_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    # The root package depends on packages A0...An-1,\n    # And package Ai depends  on packages A0...Ai-1\n    # This graph is a transitive tournament\n    packages = []\n    n = 22\n    for i in range(n):\n        package_ai = get_package(\"a\" + str(i), \"1.0\")\n        repo.add_package(package_ai)\n        packages.append(package_ai)\n        package.add_dependency(Factory.create_dependency(\"a\" + str(i), \"^1.0\"))\n        for j in range(i):\n            package_ai.add_dependency(Factory.create_dependency(\"a\" + str(j), \"^1.0\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction, [{\"job\": \"install\", \"package\": packages[i]} for i in range(n)]\n    )\n\n\ndef test_solver_duplicate_dependencies_same_constraint(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"2.7\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \">=3.4\"})\n    )\n\n    package_b = get_package(\"B\", \"1.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<3.4\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \">=3.4\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints_same_requirements(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", {\"version\": \"^1.0\"}))\n    package_a.add_dependency(Factory.create_dependency(\"B\", {\"version\": \"^2.0\"}))\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n\n    with pytest.raises(IncompatibleConstraintsError) as e:\n        solver.solve()\n\n    expected = \"\"\"\\\nIncompatible constraints in requirements of a (1.0):\nB (>=1.0,<2.0)\nB (>=2.0,<3.0)\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_solver_duplicate_dependencies_different_constraints_merge_by_marker(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<3.4\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \">=3.4\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"!=1.1\", \"python\": \"<3.4\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b11 = get_package(\"B\", \"1.1\")\n    package_b20 = get_package(\"B\", \"2.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b11)\n    repo.add_package(package_b20)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"git_first\", [False, True])\ndef test_solver_duplicate_dependencies_different_sources_direct_origin_preserved(\n    solver: Solver, repo: Repository, package: ProjectPackage, git_first: bool\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n    repo.add_package(get_package(\"cleo\", \"1.0.0\"))\n    repo.add_package(get_package(\"demo\", \"0.1.0\"))\n\n    dependency_pypi = Factory.create_dependency(\"demo\", \">=0.1.0\")\n    dependency_git = Factory.create_dependency(\n        \"demo\", {\"git\": \"https://github.com/demo/demo.git\"}, groups=[\"dev\"]\n    )\n    if git_first:\n        package.add_dependency(dependency_git)\n        package.add_dependency(dependency_pypi)\n    else:\n        package.add_dependency(dependency_pypi)\n        package.add_dependency(dependency_git)\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.source_type == demo.source_type\n    assert op.package.source_reference == DEFAULT_SOURCE_REF\n    assert op.package.source_resolved_reference is not None\n    assert demo.source_resolved_reference is not None\n    assert op.package.source_resolved_reference.startswith(\n        demo.source_resolved_reference\n    )\n\n    complete_package = solver.provider.complete_package(\n        DependencyPackage(package.to_dependency(), package)\n    )\n\n    assert len(complete_package.package.all_requires) == 1\n    dep = complete_package.package.all_requires[0]\n\n    assert isinstance(dep, VCSDependency)\n    assert dep.constraint == demo.version\n    assert (dep.name, dep.source_type, dep.source_url, dep.source_reference) == (\n        dependency_git.name,\n        dependency_git.source_type,\n        dependency_git.source_url,\n        DEFAULT_SOURCE_REF,\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints_merge_no_markers(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"1.0\"))\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a10.add_dependency(Factory.create_dependency(\"C\", {\"version\": \"^1.0\"}))\n\n    package_a20 = get_package(\"A\", \"2.0\")\n    package_a20.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^2.0\"})  # incompatible with B\n    )\n    package_a20.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"!=2.1\", \"python\": \"3.10\"})\n    )\n\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"C\", {\"version\": \"<2.0\"}))\n\n    package_c10 = get_package(\"C\", \"1.0\")\n    package_c20 = get_package(\"C\", \"2.0\")\n    package_c21 = get_package(\"C\", \"2.1\")\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a20)\n    repo.add_package(package_b)\n    repo.add_package(package_c10)\n    repo.add_package(package_c20)\n    repo.add_package(package_c21)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c10},\n            {\"job\": \"install\", \"package\": package_a10},  # only a10, not a20\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints_conflict(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \">=1.1\"))\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"<1.1\", \"python\": \"3.10\"})\n    )\n\n    repo.add_package(get_package(\"A\", \"1.0\"))\n    repo.add_package(get_package(\"A\", \"1.1\"))\n    repo.add_package(get_package(\"A\", \"1.2\"))\n\n    expectation = (\n        \"Incompatible constraints in requirements of root (1.0):\\n\"\n        \"A (>=1.1)\\n\"\n        'A (<1.1) ; python_version == \"3.10\"'\n    )\n    with pytest.raises(IncompatibleConstraintsError, match=re.escape(expectation)):\n        solver.solve()\n\n\ndef test_solver_duplicate_dependencies_different_constraints_discard_no_markers1(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Initial dependencies:\n        A (>=1.0)\n        A (<1.2) ; python >= 3.10\n        A (<1.1) ; python < 3.10\n\n    Merged dependencies:\n        A (>=1.0) ; <empty>\n        A (>=1.0,<1.2) ; python >= 3.10\n        A (>=1.0,<1.1) ; python < 3.10\n\n    The dependency with an empty marker has to be ignored.\n    \"\"\"\n    package.add_dependency(Factory.create_dependency(\"A\", \">=1.0\"))\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"<1.2\", \"python\": \">=3.10\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"<1.1\", \"python\": \"<3.10\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a11 = get_package(\"A\", \"1.1\")\n    package_a12 = get_package(\"A\", \"1.2\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a11)\n    repo.add_package(package_a12)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            # only a10 and a11, not a12\n            {\"job\": \"install\", \"package\": package_a10},\n            {\"job\": \"install\", \"package\": package_a11},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints_discard_no_markers2(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Initial dependencies:\n        A (>=1.0)\n        A (<1.2) ; python == 3.10\n\n    Merged dependencies:\n        A (>=1.0) ; python != 3.10\n        A (>=1.0,<1.2) ; python == 3.10\n\n    The first dependency has to be ignored\n    because it is not compatible with the project's python constraint.\n    \"\"\"\n    set_package_python_versions(solver.provider, \"~3.10\")\n    package.add_dependency(Factory.create_dependency(\"A\", \">=1.0\"))\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"<1.2\", \"python\": \"3.10\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a11 = get_package(\"A\", \"1.1\")\n    package_a12 = get_package(\"A\", \"1.2\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a11)\n    repo.add_package(package_a12)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a11},  # only a11, not a12\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_different_constraints_discard_no_markers3(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Initial dependencies:\n        A (>=1.0)\n        A (<1.2) ; python == 3.10\n\n    Merged dependencies:\n        A (>=1.0) ; python != 3.10\n        A (>=1.0,<1.2) ; python == 3.10\n\n    The first dependency has to be ignored\n    because it is not compatible with the current environment.\n    \"\"\"\n    package.add_dependency(Factory.create_dependency(\"A\", \">=1.0\"))\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"<1.2\", \"python\": \"3.10\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a11 = get_package(\"A\", \"1.1\")\n    package_a12 = get_package(\"A\", \"1.2\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a11)\n    repo.add_package(package_a12)\n    repo.add_package(package_b)\n\n    with solver.use_environment(MockEnv((3, 10, 0))):\n        transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a11},  # only a11, not a12\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Distinct requirements per marker:\n    * Python 2.7: A (which requires B) and B\n    * Python 3.6: same as Python 2.7 but with different versions\n    * Python 3.7: only A\n    * Python 3.8: only B\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"1.0\", \"python\": \"~2.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"2.0\", \"python\": \"~3.6\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"3.0\", \"python\": \"~3.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"1.0\", \"python\": \"~2.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"2.0\", \"python\": \"~3.6\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"3.0\", \"python\": \"~3.8\"})\n    )\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a10.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"~2.7\"})\n    )\n\n    package_a20 = get_package(\"A\", \"2.0\")\n    package_a20.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \"~3.6\"})\n    )\n\n    package_a30 = get_package(\"A\", \"3.0\")  # no dep to B\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b11 = get_package(\"B\", \"1.1\")\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b21 = get_package(\"B\", \"2.1\")\n    package_b30 = get_package(\"B\", \"3.0\")\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a20)\n    repo.add_package(package_a30)\n    repo.add_package(package_b10)\n    repo.add_package(package_b11)\n    repo.add_package(package_b20)\n    repo.add_package(package_b21)\n    repo.add_package(package_b30)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a10},\n            {\"job\": \"install\", \"package\": package_a20},\n            {\"job\": \"install\", \"package\": package_a30},\n            {\"job\": \"install\", \"package\": package_b30},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection2(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Empty intersection between top level dependency and transitive dependency.\n    \"\"\"\n    package.add_dependency(Factory.create_dependency(\"A\", {\"version\": \"1.0\"}))\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \">=2.0\", \"python\": \">=3.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"python\": \"<3.7\"})\n    )\n\n    package_a10 = get_package(\"A\", \"1.0\")\n    package_a10.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \">=2.0\", \"python\": \">=3.7\"})\n    )\n    package_a10.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"*\", \"python\": \"<3.7\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b10.python_versions = \"<3.7\"\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b20.python_versions = \">=3.7\"\n\n    repo.add_package(package_a10)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a10},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_sub_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"<3.4\"})\n    )\n    package_a.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^2.0\", \"python\": \">=3.4\"})\n    )\n\n    package_b10 = get_package(\"B\", \"1.0\")\n    package_b20 = get_package(\"B\", \"2.0\")\n    package_b10.add_dependency(Factory.create_dependency(\"C\", \"1.2\"))\n    package_b20.add_dependency(Factory.create_dependency(\"C\", \"1.5\"))\n\n    package_c12 = get_package(\"C\", \"1.2\")\n    package_c15 = get_package(\"C\", \"1.5\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n    repo.add_package(package_c12)\n    repo.add_package(package_c15)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c12},\n            {\"job\": \"install\", \"package\": package_c15},\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_duplicate_dependencies_with_overlapping_markers_simple(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(get_dependency(\"b\", \"1.0\"))\n\n    package_b = get_package(\"b\", \"1.0\")\n    dep_strings = [\n        \"a (>=1.0)\",\n        \"a (>=1.1) ; python_version >= '3.7'\",\n        \"a (<2.0) ; python_version < '3.8'\",\n        \"a (!=1.2) ; python_version == '3.7'\",\n    ]\n    deps = [Dependency.create_from_pep_508(dep) for dep in dep_strings]\n    for dep in deps:\n        package_b.add_dependency(dep)\n\n    package_a09 = get_package(\"a\", \"0.9\")\n    package_a10 = get_package(\"a\", \"1.0\")\n    package_a11 = get_package(\"a\", \"1.1\")\n    package_a12 = get_package(\"a\", \"1.2\")\n    package_a20 = get_package(\"a\", \"2.0\")\n\n    package_a11.python_versions = \">=3.7\"\n    package_a12.python_versions = \">=3.7\"\n    package_a20.python_versions = \">=3.7\"\n\n    repo.add_package(package_a09)\n    repo.add_package(package_a10)\n    repo.add_package(package_a11)\n    repo.add_package(package_a12)\n    repo.add_package(package_a20)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a10},\n            {\"job\": \"install\", \"package\": package_a11},\n            {\"job\": \"install\", \"package\": package_a20},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n    package_b_requires = {dep.to_pep_508() for dep in ops[-1].package.requires}\n    assert package_b_requires == {\n        'a (>=1.0,<2.0) ; python_version < \"3.7\"',\n        'a (>=1.1,!=1.2,<2.0) ; python_version == \"3.7\"',\n        'a (>=1.1) ; python_version >= \"3.8\"',\n    }\n\n\ndef test_solver_duplicate_dependencies_with_overlapping_markers_complex(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    Dependencies with overlapping markers from\n    https://pypi.org/project/opencv-python/4.6.0.66/\n    \"\"\"\n    package.add_dependency(get_dependency(\"opencv\", \"4.6.0.66\"))\n\n    opencv_package = get_package(\"opencv\", \"4.6.0.66\")\n    dep_strings = [\n        \"numpy (>=1.13.3) ; python_version < '3.7'\",\n        \"numpy (>=1.21.2) ; python_version >= '3.10'\",\n        (\n            \"numpy (>=1.21.2) ; python_version >= '3.6' \"\n            \"and platform_system == 'Darwin' and platform_machine == 'arm64'\"\n        ),\n        (\n            \"numpy (>=1.19.3) ; python_version >= '3.6' \"\n            \"and platform_system == 'Linux' and platform_machine == 'aarch64'\"\n        ),\n        \"numpy (>=1.14.5) ; python_version >= '3.7'\",\n        \"numpy (>=1.17.3) ; python_version >= '3.8'\",\n        \"numpy (>=1.19.3) ; python_version >= '3.9'\",\n    ]\n    deps = [Dependency.create_from_pep_508(dep) for dep in dep_strings]\n    for dep in deps:\n        opencv_package.add_dependency(dep)\n\n    for version in {\"1.13.3\", \"1.21.2\", \"1.19.3\", \"1.14.5\", \"1.17.3\"}:\n        repo.add_package(get_package(\"numpy\", version))\n    repo.add_package(opencv_package)\n\n    transaction = solver.solve()\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"numpy\", \"1.21.2\")},\n            {\"job\": \"install\", \"package\": opencv_package},\n        ],\n    )\n    opencv_requires = {dep.to_pep_508() for dep in ops[-1].package.requires}\n\n    # before https://github.com/python-poetry/poetry-core/pull/851\n    expected1 = {\n        (\n            \"numpy (>=1.21.2) ;\"\n            ' python_version >= \"3.6\" and platform_system == \"Darwin\"'\n            ' and platform_machine == \"arm64\" or python_version >= \"3.10\"'\n        ),\n        (\n            'numpy (>=1.19.3) ; platform_system == \"Linux\"'\n            ' and platform_machine == \"aarch64\" and python_version < \"3.10\"'\n            ' and python_version >= \"3.6\" or python_version == \"3.9\"'\n            ' and platform_system != \"Darwin\" or python_version == \"3.9\"'\n            ' and platform_machine != \"arm64\"'\n        ),\n        (\n            'numpy (>=1.17.3) ; python_version == \"3.8\"'\n            ' and (platform_system != \"Darwin\" or platform_machine != \"arm64\")'\n            ' and (platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n        (\n            'numpy (>=1.14.5) ; python_version == \"3.7\"'\n            ' and (platform_system != \"Darwin\" or platform_machine != \"arm64\")'\n            ' and (platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n        (\n            'numpy (>=1.13.3) ; python_version < \"3.7\"'\n            ' and (python_version < \"3.6\" or platform_system != \"Darwin\"'\n            ' or platform_machine != \"arm64\") and (python_version < \"3.6\"'\n            ' or platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n    }\n    # after https://github.com/python-poetry/poetry-core/pull/851\n    expected2 = {\n        (\n            \"numpy (>=1.21.2) ;\"\n            ' platform_system == \"Darwin\" and platform_machine == \"arm64\"'\n            ' and python_version >= \"3.6\" or python_version >= \"3.10\"'\n        ),\n        (\n            'numpy (>=1.19.3) ; python_version >= \"3.6\" and python_version < \"3.10\"'\n            ' and platform_system == \"Linux\" and platform_machine == \"aarch64\"'\n            ' or python_version == \"3.9\" and platform_machine != \"arm64\"'\n            ' or python_version == \"3.9\" and platform_system != \"Darwin\"'\n        ),\n        (\n            'numpy (>=1.17.3) ; python_version == \"3.8\"'\n            ' and (platform_system != \"Darwin\" or platform_machine != \"arm64\")'\n            ' and (platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n        (\n            'numpy (>=1.14.5) ; python_version == \"3.7\"'\n            ' and (platform_system != \"Darwin\" or platform_machine != \"arm64\")'\n            ' and (platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n        (\n            'numpy (>=1.13.3) ; python_version < \"3.7\"'\n            ' and (python_version < \"3.6\" or platform_system != \"Darwin\"'\n            ' or platform_machine != \"arm64\") and (python_version < \"3.6\"'\n            ' or platform_system != \"Linux\" or platform_machine != \"aarch64\")'\n        ),\n    }\n\n    assert opencv_requires in (expected1, expected2)\n\n\ndef test_duplicate_path_dependencies(\n    solver: Solver,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.7\")\n    project_dir = shutil.copytree(\n        fixture_dir(\"with_conditional_path_deps\"), tmp_path / \"project\"\n    )\n\n    path1 = (project_dir / \"demo_one\").as_posix()\n    demo1 = Package(\"demo\", \"1.2.3\", source_type=\"directory\", source_url=path1)\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"path\": path1, \"markers\": \"sys_platform == 'linux'\"}\n        )\n    )\n\n    path2 = (project_dir / \"demo_two\").as_posix()\n    demo2 = Package(\"demo\", \"1.2.3\", source_type=\"directory\", source_url=path2)\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"path\": path2, \"markers\": \"sys_platform == 'win32'\"}\n        )\n    )\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": demo1},\n            {\"job\": \"install\", \"package\": demo2},\n        ],\n    )\n\n\ndef test_duplicate_path_dependencies_same_path(\n    solver: Solver,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.7\")\n    project_dir = shutil.copytree(\n        fixture_dir(\"with_conditional_path_deps\"), tmp_path / \"project\"\n    )\n\n    path1 = (project_dir / \"demo_one\").as_posix()\n    demo1 = Package(\"demo\", \"1.2.3\", source_type=\"directory\", source_url=path1)\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"path\": path1, \"markers\": \"sys_platform == 'linux'\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"path\": path1, \"markers\": \"sys_platform == 'win32'\"}\n        )\n    )\n\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": demo1}])\n\n\ndef test_solver_fails_if_dependency_name_does_not_match_package(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\n            \"my-demo\", {\"git\": \"https://github.com/demo/demo.git\"}\n        )\n    )\n\n    with pytest.raises(RuntimeError):\n        solver.solve()\n\n\ndef test_solver_does_not_get_stuck_in_recursion_on_circular_dependency(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package_a = get_package(\"A\", \"1.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n    package_b = get_package(\"B\", \"1.0\")\n    package_b.add_dependency(Factory.create_dependency(\"C\", \"^1.0\"))\n    package_c = get_package(\"C\", \"1.0\")\n    package_c.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_can_resolve_git_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    transaction = solver.solve()\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.source_type == \"git\"\n    assert op.package.source_reference == DEFAULT_SOURCE_REF\n    assert op.package.source_resolved_reference is not None\n    assert op.package.source_resolved_reference.startswith(\"9cf87a2\")\n\n\ndef test_solver_can_resolve_git_dependencies_with_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"git\": \"https://github.com/demo/demo.git\", \"extras\": [\"foo\"]}\n        )\n    )\n\n    transaction = solver.solve()\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": cleo},\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\n    \"ref\",\n    [{\"branch\": \"a-branch\"}, {\"tag\": \"a-tag\"}, {\"rev\": \"9cf8\"}],\n    ids=[\"branch\", \"tag\", \"rev\"],\n)\ndef test_solver_can_resolve_git_dependencies_with_ref(\n    solver: Solver, repo: Repository, package: ProjectPackage, ref: dict[str, str]\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=ref[next(iter(ref.keys()))],\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    assert demo.source_type is not None\n    assert demo.source_url is not None\n    git_config = {demo.source_type: demo.source_url}\n    git_config.update(ref)\n    package.add_dependency(Factory.create_dependency(\"demo\", git_config))\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.source_type == \"git\"\n    assert op.package.source_reference == ref[next(iter(ref.keys()))]\n    assert op.package.source_resolved_reference is not None\n    assert op.package.source_resolved_reference.startswith(\"9cf87a2\")\n\n\ndef test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.4\")\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.6\"})\n    )\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.python_versions = \">=3.6\"\n\n    repo.add_package(package_a)\n\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": package_a}])\n\n\ndef test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible_multiple(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.4\")\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.6\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"B\", {\"version\": \"^1.0\", \"python\": \"^3.5.3\"})\n    )\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.python_versions = \">=3.6\"\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    package_b = get_package(\"B\", \"1.0.0\")\n    package_b.python_versions = \">=3.5.3\"\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_with_package_python(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.4\")\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.5\"})\n    )\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.python_versions = \">=3.6\"\n\n    repo.add_package(package_a)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.4\")\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.5\"})\n    )\n\n    package_a101 = get_package(\"A\", \"1.0.1\")\n    package_a101.python_versions = \">=3.6\"\n\n    package_a100 = get_package(\"A\", \"1.0.0\")\n    package_a100.python_versions = \">=3.5\"\n\n    repo.add_package(package_a100)\n    repo.add_package(package_a101)\n\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": package_a100}])\n\n\ndef test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    dep1 = Dependency.create_from_pep_508('B (>=1.0); extra == \"foo\"')\n    dep1.activate()\n    dep2 = Dependency.create_from_pep_508('B (>=2.0); extra == \"bar\"')\n    dep2.activate()\n\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"extras\": [\"foo\", \"bar\"]})\n    )\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.extras = {\n        canonicalize_name(\"foo\"): [dep1],\n        canonicalize_name(\"bar\"): [dep2],\n    }\n    package_a.add_dependency(dep1)\n    package_a.add_dependency(dep2)\n\n    package_b2 = get_package(\"B\", \"2.0.0\")\n    package_b1 = get_package(\"B\", \"1.0.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b1)\n    repo.add_package(package_b2)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b2},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n    assert str(ops[0].package.marker) == \"\"\n    assert str(ops[1].package.marker) == \"\"\n\n\ndef test_solver_does_not_raise_conflict_for_locked_conditional_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.4\")\n    dependency_a = Factory.create_dependency(\"A\", {\"version\": \"^1.0\", \"python\": \"^3.6\"})\n    package.add_dependency(dependency_a)\n    package.add_dependency(Factory.create_dependency(\"B\", \"^1.0\"))\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.python_versions = \">=3.6\"\n    package_a.marker = parse_marker(\n        'python_version >= \"3.6\" and python_version < \"4.0\"'\n    )\n\n    package_b = get_package(\"B\", \"1.0.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n\n    dep_package_a = DependencyPackage(dependency_a, package_a)\n    solver.provider._locked = {canonicalize_name(\"A\"): [dep_package_a]}\n    transaction = solver.solve(use_latest=[package_b.name])\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_package(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b = get_package(\"B\", \"1.0\")\n    package_c = get_package(\"C\", \"1.0\")\n    package_d = get_package(\"D\", \"1.0\")\n\n    package_b.add_dependency(\n        Factory.create_dependency(\"C\", {\"version\": \"^1.0\", \"extras\": [\"foo\"]})\n    )\n\n    package_c.add_dependency(\n        Factory.create_dependency(\"D\", {\"version\": \"^1.0\", \"optional\": True})\n    )\n    package_c.extras = {\n        canonicalize_name(\"foo\"): [Factory.create_dependency(\"D\", \"^1.0\")]\n    }\n\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n    repo.add_package(package_d)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_d},\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b},\n        ],\n    )\n\n\ndef test_solver_should_not_resolve_prerelease_version_if_not_requested(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"A\", \"~1.8.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^0.5.0\"))\n\n    package_a185 = get_package(\"A\", \"1.8.5\")\n    package_a19b1 = get_package(\"A\", \"1.9b1\")\n    package_b = get_package(\"B\", \"0.5.0\")\n    package_b.add_dependency(Factory.create_dependency(\"A\", \">=1.9b1\"))\n\n    repo.add_package(package_a185)\n    repo.add_package(package_a19b1)\n    repo.add_package(package_b)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_ignores_dependencies_with_incompatible_python_full_version_marker(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"^3.6\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"^2.0\"))\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(\n        Dependency.create_from_pep_508(\n            'B (<2.0); platform_python_implementation == \"PyPy\" and python_full_version'\n            ' < \"2.7.9\"'\n        )\n    )\n\n    package_b200 = get_package(\"B\", \"2.0.0\")\n    package_b100 = get_package(\"B\", \"1.0.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b100)\n    repo.add_package(package_b200)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a},\n            {\"job\": \"install\", \"package\": package_b200},\n        ],\n    )\n\n\ndef test_solver_git_dependencies_update(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    demo_installed = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=\"123456\",\n    )\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    solver = Solver(package, pool, [demo_installed], [], io)\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"update\", \"from\": demo_installed, \"to\": demo},\n        ],\n    )\n\n    op = ops[1]\n\n    assert op.job_type == \"update\"\n    assert isinstance(op, Update)\n    assert op.package.source_type == \"git\"\n    assert op.package.source_reference == DEFAULT_SOURCE_REF\n    assert op.package.source_resolved_reference is not None\n    assert op.package.source_resolved_reference.startswith(\"9cf87a2\")\n    assert op.initial_package.source_resolved_reference == \"123456\"\n\n\ndef test_solver_git_dependencies_update_skipped(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=\"master\",\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    solver = Solver(package, pool, [demo], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo, \"skipped\": True},\n        ],\n    )\n\n\ndef test_solver_git_dependencies_short_hash_update_skipped(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    demo = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=MOCK_DEFAULT_GIT_REVISION,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\", {\"git\": \"https://github.com/demo/demo.git\", \"rev\": \"9cf87a2\"}\n        )\n    )\n\n    solver = Solver(package, pool, [demo], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": pendulum},\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"demo\",\n                    \"0.1.2\",\n                    source_type=\"git\",\n                    source_url=\"https://github.com/demo/demo.git\",\n                    source_reference=MOCK_DEFAULT_GIT_REVISION,\n                    source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n                ),\n                \"skipped\": True,\n            },\n        ],\n    )\n\n\ndef test_solver_can_resolve_directory_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n\n    project_dir = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\",\n        tmp_path / \"project\",\n    )\n    path = project_dir.as_posix()\n\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.2\", source_type=\"directory\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.2\"\n    assert op.package.source_type == \"directory\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_resolve_directory_dependencies_nested_editable(\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    base = fixture_dir(\"project_with_nested_local\")\n    poetry = Factory().create_poetry(cwd=base)\n    package = poetry.package\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"quix\",\n                    \"1.2.3\",\n                    source_type=\"directory\",\n                    source_url=(base / \"quix\").as_posix(),\n                ),\n                \"skipped\": False,\n            },\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"bar\",\n                    \"1.2.3\",\n                    source_type=\"directory\",\n                    source_url=(base / \"bar\").as_posix(),\n                ),\n                \"skipped\": False,\n            },\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"foo\",\n                    \"1.2.3\",\n                    source_type=\"directory\",\n                    source_url=(base / \"foo\").as_posix(),\n                ),\n                \"skipped\": False,\n            },\n        ],\n    )\n\n    for op in ops:\n        assert op.package.source_type == \"directory\"\n        assert op.package.develop is True\n\n\ndef test_solver_can_resolve_directory_dependencies_with_extras(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    project_dir = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\",\n        tmp_path / \"project\",\n    )\n    path = project_dir.as_posix()\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"path\": path, \"extras\": [\"foo\"]})\n    )\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.2\", source_type=\"directory\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": cleo},\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo},\n        ],\n    )\n\n    op = ops[2]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.2\"\n    assert op.package.source_type == \"directory\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_resolve_sdist_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\", project_dir)\n    path = Path(path).as_posix()\n\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.0\"\n    assert op.package.source_type == \"file\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_resolve_sdist_dependencies_with_extras(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\", project_dir)\n    path = Path(path).as_posix()\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"path\": path, \"extras\": [\"foo\"]})\n    )\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": cleo},\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo},\n        ],\n    )\n\n    op = ops[2]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.0\"\n    assert op.package.source_type == \"file\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_resolve_wheel_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\", project_dir\n    )\n    path = Path(path).as_posix()\n\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pendulum}, {\"job\": \"install\", \"package\": demo}],\n    )\n\n    op = ops[1]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.0\"\n    assert op.package.source_type == \"file\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_resolve_wheel_dependencies_with_extras(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    cleo = get_package(\"cleo\", \"1.0.0\")\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\", project_dir\n    )\n    path = Path(path).as_posix()\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"path\": path, \"extras\": [\"foo\"]})\n    )\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.0\", source_type=\"file\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": cleo},\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo},\n        ],\n    )\n\n    op = ops[2]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.0\"\n    assert op.package.source_type == \"file\"\n    assert op.package.source_url == path\n\n\ndef test_solver_can_solve_with_legacy_repository_using_proper_dists(\n    package: ProjectPackage, io: NullIO, legacy_repository: LegacyRepository\n) -> None:\n    repo = legacy_repository\n    pool = RepositoryPool([repo])\n\n    solver = Solver(package, pool, [], [], io)\n\n    package.add_dependency(Factory.create_dependency(\"isort\", \"4.3.4\"))\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"futures\",\n                    \"3.2.0\",\n                    source_type=\"legacy\",\n                    source_url=repo.url,\n                    source_reference=repo.name,\n                ),\n            },\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"isort\",\n                    \"4.3.4\",\n                    source_type=\"legacy\",\n                    source_url=repo.url,\n                    source_reference=repo.name,\n                ),\n            },\n        ],\n    )\n\n    futures = ops[0].package\n    assert futures.python_versions == \">=2.6, <3\"\n\n\ndef test_solver_can_solve_with_legacy_repository_using_proper_python_compatible_dists(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n\n    repo = legacy_repository\n    pool = RepositoryPool([repo])\n\n    solver = Solver(package, pool, [], [], io)\n\n    package.add_dependency(Factory.create_dependency(\"isort\", \"4.3.4\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"isort\",\n                    \"4.3.4\",\n                    source_type=\"legacy\",\n                    source_url=repo.url,\n                    source_reference=repo.name,\n                ),\n            }\n        ],\n    )\n\n\ndef test_solver_skips_invalid_versions(\n    package: ProjectPackage, io: NullIO, pypi_repository: PyPiRepository\n) -> None:\n    package.python_versions = \"^3.9\"\n\n    pool = RepositoryPool([pypi_repository])\n\n    solver = Solver(package, pool, [], [], io)\n\n    package.add_dependency(Factory.create_dependency(\"six-unknown-version\", \"^1.11\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": get_package(\"six-unknown-version\", \"1.11.0\")}],\n    )\n\n\ndef test_multiple_constraints_on_root(\n    package: ProjectPackage, solver: Solver, repo: Repository\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"foo\", {\"version\": \"^1.0\", \"python\": \"^2.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"foo\", {\"version\": \"^2.0\", \"python\": \"^3.7\"})\n    )\n\n    foo15 = get_package(\"foo\", \"1.5.0\")\n    foo25 = get_package(\"foo\", \"2.5.0\")\n\n    repo.add_package(foo15)\n    repo.add_package(foo25)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": foo15}, {\"job\": \"install\", \"package\": foo25}],\n    )\n\n\ndef test_solver_chooses_most_recent_version_amongst_repositories(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(Factory.create_dependency(\"tomlkit\", {\"version\": \"^0.5\"}))\n\n    pool = RepositoryPool([legacy_repository, pypi_repository])\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction, [{\"job\": \"install\", \"package\": get_package(\"tomlkit\", \"0.5.3\")}]\n    )\n\n    assert ops[0].package.source_type is None\n    assert ops[0].package.source_url is None\n\n\ndef test_solver_chooses_from_correct_repository_if_forced(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(\n        Factory.create_dependency(\"tomlkit\", {\"version\": \"^0.5\", \"source\": \"legacy\"})\n    )\n\n    pool = RepositoryPool([legacy_repository, pypi_repository])\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"tomlkit\",\n                    \"0.5.2\",\n                    source_type=\"legacy\",\n                    source_url=legacy_repository.url,\n                    source_reference=legacy_repository.name,\n                ),\n            }\n        ],\n    )\n\n    assert ops[0].package.source_url == legacy_repository.url\n\n\n@pytest.mark.parametrize(\"project_dependencies\", [True, False])\ndef test_solver_chooses_from_correct_repository_if_forced_and_transitive_dependency(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n    project_dependencies: bool,\n) -> None:\n    package.python_versions = \"^3.7\"\n    if project_dependencies:\n        main_group = DependencyGroup(MAIN_GROUP)\n        package.add_dependency_group(main_group)\n        main_group.add_dependency(Factory.create_dependency(\"foo\", \"^1.0\"))\n        main_group.add_dependency(Factory.create_dependency(\"tomlkit\", \"^0.5\"))\n        main_group.add_poetry_dependency(\n            Factory.create_dependency(\"tomlkit\", {\"source\": \"legacy\"})\n        )\n    else:\n        package.add_dependency(Factory.create_dependency(\"foo\", \"^1.0\"))\n        package.add_dependency(\n            Factory.create_dependency(\n                \"tomlkit\", {\"version\": \"^0.5\", \"source\": \"legacy\"}\n            )\n        )\n\n    repo = Repository(\"repo\")\n    foo = get_package(\"foo\", \"1.0.0\")\n    foo.add_dependency(Factory.create_dependency(\"tomlkit\", \"^0.5.0\"))\n    repo.add_package(foo)\n\n    pool = RepositoryPool([legacy_repository, repo, pypi_repository])\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"tomlkit\",\n                    \"0.5.2\",\n                    source_type=\"legacy\",\n                    source_url=legacy_repository.url,\n                    source_reference=\"legacy\",\n                ),\n            },\n            {\"job\": \"install\", \"package\": foo},\n        ],\n    )\n\n    assert ops[0].package.source_url == legacy_repository.url\n\n    assert ops[1].package.source_type is None\n    assert ops[1].package.source_url is None\n\n\ndef test_solver_does_not_choose_from_supplemental_repository_by_default(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(Factory.create_dependency(\"clikit\", {\"version\": \"^0.2.0\"}))\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository, priority=Priority.SUPPLEMENTAL)\n    pool.add_repository(legacy_repository)\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"pastel\",\n                    \"0.1.0\",\n                    source_type=\"legacy\",\n                    source_url=legacy_repository.url,\n                    source_reference=\"legacy\",\n                ),\n            },\n            {\"job\": \"install\", \"package\": get_package(\"pylev\", \"1.3.0\")},\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"clikit\",\n                    \"0.2.4\",\n                    source_type=\"legacy\",\n                    source_url=legacy_repository.url,\n                    source_reference=\"legacy\",\n                ),\n            },\n        ],\n    )\n\n    assert ops[0].package.source_url == legacy_repository.url\n    assert ops[1].package.source_type is None\n    assert ops[1].package.source_url is None\n    assert ops[2].package.source_url == legacy_repository.url\n\n\ndef test_solver_chooses_from_supplemental_if_explicit(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(\n        Factory.create_dependency(\"clikit\", {\"version\": \"^0.2.0\", \"source\": \"PyPI\"})\n    )\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository, priority=Priority.SUPPLEMENTAL)\n    pool.add_repository(legacy_repository)\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\n                \"job\": \"install\",\n                \"package\": Package(\n                    \"pastel\",\n                    \"0.1.0\",\n                    source_type=\"legacy\",\n                    source_url=legacy_repository.url,\n                    source_reference=\"legacy\",\n                ),\n            },\n            {\"job\": \"install\", \"package\": get_package(\"pylev\", \"1.3.0\")},\n            {\"job\": \"install\", \"package\": get_package(\"clikit\", \"0.2.4\")},\n        ],\n    )\n\n    assert ops[0].package.source_url == legacy_repository.url\n    assert ops[1].package.source_type is None\n    assert ops[1].package.source_url is None\n    assert ops[2].package.source_type is None\n    assert ops[2].package.source_url is None\n\n\ndef test_solver_does_not_choose_from_explicit_repository(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(Factory.create_dependency(\"attrs\", {\"version\": \"^17.4.0\"}))\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository, priority=Priority.EXPLICIT)\n    pool.add_repository(legacy_repository)\n\n    solver = Solver(package, pool, [], [], io)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_chooses_direct_dependency_from_explicit_if_explicit(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    package.python_versions = \"^3.7\"\n    package.add_dependency(\n        Factory.create_dependency(\"pylev\", {\"version\": \"^1.2.0\", \"source\": \"PyPI\"})\n    )\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository, priority=Priority.EXPLICIT)\n    pool.add_repository(legacy_repository)\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"pylev\", \"1.3.0\")},\n        ],\n    )\n\n    assert ops[0].package.source_type is None\n    assert ops[0].package.source_url is None\n\n\ndef test_solver_ignores_explicit_repo_for_transitive_dependencies(\n    package: ProjectPackage,\n    io: NullIO,\n    legacy_repository: LegacyRepository,\n    pypi_repository: PyPiRepository,\n) -> None:\n    # clikit depends on pylev, which is in pypi_repository (explicit) but not in\n    # legacy_repository\n    package.python_versions = \"^3.7\"\n    package.add_dependency(\n        Factory.create_dependency(\"clikit\", {\"version\": \"^0.2.0\", \"source\": \"PyPI\"})\n    )\n\n    pool = RepositoryPool()\n    pool.add_repository(pypi_repository, priority=Priority.EXPLICIT)\n    pool.add_repository(legacy_repository)\n\n    solver = Solver(package, pool, [], [], io)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\n@pytest.mark.parametrize(\n    (\"lib_versions\", \"other_versions\"),\n    [\n        # number of versions influences which dependency is resolved first\n        ([\"1.0\", \"2.0\"], [\"1.0\", \"1.1\", \"2.0\"]),  # more other than lib\n        ([\"1.0\", \"1.1\", \"2.0\"], [\"1.0\", \"2.0\"]),  # more lib than other\n    ],\n)\ndef test_direct_dependency_with_extras_from_explicit_and_transitive_dependency(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    lib_versions: list[str],\n    other_versions: list[str],\n) -> None:\n    \"\"\"\n    The root package depends on \"lib[extra]\" and \"other\", both with an explicit source.\n    \"other\" depends on \"lib\" (without an extra and of course without an explicit source\n    because explicit sources can only be defined in the root package).\n\n    If \"other\" is resolved before \"lib[extra]\", the solver must not try to fetch \"lib\"\n    from the default source but from the explicit source defined for \"lib[extra]\".\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\", {\"version\": \">=1.0\", \"extras\": [\"extra\"], \"source\": \"explicit\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"other\", {\"version\": \">=1.0\", \"source\": \"explicit\"})\n    )\n\n    explicit_repo = Repository(\"explicit\")\n    pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)\n\n    package_extra = get_package(\"extra\", \"1.0\")\n    repo.add_package(package_extra)  # extra only in default repo\n\n    for version in lib_versions:\n        package_lib = Package(\n            \"lib\", version, source_type=\"legacy\", source_reference=\"explicit\"\n        )\n\n        dep_extra = get_dependency(\"extra\", \">=1.0\")\n        package_lib.add_dependency(\n            Factory.create_dependency(\"extra\", {\"version\": \">=1.0\", \"optional\": True})\n        )\n        package_lib.extras = {canonicalize_name(\"extra\"): [dep_extra]}\n\n        explicit_repo.add_package(package_lib)  # lib only in explicit repo\n\n    for version in other_versions:\n        package_other = Package(\n            \"other\", version, source_type=\"legacy\", source_reference=\"explicit\"\n        )\n        package_other.add_dependency(Factory.create_dependency(\"lib\", \">=1.0\"))\n        explicit_repo.add_package(package_other)  # other only in explicit repo\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    expected_lib = Package(\n        \"lib\", \"2.0\", source_type=\"legacy\", source_reference=\"explicit\"\n    )\n    expected_other = Package(\n        \"other\", \"2.0\", source_type=\"legacy\", source_reference=\"explicit\"\n    )\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"extra\", \"1.0\")},\n            {\"job\": \"install\", \"package\": expected_lib},\n            {\"job\": \"install\", \"package\": expected_other},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\n    (\"lib_versions\", \"other_versions\"),\n    [\n        # number of versions influences which dependency is resolved first\n        ([\"1.0\", \"2.0\"], [\"1.0\", \"1.1\", \"2.0\"]),  # more other than lib\n        ([\"1.0\", \"1.1\", \"2.0\"], [\"1.0\", \"2.0\"]),  # more lib than other\n    ],\n)\ndef test_direct_dependency_with_extras_from_explicit_and_transitive_dependency2(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    lib_versions: list[str],\n    other_versions: list[str],\n) -> None:\n    \"\"\"\n    The root package depends on \"lib[extra]\" and \"other\", both with an explicit source.\n    \"other\" depends on \"lib[other-extra]\" (with another extra and of course without an\n    explicit source because explicit sources can only be defined in the root package).\n\n    The solver must not try to fetch \"lib[other-extra]\" from the default source\n    but from the explicit source defined for \"lib[extra]\".\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\", {\"version\": \">=1.0\", \"extras\": [\"extra\"], \"source\": \"explicit\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"other\", {\"version\": \">=1.0\", \"source\": \"explicit\"})\n    )\n\n    explicit_repo = Repository(\"explicit\")\n    pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)\n\n    package_extra = get_package(\"extra\", \"1.0\")\n    repo.add_package(package_extra)  # extra only in default repo\n    package_other_extra = get_package(\"other-extra\", \"1.0\")\n    repo.add_package(package_other_extra)  # extra only in default repo\n\n    for version in lib_versions:\n        package_lib = Package(\n            \"lib\", version, source_type=\"legacy\", source_reference=\"explicit\"\n        )\n\n        dep_extra = get_dependency(\"extra\", \">=1.0\")\n        package_lib.add_dependency(\n            Factory.create_dependency(\"extra\", {\"version\": \">=1.0\", \"optional\": True})\n        )\n\n        dep_other_extra = get_dependency(\"other-extra\", \">=1.0\")\n        package_lib.add_dependency(\n            Factory.create_dependency(\n                \"other-extra\", {\"version\": \">=1.0\", \"optional\": True}\n            )\n        )\n        package_lib.extras = {\n            canonicalize_name(\"extra\"): [dep_extra],\n            canonicalize_name(\"other-extra\"): [dep_other_extra],\n        }\n\n        explicit_repo.add_package(package_lib)  # lib only in explicit repo\n\n    for version in other_versions:\n        package_other = Package(\n            \"other\", version, source_type=\"legacy\", source_reference=\"explicit\"\n        )\n        package_other.add_dependency(\n            Factory.create_dependency(\n                \"lib\", {\"version\": \">=1.0\", \"extras\": [\"other-extra\"]}\n            )\n        )\n        explicit_repo.add_package(package_other)  # other only in explicit repo\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    expected_lib = Package(\n        \"lib\", \"2.0\", source_type=\"legacy\", source_reference=\"explicit\"\n    )\n    expected_other = Package(\n        \"other\", \"2.0\", source_type=\"legacy\", source_reference=\"explicit\"\n    )\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"other-extra\", \"1.0\")},\n            {\"job\": \"install\", \"package\": get_package(\"extra\", \"1.0\")},\n            {\"job\": \"install\", \"package\": expected_lib},\n            {\"job\": \"install\", \"package\": expected_other},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"locked\", [False, True])\ndef test_multiple_constraints_explicit_source_transitive_locked_use_latest(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    locked: bool,\n) -> None:\n    \"\"\"\n    The root package depends on\n     * lib[extra] == 1.0; sys_platform != \"linux\" with source=explicit1\n     * lib[extra] == 2.0; sys_platform == \"linux\" with source=explicit2\n     * other >= 1.0\n    \"other\" depends on \"lib\" (without an extra and of course without an explicit source\n    because explicit sources can only be defined in the root package).\n\n    If only \"other\" is in use_latest (equivalent to \"poetry update other\"),\n    the transitive dependency of \"other\" on \"lib\" is resolved before\n    the direct dependency on \"lib[extra]\" (if packages have been locked before).\n    We still have to make sure that the locked package is looked up in the explicit\n    source although the DependencyCache is not used for locked packages,\n    so we can't rely on it to propagate the correct source.\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\",\n            {\n                \"version\": \"1.0\",\n                \"extras\": [\"extra\"],\n                \"source\": \"explicit1\",\n                \"markers\": \"sys_platform != 'linux'\",\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\",\n            {\n                \"version\": \"2.0\",\n                \"extras\": [\"extra\"],\n                \"source\": \"explicit2\",\n                \"markers\": \"sys_platform == 'linux'\",\n            },\n        )\n    )\n    package.add_dependency(Factory.create_dependency(\"other\", {\"version\": \">=1.0\"}))\n\n    explicit_repo1 = Repository(\"explicit1\")\n    pool.add_repository(explicit_repo1, priority=Priority.EXPLICIT)\n    explicit_repo2 = Repository(\"explicit2\")\n    pool.add_repository(explicit_repo2, priority=Priority.EXPLICIT)\n\n    dep_extra = get_dependency(\"extra\", \">=1.0\")\n    dep_extra_opt = Factory.create_dependency(\n        \"extra\", {\"version\": \">=1.0\", \"optional\": True}\n    )\n    package_lib1 = Package(\n        \"lib\", \"1.0\", source_type=\"legacy\", source_reference=\"explicit1\"\n    )\n    package_lib1.extras = {canonicalize_name(\"extra\"): [dep_extra]}\n    package_lib1.add_dependency(dep_extra_opt)\n    explicit_repo1.add_package(package_lib1)\n    package_lib2 = Package(\n        \"lib\", \"2.0\", source_type=\"legacy\", source_reference=\"explicit2\"\n    )\n    package_lib2.extras = {canonicalize_name(\"extra\"): [dep_extra]}\n    package_lib2.add_dependency(dep_extra_opt)\n    explicit_repo2.add_package(package_lib2)\n\n    package_extra = Package(\"extra\", \"1.0\")\n    repo.add_package(package_extra)\n    package_other = Package(\"other\", \"1.5\")\n    package_other.add_dependency(Factory.create_dependency(\"lib\", \">=1.0\"))\n    repo.add_package(package_other)\n\n    if locked:\n        locked_packages = [package_extra, package_lib1, package_lib2, package_other]\n        use_latest = [canonicalize_name(\"other\")]\n    else:\n        locked_packages = []\n        use_latest = None\n    solver = Solver(package, pool, [], locked_packages, io)\n\n    transaction = solver.solve(use_latest=use_latest)\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_extra},\n            {\"job\": \"install\", \"package\": package_lib1},\n            {\"job\": \"install\", \"package\": package_lib2},\n            {\"job\": \"install\", \"package\": package_other},\n        ],\n    )\n    assert ops[1].package.source_reference == \"explicit1\"\n    assert ops[2].package.source_reference == \"explicit2\"\n\n\n@pytest.mark.parametrize(\"locked\", [False, True])\ndef test_multiple_constraints_incomplete_explicit_source_transitive_locked(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    locked: bool,\n) -> None:\n    \"\"\"\n    The root package depends on\n     * lib == 1.0+cu ; sys_platform == \"linux\" with source=explicit\n     * lib == 1.0 ; sys_platform == \"darwin\" with no explicit source\n     * other >= 1.0\n    \"other\" depends on \"lib\"\n\n    Since the source for lib 1.0+cu has the priority \"explicit\",\n    the default source must be chosen for lib 1.0.\n    Since the multiple constraints are incomplete - they are only defined for linux\n    and darwin, there is another hidden override that also requires lib via other.\n    In this hidden override lib 1.0 from the default source must be chosen\n    (because the other source has the priority \"explicit\").\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\",\n            {\n                \"version\": \"1.0+cu\",\n                \"source\": \"explicit\",\n                \"markers\": \"sys_platform == 'linux'\",\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"lib\",\n            {\n                \"version\": \"1.0\",\n                \"markers\": \"sys_platform == 'darwin'\",\n            },\n        )\n    )\n    package.add_dependency(Factory.create_dependency(\"other\", {\"version\": \">=1.0\"}))\n\n    explicit_repo = Repository(\"explicit\")\n    pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)\n\n    package_lib_explicit = Package(\n        \"lib\", \"1.0+cu\", source_type=\"legacy\", source_reference=\"explicit\"\n    )\n    explicit_repo.add_package(package_lib_explicit)\n    package_lib_default = Package(\"lib\", \"1.0\")\n    repo.add_package(package_lib_default)\n\n    package_other = Package(\"other\", \"1.5\")\n    package_other.add_dependency(Factory.create_dependency(\"lib\", \">=1.0\"))\n    repo.add_package(package_other)\n\n    if locked:\n        # order does not matter because packages are sorted in the provider\n        # (latest first) so that the package from the explicit source is preferred\n        locked_packages = [package_lib_default, package_lib_explicit, package_other]\n    else:\n        locked_packages = []\n    solver = Solver(package, pool, [], locked_packages, io)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_lib_default},\n            {\"job\": \"install\", \"package\": package_lib_explicit},\n            {\"job\": \"install\", \"package\": package_other},\n        ],\n    )\n\n\ndef test_solver_discards_packages_with_empty_markers(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n) -> None:\n    package.python_versions = \"~2.7 || ^3.4\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"a\", {\"version\": \"^0.1.0\", \"markers\": \"python_version >= '3.4'\"}\n        )\n    )\n\n    package_a = get_package(\"a\", \"0.1.0\")\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"b\", {\"version\": \"^0.1.0\", \"markers\": \"python_version < '3.2'\"}\n        )\n    )\n    package_a.add_dependency(Factory.create_dependency(\"c\", \"^0.2.0\"))\n    package_b = get_package(\"b\", \"0.1.0\")\n    package_c = get_package(\"c\", \"0.2.0\")\n    repo.add_package(package_a)\n    repo.add_package(package_b)\n    repo.add_package(package_c)\n\n    solver = Solver(package, pool, [], [], io)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_does_not_raise_conflict_for_conditional_dev_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.5\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"^1.0\", \"python\": \"~2.7\"}, groups=[\"dev\"]\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"^2.0\", \"python\": \"^3.5\"}, groups=[\"dev\"]\n        )\n    )\n\n    package_a100 = get_package(\"A\", \"1.0.0\")\n    package_a200 = get_package(\"A\", \"2.0.0\")\n\n    repo.add_package(package_a100)\n    repo.add_package(package_a200)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a100},\n            {\"job\": \"install\", \"package\": package_a200},\n        ],\n    )\n\n\ndef test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.5\")\n    package.add_dependency(\n        Factory.create_dependency(\n            \"requests\", {\"version\": \"^2.22.0\", \"extras\": [\"security\"]}\n        )\n    )\n\n    requests = get_package(\"requests\", \"2.22.0\")\n    requests.add_dependency(Factory.create_dependency(\"idna\", \">=2.5,<2.9\"))\n    requests.add_dependency(\n        Factory.create_dependency(\n            \"idna\", {\"version\": \">=2.0.0\", \"markers\": \"extra == 'security'\"}\n        )\n    )\n    requests.extras = {\n        canonicalize_name(\"security\"): [get_dependency(\"idna\", \">=2.0.0\")]\n    }\n    idna = get_package(\"idna\", \"2.8\")\n\n    repo.add_package(requests)\n    repo.add_package(idna)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": idna}, {\"job\": \"install\", \"package\": requests}],\n    )\n\n\ndef test_solver_does_not_fail_with_locked_git_and_non_git_dependencies(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"a\", \"^1.2.3\"))\n\n    git_package = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=MOCK_DEFAULT_GIT_REVISION,\n    )\n\n    repo.add_package(get_package(\"a\", \"1.2.3\"))\n    repo.add_package(Package(\"pendulum\", \"2.1.2\"))\n\n    installed = [git_package]\n    locked = [get_package(\"a\", \"1.2.3\"), git_package]\n\n    solver = Solver(package, pool, installed, locked, io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"a\", \"1.2.3\")},\n            {\"job\": \"install\", \"package\": git_package, \"skipped\": True},\n        ],\n    )\n\n\ndef test_ignore_python_constraint_no_overlap_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    pytest = get_package(\"demo\", \"1.0.0\")\n    pytest.add_dependency(\n        Factory.create_dependency(\n            \"configparser\", {\"version\": \"^1.2.3\", \"python\": \"<3.2\"}\n        )\n    )\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"version\": \"^1.0.0\", \"python\": \"^3.6\"})\n    )\n\n    repo.add_package(pytest)\n    repo.add_package(get_package(\"configparser\", \"1.2.3\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": pytest}],\n    )\n\n\ndef test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.5\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n\n    package_a = get_package(\"A\", \"1.0.0\")\n    package_a.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package_a.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"^1.0\", \"markers\": \"implementation_name == 'pypy'\"}\n        )\n    )\n\n    package_b20 = get_package(\"B\", \"2.0.0\")\n    package_b10 = get_package(\"B\", \"1.0.0\")\n\n    repo.add_package(package_a)\n    repo.add_package(package_b10)\n    repo.add_package(package_b20)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_b10},\n            {\"job\": \"install\", \"package\": package_b20},\n            {\"job\": \"install\", \"package\": package_a},\n        ],\n    )\n\n\ndef test_solver_synchronize_single(\n    package: ProjectPackage, pool: RepositoryPool, io: NullIO\n) -> None:\n    package_a = get_package(\"a\", \"1.0\")\n\n    solver = Solver(package, pool, [package_a], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction, [{\"job\": \"remove\", \"package\": package_a}], synchronize=True\n    )\n\n\ndef test_solver_cannot_choose_another_version_for_directory_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    demo = get_package(\"demo\", \"0.1.0\")\n    foo = get_package(\"foo\", \"1.2.3\")\n    foo.add_dependency(Factory.create_dependency(\"demo\", \"<0.1.2\"))\n    repo.add_package(foo)\n    repo.add_package(demo)\n    repo.add_package(pendulum)\n\n    project_dir = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\", tmp_path / \"project\"\n    )\n    path = project_dir.as_posix()\n\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n    package.add_dependency(Factory.create_dependency(\"foo\", \"^1.2.3\"))\n\n    # This is not solvable since the demo version is pinned\n    # via the directory dependency\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_cannot_choose_another_version_for_file_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    demo = get_package(\"demo\", \"0.0.8\")\n    foo = get_package(\"foo\", \"1.2.3\")\n    foo.add_dependency(Factory.create_dependency(\"demo\", \"<0.1.0\"))\n    repo.add_package(foo)\n    repo.add_package(demo)\n    repo.add_package(pendulum)\n\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\", project_dir\n    )\n\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n    package.add_dependency(Factory.create_dependency(\"foo\", \"^1.2.3\"))\n\n    # This is not solvable since the demo version is pinned\n    # via the file dependency\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_cannot_choose_another_version_for_git_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    demo = get_package(\"demo\", \"0.0.8\")\n    foo = get_package(\"foo\", \"1.2.3\")\n    foo.add_dependency(Factory.create_dependency(\"demo\", \"<0.1.0\"))\n    repo.add_package(foo)\n    repo.add_package(demo)\n    repo.add_package(pendulum)\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n    package.add_dependency(Factory.create_dependency(\"foo\", \"^1.2.3\"))\n\n    # This is not solvable since the demo version is pinned\n    # via the file dependency\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_cannot_choose_another_version_for_url_dependencies(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    http: responses.RequestsMock,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    project_dir = tmp_path / \"project\"\n    project_dir.mkdir()\n    path = shutil.copy(\n        fixture_dir(\"distributions\") / \"demo-0.1.0-py2.py3-none-any.whl\", project_dir\n    )\n\n    http.get(\n        \"https://files.pythonhosted.org/demo-0.1.0-py2.py3-none-any.whl\",\n        body=Path(path).read_bytes(),\n    )\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    demo = get_package(\"demo\", \"0.0.8\")\n    foo = get_package(\"foo\", \"1.2.3\")\n    foo.add_dependency(Factory.create_dependency(\"demo\", \"<0.1.0\"))\n    repo.add_package(foo)\n    repo.add_package(demo)\n    repo.add_package(pendulum)\n\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"url\": \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n            },\n        )\n    )\n    package.add_dependency(Factory.create_dependency(\"foo\", \"^1.2.3\"))\n\n    # This is not solvable since the demo version is pinned\n    # via the git dependency\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\n@pytest.mark.parametrize(\"explicit_source\", [True, False])\ndef test_solver_cannot_choose_url_dependency_for_explicit_source(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    explicit_source: bool,\n) -> None:\n    \"\"\"A direct origin dependency cannot satisfy a version dependency with an explicit\n    source. (It can satisfy a version dependency without an explicit source.)\n    \"\"\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"markers\": \"sys_platform != 'darwin'\",\n                \"url\": \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\",\n            },\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"demo\",\n            {\n                \"version\": \"0.1.0\",\n                \"markers\": \"sys_platform == 'darwin'\",\n                \"source\": \"repo\" if explicit_source else None,\n            },\n        )\n    )\n\n    package_pendulum = get_package(\"pendulum\", \"1.4.4\")\n    package_demo = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"legacy\" if explicit_source else None,\n        source_reference=\"repo\" if explicit_source else None,\n    )\n    package_demo_url = Package(\n        \"demo\",\n        \"0.1.0\",\n        source_type=\"url\",\n        source_url=\"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\",\n    )\n    # The url demo dependency depends on pendulum.\n    repo.add_package(package_pendulum)\n    repo.add_package(package_demo)\n\n    transaction = solver.solve()\n\n    if explicit_source:\n        # direct origin cannot satisfy explicit source\n        # -> package_demo MUST be included\n        expected = [\n            {\"job\": \"install\", \"package\": package_pendulum},\n            {\"job\": \"install\", \"package\": package_demo_url},\n            {\"job\": \"install\", \"package\": package_demo},\n        ]\n    else:\n        # direct origin can satisfy dependency without source\n        # -> package_demo NEED NOT (but could) be included\n        expected = [\n            {\"job\": \"install\", \"package\": package_pendulum},\n            {\"job\": \"install\", \"package\": package_demo_url},\n        ]\n\n    check_solver_result(transaction, expected)\n\n\ndef test_solver_should_not_update_same_version_packages_if_installed_has_no_source_type(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"foo\", \"1.0.0\"))\n\n    foo = Package(\n        \"foo\",\n        \"1.0.0\",\n        source_type=\"legacy\",\n        source_url=\"https://foo.bar\",\n        source_reference=\"custom\",\n    )\n    repo.add_package(foo)\n\n    solver = Solver(package, pool, [get_package(\"foo\", \"1.0.0\")], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction, [{\"job\": \"install\", \"package\": foo, \"skipped\": True}]\n    )\n\n\ndef test_solver_should_use_the_python_constraint_from_the_environment_if_available(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.5\")\n    package.add_dependency(Factory.create_dependency(\"A\", \"^1.0\"))\n\n    a = get_package(\"A\", \"1.0.0\")\n    a.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"^1.0.0\", \"markers\": 'python_version < \"3.2\"'}\n        )\n    )\n    b = get_package(\"B\", \"1.0.0\")\n    b.python_versions = \">=2.6, <3\"\n\n    repo.add_package(a)\n    repo.add_package(b)\n\n    with solver.use_environment(MockEnv((2, 7, 18))):\n        transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [{\"job\": \"install\", \"package\": b}, {\"job\": \"install\", \"package\": a}],\n    )\n\n\ndef test_solver_should_resolve_all_versions_for_multiple_duplicate_dependencies(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.python_versions = \"~2.7 || ^3.5\"\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"^1.0\", \"markers\": \"python_version < '3.5'\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"A\", {\"version\": \"^2.0\", \"markers\": \"python_version >= '3.5'\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"^3.0\", \"markers\": \"python_version < '3.5'\"}\n        )\n    )\n    package.add_dependency(\n        Factory.create_dependency(\n            \"B\", {\"version\": \"^4.0\", \"markers\": \"python_version >= '3.5'\"}\n        )\n    )\n\n    package_a10 = get_package(\"A\", \"1.0.0\")\n    package_a20 = get_package(\"A\", \"2.0.0\")\n    package_b30 = get_package(\"B\", \"3.0.0\")\n    package_b40 = get_package(\"B\", \"4.0.0\")\n\n    repo.add_package(package_a10)\n    repo.add_package(package_a20)\n    repo.add_package(package_b30)\n    repo.add_package(package_b40)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_a10},\n            {\"job\": \"install\", \"package\": package_a20},\n            {\"job\": \"install\", \"package\": package_b30},\n            {\"job\": \"install\", \"package\": package_b40},\n        ],\n    )\n\n\ndef test_solver_should_not_raise_errors_for_irrelevant_python_constraints(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.python_versions = \"^3.6\"\n    set_package_python_versions(solver.provider, \"^3.6\")\n    package.add_dependency(\n        Factory.create_dependency(\"dataclasses\", {\"version\": \"^0.7\", \"python\": \"<3.7\"})\n    )\n\n    dataclasses = get_package(\"dataclasses\", \"0.7\")\n    dataclasses.python_versions = \">=3.6, <3.7\"\n\n    repo.add_package(dataclasses)\n    transaction = solver.solve()\n\n    check_solver_result(transaction, [{\"job\": \"install\", \"package\": dataclasses}])\n\n\ndef test_solver_can_resolve_transitive_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"requests\", \"^2.24.0\"))\n    package.add_dependency(Factory.create_dependency(\"PyOTA\", \"^2.1.0\"))\n\n    requests = get_package(\"requests\", \"2.24.0\")\n    requests.add_dependency(Factory.create_dependency(\"certifi\", \">=2017.4.17\"))\n    dep = get_dependency(\"PyOpenSSL\", \">=0.14\")\n    requests.add_dependency(\n        Factory.create_dependency(\"PyOpenSSL\", {\"version\": \">=0.14\", \"optional\": True})\n    )\n    requests.extras = {canonicalize_name(\"security\"): [dep]}\n    pyota = get_package(\"PyOTA\", \"2.1.0\")\n    pyota.add_dependency(\n        Factory.create_dependency(\n            \"requests\", {\"version\": \">=2.24.0\", \"extras\": [\"security\"]}\n        )\n    )\n\n    repo.add_package(requests)\n    repo.add_package(pyota)\n    repo.add_package(get_package(\"certifi\", \"2017.4.17\"))\n    repo.add_package(get_package(\"pyopenssl\", \"0.14\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": get_package(\"certifi\", \"2017.4.17\")},\n            {\"job\": \"install\", \"package\": get_package(\"pyopenssl\", \"0.14\")},\n            {\"job\": \"install\", \"package\": requests},\n            {\"job\": \"install\", \"package\": pyota},\n        ],\n    )\n\n\ndef test_solver_can_resolve_for_packages_with_missing_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\n            \"django-anymail\", {\"version\": \"^6.0\", \"extras\": [\"postmark\"]}\n        )\n    )\n\n    django_anymail = get_package(\"django-anymail\", \"6.1.0\")\n    django_anymail.add_dependency(Factory.create_dependency(\"django\", \">=2.0\"))\n    django_anymail.add_dependency(Factory.create_dependency(\"requests\", \">=2.4.3\"))\n    django_anymail.add_dependency(\n        Factory.create_dependency(\"boto3\", {\"version\": \"*\", \"optional\": True})\n    )\n    django_anymail.extras = {\n        canonicalize_name(\"amazon_ses\"): [Factory.create_dependency(\"boto3\", \"*\")]\n    }\n    django = get_package(\"django\", \"2.2.0\")\n    boto3 = get_package(\"boto3\", \"1.0.0\")\n    requests = get_package(\"requests\", \"2.24.0\")\n\n    repo.add_package(django_anymail)\n    repo.add_package(django)\n    repo.add_package(boto3)\n    repo.add_package(requests)\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": django},\n            {\"job\": \"install\", \"package\": requests},\n            {\"job\": \"install\", \"package\": django_anymail},\n        ],\n    )\n\n\ndef test_solver_can_resolve_python_restricted_package_dependencies(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"futures\", {\"version\": \"^3.3.0\", \"python\": \"~2.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"pre-commit\", {\"version\": \"^2.6\", \"python\": \"^3.6.1\"})\n    )\n\n    futures = Package(\"futures\", \"3.3.0\")\n    futures.python_versions = \">=2.6, <3\"\n\n    pre_commit = Package(\"pre-commit\", \"2.7.1\")\n    pre_commit.python_versions = \">=3.6.1\"\n\n    repo.add_package(futures)\n    repo.add_package(pre_commit)\n\n    solver = Solver(package, pool, [], [futures, pre_commit], io)\n    transaction = solver.solve(use_latest=[canonicalize_name(\"pre-commit\")])\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": futures},\n            {\"job\": \"install\", \"package\": pre_commit},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"virtualenv_before_pre_commit\", [False, True])\ndef test_solver_should_not_raise_errors_for_irrelevant_transitive_python_constraints(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    mocker: MockerFixture,\n    virtualenv_before_pre_commit: bool,\n) -> None:\n    package.python_versions = \"~2.7 || ^3.5\"\n    set_package_python_versions(solver.provider, \"~2.7 || ^3.5\")\n    package.add_dependency(Factory.create_dependency(\"virtualenv\", \"^20.4.3\"))\n    package.add_dependency(\n        Factory.create_dependency(\"pre-commit\", {\"version\": \"^2.6\", \"python\": \"^3.6.1\"})\n    )\n\n    virtualenv = get_package(\"virtualenv\", \"20.4.3\")\n    virtualenv.python_versions = \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7\"\n    virtualenv.add_dependency(\n        Factory.create_dependency(\n            \"importlib-resources\", {\"version\": \"*\", \"markers\": 'python_version < \"3.7\"'}\n        )\n    )\n\n    pre_commit = Package(\"pre-commit\", \"2.7.1\")\n    pre_commit.python_versions = \">=3.6.1\"\n    pre_commit.add_dependency(\n        Factory.create_dependency(\n            \"importlib-resources\", {\"version\": \"*\", \"markers\": 'python_version < \"3.7\"'}\n        )\n    )\n\n    importlib_resources = get_package(\"importlib-resources\", \"5.1.2\")\n    importlib_resources.python_versions = \">=3.6\"\n\n    importlib_resources_3_2_1 = get_package(\"importlib-resources\", \"3.2.1\")\n    importlib_resources_3_2_1.python_versions = (\n        \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\"\n    )\n\n    repo.add_package(virtualenv)\n    repo.add_package(pre_commit)\n    repo.add_package(importlib_resources)\n    repo.add_package(importlib_resources_3_2_1)\n\n    def patched_choose_next(unsatisfied: list[Dependency]) -> Dependency:\n        order = (\n            (\"root\", \"virtualenv\", \"pre-commit\", \"importlib-resources\")\n            if virtualenv_before_pre_commit\n            else (\"root\", \"pre-commit\", \"virtualenv\", \"importlib-resources\")\n        )\n        for preferred in order:\n            for dep in unsatisfied:\n                if dep.name == preferred:\n                    return dep\n        raise RuntimeError\n\n    mocker.patch(\n        \"poetry.mixology.version_solver.VersionSolver._choose_next\",\n        side_effect=patched_choose_next,\n    )\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": importlib_resources_3_2_1},\n            {\"job\": \"install\", \"package\": pre_commit},\n            {\"job\": \"install\", \"package\": virtualenv},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"numpy_before_pandas\", [False, True])\n@pytest.mark.parametrize(\"conflict\", [False, True])\ndef test_solver_should_not_raise_errors_for_irrelevant_transitive_python_constraints2(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    mocker: MockerFixture,\n    numpy_before_pandas: bool,\n    conflict: bool,\n) -> None:\n    \"\"\"This time with overrides.\"\"\"\n    package.python_versions = \">=3.6.2, <3.10\"\n    set_package_python_versions(solver.provider, \">=3.6.2, <3.10\")\n    package.add_dependency(Factory.create_dependency(\"pandas\", \">=1\"))\n    package.add_dependency(\n        Factory.create_dependency(\"numpy\", {\"version\": \">=1.20.0\", \"python\": \">=3.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"numpy\", {\"version\": \"*\", \"python\": \"<3.7\"})\n    )\n\n    pandas = get_package(\"pandas\", \"1.1.5\")\n    pandas.add_dependency(Factory.create_dependency(\"numpy\", \">=1.15\"))\n\n    numpy_19 = get_package(\"numpy\", \"1.19\")\n    numpy_19.python_versions = \">=3.6\"\n    numpy_20 = get_package(\"numpy\", \"1.20\")\n    numpy_20.python_versions = \">=3.8\" if conflict else \">=3.7\"\n\n    repo.add_package(pandas)\n    repo.add_package(numpy_19)\n    repo.add_package(numpy_20)\n\n    def patched_choose_next(unsatisfied: list[Dependency]) -> Dependency:\n        order = (\n            (\"root\", \"pandas\", \"numpy\")\n            if numpy_before_pandas\n            else (\"root\", \"numpy\", \"pandas\")\n        )\n        for preferred in order:\n            for dep in unsatisfied:\n                if dep.name == preferred:\n                    return dep\n        raise RuntimeError\n\n    mocker.patch(\n        \"poetry.mixology.version_solver.VersionSolver._choose_next\",\n        side_effect=patched_choose_next,\n    )\n\n    if conflict:\n        with pytest.raises(SolverProblemError):\n            solver.solve()\n    else:\n        transaction = solver.solve()\n\n        check_solver_result(\n            transaction,\n            [\n                {\"job\": \"install\", \"package\": numpy_19},\n                {\"job\": \"install\", \"package\": numpy_20},\n                {\"job\": \"install\", \"package\": pandas},\n            ],\n        )\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\ndef test_solver_keeps_multiple_locked_dependencies_for_same_package(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    is_locked: bool,\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"~1.1\", \"python\": \"<3.7\"})\n    )\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"~1.2\", \"python\": \">=3.7\"})\n    )\n\n    a11 = Package(\"A\", \"1.1\")\n    a12 = Package(\"A\", \"1.2\")\n\n    a11.add_dependency(Factory.create_dependency(\"B\", {\"version\": \">=0.3\"}))\n    a12.add_dependency(Factory.create_dependency(\"B\", {\"version\": \">=0.3\"}))\n\n    b03 = Package(\"B\", \"0.3\")\n    b04 = Package(\"B\", \"0.4\")\n    b04.python_versions = \">=3.6.2,<4.0.0\"\n\n    repo.add_package(a11)\n    repo.add_package(a12)\n    repo.add_package(b03)\n    repo.add_package(b04)\n\n    if is_locked:\n        a11_locked = a11.clone()\n        a11_locked.python_versions = \"<3.7\"\n        a12_locked = a12.clone()\n        a12_locked.python_versions = \">=3.7\"\n        locked = [a11_locked, a12_locked, b03.clone(), b04.clone()]\n    else:\n        locked = []\n\n    solver = Solver(package, pool, [], locked, io)\n    set_package_python_versions(solver.provider, \"^3.6\")\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": b03},\n            {\"job\": \"install\", \"package\": b04},\n            {\"job\": \"install\", \"package\": a11},\n            {\"job\": \"install\", \"package\": a12},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\ndef test_solver_does_not_update_ref_of_locked_vcs_package(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    is_locked: bool,\n) -> None:\n    locked_ref = \"123456\"\n    latest_ref = \"9cf87a285a2d3fbb0b9fa621997b3acc3631ed24\"\n    demo_locked = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=locked_ref,\n    )\n    demo_locked.add_dependency(Factory.create_dependency(\"pendulum\", \"*\"))\n    demo_latest = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=latest_ref,\n    )\n    locked = [demo_locked] if is_locked else []\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    # transitive dependencies of demo\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n\n    solver = Solver(package, pool, [], locked, io)\n    transaction = solver.solve()\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo_locked if is_locked else demo_latest},\n        ],\n    )\n\n    op = ops[1]\n\n    assert op.package.source_type == \"git\"\n    assert op.package.source_reference == DEFAULT_SOURCE_REF\n    assert (\n        op.package.source_resolved_reference == locked_ref if is_locked else latest_ref\n    )\n\n\ndef test_solver_does_not_fetch_locked_vcs_package_with_ref(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    mocker: MockerFixture,\n) -> None:\n    locked_ref = \"123456\"\n    demo_locked = Package(\n        \"demo\",\n        \"0.1.2\",\n        source_type=\"git\",\n        source_url=\"https://github.com/demo/demo.git\",\n        source_reference=DEFAULT_SOURCE_REF,\n        source_resolved_reference=locked_ref,\n    )\n    demo_locked.add_dependency(Factory.create_dependency(\"pendulum\", \"*\"))\n\n    package.add_dependency(\n        Factory.create_dependency(\"demo\", {\"git\": \"https://github.com/demo/demo.git\"})\n    )\n\n    # transitive dependencies of demo\n    pendulum = get_package(\"pendulum\", \"2.0.3\")\n    repo.add_package(pendulum)\n\n    solver = Solver(package, pool, [], [demo_locked], io)\n    spy = mocker.spy(solver._provider, \"_search_for_vcs\")\n\n    solver.solve()\n\n    spy.assert_not_called()\n\n\ndef test_solver_direct_origin_dependency_with_extras_requested_by_other_package(\n    solver: Solver,\n    repo: Repository,\n    package: ProjectPackage,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n) -> None:\n    \"\"\"\n    Another package requires the same dependency with extras that is required\n    by the project as direct origin dependency without any extras.\n    \"\"\"\n    pendulum = get_package(\"pendulum\", \"2.0.3\")  # required by demo\n    cleo = get_package(\"cleo\", \"1.0.0\")  # required by demo[foo]\n    demo_foo = get_package(\"demo-foo\", \"1.2.3\")\n    demo_foo.add_dependency(\n        Factory.create_dependency(\"demo\", {\"version\": \">=0.1\", \"extras\": [\"foo\"]})\n    )\n    repo.add_package(demo_foo)\n    repo.add_package(pendulum)\n    repo.add_package(cleo)\n\n    project_dir = shutil.copytree(\n        fixture_dir(\"git\") / \"github.com\" / \"demo\" / \"demo\",\n        tmp_path / \"project\",\n    )\n    path = project_dir.as_posix()\n\n    # project requires path dependency of demo while demo-foo requires demo[foo]\n    package.add_dependency(Factory.create_dependency(\"demo\", {\"path\": path}))\n    package.add_dependency(Factory.create_dependency(\"demo-foo\", \"^1.2.3\"))\n\n    transaction = solver.solve()\n\n    demo = Package(\"demo\", \"0.1.2\", source_type=\"directory\", source_url=path)\n\n    ops = check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": cleo},\n            {\"job\": \"install\", \"package\": pendulum},\n            {\"job\": \"install\", \"package\": demo},\n            {\"job\": \"install\", \"package\": demo_foo},\n        ],\n    )\n\n    op = ops[2]\n\n    assert op.package.name == \"demo\"\n    assert op.package.version.text == \"0.1.2\"\n    assert op.package.source_type == \"directory\"\n    assert op.package.source_url == path\n\n\ndef test_solver_incompatible_dependency_with_and_without_extras(\n    solver: Solver, repo: Repository, package: ProjectPackage\n) -> None:\n    \"\"\"\n    The solver first encounters a requirement for google-auth and then later an\n    incompatible requirement for google-auth[aiohttp].\n\n    Testcase derived from https://github.com/python-poetry/poetry/issues/6054.\n    \"\"\"\n    # Incompatible requirements from foo and bar2.\n    foo = get_package(\"foo\", \"1.0.0\")\n    foo.add_dependency(Factory.create_dependency(\"google-auth\", {\"version\": \"^1\"}))\n\n    bar = get_package(\"bar\", \"1.0.0\")\n\n    bar2 = get_package(\"bar\", \"2.0.0\")\n    bar2.add_dependency(\n        Factory.create_dependency(\n            \"google-auth\", {\"version\": \"^2\", \"extras\": [\"aiohttp\"]}\n        )\n    )\n\n    baz = get_package(\"baz\", \"1.0.0\")  # required by google-auth[aiohttp]\n\n    google_auth = get_package(\"google-auth\", \"1.2.3\")\n    google_auth.extras = {canonicalize_name(\"aiohttp\"): [get_dependency(\"baz\", \"^1.0\")]}\n\n    google_auth2 = get_package(\"google-auth\", \"2.3.4\")\n    google_auth2.extras = {\n        canonicalize_name(\"aiohttp\"): [get_dependency(\"baz\", \"^1.0\")]\n    }\n\n    repo.add_package(foo)\n    repo.add_package(bar)\n    repo.add_package(bar2)\n    repo.add_package(baz)\n    repo.add_package(google_auth)\n    repo.add_package(google_auth2)\n\n    package.add_dependency(Factory.create_dependency(\"foo\", \">=1\"))\n    package.add_dependency(Factory.create_dependency(\"bar\", \">=1\"))\n\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": google_auth},\n            {\"job\": \"install\", \"package\": bar},\n            {\"job\": \"install\", \"package\": foo},\n        ],\n    )\n\n\ndef test_update_with_prerelease_and_no_solution(\n    package: ProjectPackage, repo: Repository, pool: RepositoryPool, io: NullIO\n) -> None:\n    # Locked and installed: cleo which depends on an old version of crashtest.\n    cleo = get_package(\"cleo\", \"1.0.0a5\")\n    crashtest = get_package(\"crashtest\", \"0.3.0\")\n    cleo.add_dependency(Factory.create_dependency(\"crashtest\", {\"version\": \"<0.4.0\"}))\n    installed = [cleo, crashtest]\n    locked = [cleo, crashtest]\n\n    # Try to upgrade to a new version of crashtest, this will be disallowed by the\n    # dependency from cleo.\n    package.add_dependency(Factory.create_dependency(\"cleo\", \"^1.0.0a5\"))\n    package.add_dependency(Factory.create_dependency(\"crashtest\", \"^0.4.0\"))\n\n    newer_crashtest = get_package(\"crashtest\", \"0.4.0\")\n    even_newer_crashtest = get_package(\"crashtest\", \"0.4.1\")\n    repo.add_package(cleo)\n    repo.add_package(crashtest)\n    repo.add_package(newer_crashtest)\n    repo.add_package(even_newer_crashtest)\n\n    solver = Solver(package, pool, installed, locked, io)\n\n    with pytest.raises(SolverProblemError):\n        solver.solve()\n\n\ndef test_solver_yanked_warning(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n) -> None:\n    package.add_dependency(Factory.create_dependency(\"foo\", \"==1\"))\n    package.add_dependency(Factory.create_dependency(\"bar\", \"==2\"))\n    package.add_dependency(Factory.create_dependency(\"baz\", \"==3\"))\n    foo = get_package(\"foo\", \"1\", yanked=False)\n    bar = get_package(\"bar\", \"2\", yanked=True)\n    baz = get_package(\"baz\", \"3\", yanked=\"just wrong\")\n    repo.add_package(foo)\n    repo.add_package(bar)\n    repo.add_package(baz)\n\n    io = BufferedIO(decorated=False)\n    solver = Solver(package, pool, [], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": bar},\n            {\"job\": \"install\", \"package\": baz},\n            {\"job\": \"install\", \"package\": foo},\n        ],\n    )\n    error = io.fetch_error()\n    assert \"foo\" not in error\n    assert \"The locked version 2 for bar is a yanked version.\" in error\n    assert (\n        \"The locked version 3 for baz is a yanked version. Reason for being yanked:\"\n        \" just wrong\" in error\n    )\n    assert error.count(\"is a yanked version\") == 2\n    assert error.count(\"Reason for being yanked\") == 1\n\n\n@pytest.mark.parametrize(\"is_locked\", [False, True])\ndef test_update_with_use_latest_vs_lock(\n    package: ProjectPackage,\n    repo: Repository,\n    pool: RepositoryPool,\n    io: NullIO,\n    is_locked: bool,\n) -> None:\n    \"\"\"\n    A1 depends on B2, A2 and A3 depend on B1. Same for C.\n    B1 depends on A2/C2, B2 depends on A1/C1.\n\n    Because there are more versions of B than of A and C, B is resolved first\n    so that latest version of B is used.\n    There shouldn't be a difference between `poetry lock` (not is_locked)\n    and `poetry update` (is_locked + use_latest)\n    \"\"\"\n    # B added between A and C (and also alphabetically between)\n    # to ensure that neither the first nor the last one is resolved first\n    package.add_dependency(Factory.create_dependency(\"A\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"B\", \"*\"))\n    package.add_dependency(Factory.create_dependency(\"C\", \"*\"))\n\n    package_a1 = get_package(\"A\", \"1\")\n    package_a1.add_dependency(Factory.create_dependency(\"B\", \"3\"))\n    package_a2 = get_package(\"A\", \"2\")\n    package_a2.add_dependency(Factory.create_dependency(\"B\", \"1\"))\n\n    package_c1 = get_package(\"C\", \"1\")\n    package_c1.add_dependency(Factory.create_dependency(\"B\", \"3\"))\n    package_c2 = get_package(\"C\", \"2\")\n    package_c2.add_dependency(Factory.create_dependency(\"B\", \"1\"))\n\n    package_b1 = get_package(\"B\", \"1\")\n    package_b1.add_dependency(Factory.create_dependency(\"A\", \"2\"))\n    package_b1.add_dependency(Factory.create_dependency(\"C\", \"2\"))\n    package_b2 = get_package(\"B\", \"2\")\n    package_b2.add_dependency(Factory.create_dependency(\"A\", \"1\"))\n    package_b2.add_dependency(Factory.create_dependency(\"C\", \"1\"))\n    package_b3 = get_package(\"B\", \"3\")\n    package_b3.add_dependency(Factory.create_dependency(\"A\", \"1\"))\n    package_b3.add_dependency(Factory.create_dependency(\"C\", \"1\"))\n\n    repo.add_package(package_a1)\n    repo.add_package(package_a2)\n    repo.add_package(package_b1)\n    repo.add_package(package_b2)\n    repo.add_package(package_b3)\n    repo.add_package(package_c1)\n    repo.add_package(package_c2)\n\n    if is_locked:\n        locked = [package_a1, package_b3, package_c1]\n        use_latest = [package.name for package in locked]\n    else:\n        locked = []\n        use_latest = []\n\n    solver = Solver(package, pool, [], locked, io)\n    transaction = solver.solve(use_latest)\n\n    check_solver_result(\n        transaction,\n        [\n            {\"job\": \"install\", \"package\": package_c1},\n            {\"job\": \"install\", \"package\": package_b3},\n            {\"job\": \"install\", \"package\": package_a1},\n        ],\n    )\n\n\n@pytest.mark.parametrize(\"with_extra\", [False, True])\ndef test_solver_resolves_duplicate_dependency_in_extra(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n    io: NullIO,\n    with_extra: bool,\n) -> None:\n    \"\"\"\n    Without extras, a newer version of B can be chosen than with extras.\n    See https://github.com/python-poetry/poetry/issues/8380.\n    \"\"\"\n    constraint: dict[str, Any] = {\"version\": \"*\"}\n    if with_extra:\n        constraint[\"extras\"] = [\"foo\"]\n    package.add_dependency(Factory.create_dependency(\"A\", constraint))\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b1 = get_package(\"B\", \"1.0\")\n    package_b2 = get_package(\"B\", \"2.0\")\n\n    dep = get_dependency(\"B\", \">=1.0\")\n    package_a.add_dependency(dep)\n\n    dep_extra = get_dependency(\"B\", \"^1.0\", optional=True)\n    dep_extra.marker = parse_marker(\"extra == 'foo'\")\n    package_a.extras = {canonicalize_name(\"foo\"): [dep_extra]}\n    package_a.add_dependency(dep_extra)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b1)\n    repo.add_package(package_b2)\n\n    solver = Solver(package, pool, [], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        (\n            [\n                {\"job\": \"install\", \"package\": package_b1 if with_extra else package_b2},\n                {\"job\": \"install\", \"package\": package_a},\n            ]\n        ),\n    )\n\n\ndef test_solver_resolves_conflicting_dependency_in_root_extra(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n    io: NullIO,\n) -> None:\n    package_a1 = get_package(\"A\", \"1.0\")\n    package_a2 = get_package(\"A\", \"2.0\")\n\n    dep = get_dependency(\"A\", {\"version\": \"1.0\", \"markers\": \"extra != 'foo'\"})\n    package.add_dependency(dep)\n\n    dep_extra = get_dependency(\"A\", \"2.0\", optional=True)\n    dep_extra._in_extras = [canonicalize_name(\"foo\")]\n    package.extras = {canonicalize_name(\"foo\"): [dep_extra]}\n    package.add_dependency(dep_extra)\n\n    repo.add_package(package_a1)\n    repo.add_package(package_a2)\n\n    solver = Solver(package, pool, [], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        (\n            [\n                {\"job\": \"install\", \"package\": package_a1},\n                {\"job\": \"install\", \"package\": package_a2},\n            ]\n        ),\n    )\n    solved_packages = transaction.get_solved_packages()\n    assert solved_packages[package_a1].markers[MAIN_GROUP] == parse_marker(\n        \"extra != 'foo'\"\n    )\n    assert solved_packages[package_a2].markers[MAIN_GROUP] == parse_marker(\n        \"extra == 'foo'\"\n    )\n\n\ndef test_solver_resolves_conflicting_dependency_in_root_extras(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n    io: NullIO,\n) -> None:\n    package_a1 = get_package(\"A\", \"1.0\")\n    package_a2 = get_package(\"A\", \"2.0\")\n\n    dep_extra1 = get_dependency(  # extra == 'foo' is implicit via _in_extras!\n        \"A\", {\"version\": \"1.0\", \"markers\": \"extra != 'bar'\"}, optional=True\n    )\n    dep_extra1._in_extras = [canonicalize_name(\"foo\")]\n    package.add_dependency(dep_extra1)\n\n    dep_extra2 = get_dependency(  # extra == 'bar' is implicit via _in_extras!\n        \"A\", {\"version\": \"2.0\", \"markers\": \"extra != 'foo'\"}, optional=True\n    )\n    dep_extra2._in_extras = [canonicalize_name(\"bar\")]\n    package.extras = {\n        canonicalize_name(\"foo\"): [dep_extra1],\n        canonicalize_name(\"bar\"): [dep_extra2],\n    }\n    package.add_dependency(dep_extra1)\n    package.add_dependency(dep_extra2)\n\n    repo.add_package(package_a1)\n    repo.add_package(package_a2)\n\n    solver = Solver(package, pool, [], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        (\n            [\n                {\"job\": \"install\", \"package\": package_a1},\n                {\"job\": \"install\", \"package\": package_a2},\n            ]\n        ),\n    )\n    solved_packages = transaction.get_solved_packages()\n    assert solved_packages[package_a1].markers[MAIN_GROUP] == parse_marker(\n        \"extra != 'bar' and extra == 'foo'\"\n    )\n    assert solved_packages[package_a2].markers[MAIN_GROUP] == parse_marker(\n        \"extra != 'foo' and extra == 'bar'\"\n    )\n\n\n@pytest.mark.parametrize(\"with_extra\", [False, True])\ndef test_solver_resolves_duplicate_dependency_in_root_extra_for_installation(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n    io: NullIO,\n    with_extra: bool,\n) -> None:\n    \"\"\"\n    Without extras, a newer version of A can be chosen than with root extras.\n    \"\"\"\n    extra = [canonicalize_name(\"foo\")] if with_extra else []\n\n    package_a1 = get_package(\"A\", \"1.0\")\n    package_a2 = get_package(\"A\", \"2.0\")\n\n    dep = get_dependency(\"A\", \">=1.0\")\n    package.add_dependency(dep)\n\n    dep_extra = get_dependency(\"A\", \"^1.0\", optional=True)\n    dep_extra.marker = parse_marker(\"extra == 'foo'\")\n    package.extras = {canonicalize_name(\"foo\"): [dep_extra]}\n    package.add_dependency(dep_extra)\n\n    repo.add_package(package_a1)\n    repo.add_package(package_a2)\n\n    solver = Solver(\n        package, pool, [], [package_a1, package_a2], io, active_root_extras=extra\n    )\n    with solver.use_environment(MockEnv()):\n        transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        (\n            [\n                {\"job\": \"install\", \"package\": package_a1 if with_extra else package_a2},\n            ]\n        ),\n    )\n\n\ndef test_solver_resolves_duplicate_dependencies_with_restricted_extras(\n    package: ProjectPackage,\n    pool: RepositoryPool,\n    repo: Repository,\n    io: NullIO,\n) -> None:\n    package.add_dependency(\n        Factory.create_dependency(\"A\", {\"version\": \"*\", \"extras\": [\"foo\"]})\n    )\n\n    package_a = get_package(\"A\", \"1.0\")\n    package_b1 = get_package(\"B\", \"1.0\")\n    package_b2 = get_package(\"B\", \"2.0\")\n\n    dep1 = get_dependency(\"B\", \"^1.0\", optional=True)\n    dep1.marker = parse_marker(\"sys_platform == 'win32' and extra == 'foo'\")\n    dep2 = get_dependency(\"B\", \"^2.0\", optional=True)\n    dep2.marker = parse_marker(\"sys_platform == 'linux' and extra == 'foo'\")\n    package_a.extras = {canonicalize_name(\"foo\"): [dep1, dep2]}\n    package_a.add_dependency(dep1)\n    package_a.add_dependency(dep2)\n\n    repo.add_package(package_a)\n    repo.add_package(package_b1)\n    repo.add_package(package_b2)\n\n    solver = Solver(package, pool, [], [], io)\n    transaction = solver.solve()\n\n    check_solver_result(\n        transaction,\n        (\n            [\n                {\"job\": \"install\", \"package\": package_b1},\n                {\"job\": \"install\", \"package\": package_b2},\n                {\"job\": \"install\", \"package\": package_a},\n            ]\n        ),\n    )\n"
  },
  {
    "path": "tests/puzzle/test_solver_internals.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.package import Package\nfrom poetry.core.version.markers import AnyMarker\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.factory import Factory\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\nfrom poetry.puzzle.solver import PackageNode\nfrom poetry.puzzle.solver import Solver\nfrom poetry.puzzle.solver import depth_first_search\nfrom poetry.puzzle.solver import merge_override_packages\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n    from collections.abc import Sequence\n\n    from poetry.core.packages.project_package import ProjectPackage\n\n\nDEV_GROUP = canonicalize_name(\"dev\")\n\n\ndef dep(\n    name: str,\n    marker: str = \"\",\n    extras: Iterable[str] = (),\n    in_extras: Sequence[str] = (),\n    groups: Iterable[str] = (),\n) -> Dependency:\n    d = Dependency(name, \"1\", groups=groups, extras=extras)\n    d._in_extras = [canonicalize_name(e) for e in in_extras]\n    if marker:\n        d.marker = marker\n    return d\n\n\ndef tm(info: TransitivePackageInfo) -> dict[str, str]:\n    return {key: str(value) for key, value in info.markers.items()}\n\n\ndef test_dfs_depth(package: ProjectPackage) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    packages = [package, a, b, c]\n    package.add_dependency(dep(\"a\"))\n    package.add_dependency(dep(\"b\"))\n    a.add_dependency(dep(\"b\"))\n    b.add_dependency(dep(\"c\"))\n\n    result, __ = depth_first_search(PackageNode(package, packages))\n    depths = {\n        nodes[0].package.complete_name: [node.depth for node in nodes]\n        for nodes in result\n    }\n\n    assert depths == {\"root\": [-1], \"a\": [0], \"b\": [1], \"c\": [2]}\n\n\ndef test_dfs_depth_with_cycle(package: ProjectPackage) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    packages = [package, a, b, c]\n    package.add_dependency(dep(\"a\"))\n    package.add_dependency(dep(\"b\"))\n    a.add_dependency(dep(\"b\"))\n    b.add_dependency(dep(\"a\"))\n    a.add_dependency(dep(\"c\"))\n\n    result, __ = depth_first_search(PackageNode(package, packages))\n    depths = {\n        nodes[0].package.complete_name: [node.depth for node in nodes]\n        for nodes in result\n    }\n\n    assert depths == {\"root\": [-1], \"a\": [0], \"b\": [1], \"c\": [1]}\n\n\ndef test_dfs_depth_with_extra(package: ProjectPackage) -> None:\n    a_foo = Package(\"a\", \"1\", features=[\"foo\"])\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    packages = [package, a_foo, a, b, c]\n    package.add_dependency(dep(\"a\", extras=[\"foo\"]))\n    a_foo.add_dependency(dep(\"a\"))\n    a_foo.add_dependency(dep(\"b\"))\n    a_foo.add_dependency(dep(\"c\", 'extra == \"foo\"'))\n    a.add_dependency(dep(\"b\"))\n\n    result, __ = depth_first_search(PackageNode(package, packages))\n    depths = {\n        nodes[0].package.complete_name: [node.depth for node in nodes]\n        for nodes in result\n    }\n\n    assert depths == {\"root\": [-1], \"a[foo]\": [0], \"a\": [0], \"b\": [1], \"c\": [1]}\n\n\ndef test_propagate_markers(package: ProjectPackage, solver: Solver) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    d = Package(\"d\", \"1\")\n    e = Package(\"e\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"'))\n    package.add_dependency(dep(\"b\", 'sys_platform == \"linux\"'))\n    a.add_dependency(dep(\"c\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"d\", 'python_version == \"3.9\"'))\n    a.add_dependency(dep(\"e\", 'python_version == \"3.10\"'))\n    b.add_dependency(dep(\"e\", 'python_version == \"3.11\"'))\n\n    packages = [package, a, b, c, d, e]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == 6\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": 'sys_platform == \"win32\"'}\n    assert tm(result[b]) == {\"main\": 'sys_platform == \"linux\"'}\n    assert tm(result[c]) == {\n        \"main\": 'sys_platform == \"win32\" and python_version == \"3.8\"'\n    }\n    assert tm(result[d]) == {\n        \"main\": 'sys_platform == \"linux\" and python_version == \"3.9\"'\n    }\n    assert tm(result[e]) == {\n        \"main\": 'sys_platform == \"win32\" and python_version == \"3.10\"'\n        ' or sys_platform == \"linux\" and python_version == \"3.11\"'\n    }\n\n\ndef test_propagate_markers_same_name(package: ProjectPackage, solver: Solver) -> None:\n    urls = {\n        \"linux\": \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\",\n        \"win32\": (\n            \"https://files.pythonhosted.org/distributions/demo-0.1.0-py2.py3-none-any.whl\"\n        ),\n    }\n    sdist = Package(\"demo\", \"0.1.0\", source_type=\"url\", source_url=urls[\"linux\"])\n    wheel = Package(\"demo\", \"0.1.0\", source_type=\"url\", source_url=urls[\"win32\"])\n    for platform, url in urls.items():\n        package.add_dependency(\n            Factory.create_dependency(\n                \"demo\",\n                {\"url\": url, \"markers\": f\"sys_platform == '{platform}'\"},\n            )\n        )\n\n    packages = [package, sdist, wheel]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == 3\n    assert tm(result[package]) == {}\n    assert tm(result[sdist]) == {\"main\": 'sys_platform == \"linux\"'}\n    assert tm(result[wheel]) == {\"main\": 'sys_platform == \"win32\"'}\n\n\ndef test_propagate_markers_with_extra(package: ProjectPackage, solver: Solver) -> None:\n    a_foo = Package(\"a\", \"1\", features=[\"foo\"])\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    d = Package(\"d\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"', extras=[\"foo\"]))\n    package.add_dependency(dep(\"b\", 'sys_platform == \"linux\"'))\n    a_foo.add_dependency(dep(\"a\"))\n    a_foo.add_dependency(dep(\"c\", 'python_version == \"3.8\"'))\n    a_foo.add_dependency(dep(\"d\", 'extra == \"foo\"'))\n    a.add_dependency(dep(\"c\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"a\", 'python_version == \"3.9\"'))\n\n    packages = [package, a_foo, a, b, c, d]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages) - 1\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\n        \"main\": (\n            'sys_platform == \"linux\" and python_version == \"3.9\" or sys_platform == \"win32\"'\n        )\n    }\n    assert tm(result[b]) == {\"main\": 'sys_platform == \"linux\"'}\n    assert tm(result[c]) == {\n        \"main\": 'sys_platform == \"win32\" and python_version == \"3.8\"'\n    }\n    assert tm(result[d]) == {\"main\": 'sys_platform == \"win32\"'}\n\n\ndef test_propagate_markers_with_root_extra(\n    package: ProjectPackage, solver: Solver\n) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    d = Package(\"d\", \"1\")\n    # \"extra\" is not present in the marker of an extra dependency of the root package,\n    # there is only \"in_extras\"...\n    package.add_dependency(dep(\"a\", in_extras=[\"foo\"]))\n    package.add_dependency(\n        dep(\"b\", 'sys_platform == \"linux\"', in_extras=[\"foo\", \"bar\"])\n    )\n    a.add_dependency(dep(\"c\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"d\", 'python_version == \"3.9\"'))\n\n    packages = [package, a, b, c, d]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": 'extra == \"foo\"'}\n    assert tm(result[b]) == {\n        \"main\": 'sys_platform == \"linux\" and (extra == \"foo\" or extra == \"bar\")',\n    }\n    assert tm(result[c]) == {\"main\": 'extra == \"foo\" and python_version == \"3.8\"'}\n    assert tm(result[d]) == {\n        \"main\": (\n            'sys_platform == \"linux\" and (extra == \"foo\" or extra == \"bar\")'\n            ' and python_version == \"3.9\"'\n        )\n    }\n\n\ndef test_propagate_markers_with_duplicate_dependency_root_extra(\n    package: ProjectPackage, solver: Solver\n) -> None:\n    a = Package(\"a\", \"1\")\n    package.add_dependency(dep(\"a\"))\n    # \"extra\" is not present in the marker of an extra dependency of the root package,\n    # there is only \"in_extras\"...\n    package.add_dependency(dep(\"a\", in_extras=[\"foo\"]))\n\n    packages = [package, a]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": \"\"}  # not \"extra == 'foo'\" !\n\n\ndef test_propagate_groups_with_extra(package: ProjectPackage, solver: Solver) -> None:\n    a_foo = Package(\"a\", \"1\", features=[\"foo\"])\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    package.add_dependency(dep(\"a\", groups=[\"main\"]))\n    package.add_dependency(dep(\"a\", groups=[\"dev\"], extras=[\"foo\"]))\n    a_foo.add_dependency(dep(\"a\"))\n    a_foo.add_dependency(dep(\"b\"))\n    a_foo.add_dependency(dep(\"c\", 'extra == \"foo\"'))\n    a.add_dependency(dep(\"b\"))\n\n    packages = [package, a_foo, a, b, c]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages) - 1\n    assert result[package].groups == set()\n    assert result[a].groups == {\"main\", \"dev\"}\n    assert result[b].groups == {\"main\", \"dev\"}\n    assert result[c].groups == {\"dev\"}\n\n\ndef test_propagate_markers_for_groups1(package: ProjectPackage, solver: Solver) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"', groups=[\"main\"]))\n    package.add_dependency(dep(\"b\", 'sys_platform == \"linux\"', groups=[\"dev\"]))\n    a.add_dependency(dep(\"c\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"c\", 'python_version == \"3.9\"'))\n\n    packages = [package, a, b, c]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert result[package].groups == set()\n    assert result[a].groups == {\"main\"}\n    assert result[b].groups == {\"dev\"}\n    assert result[c].groups == {\"main\", \"dev\"}\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": 'sys_platform == \"win32\"'}\n    assert tm(result[b]) == {\"dev\": 'sys_platform == \"linux\"'}\n    assert tm(result[c]) == {\n        \"main\": 'sys_platform == \"win32\" and python_version == \"3.8\"',\n        \"dev\": 'sys_platform == \"linux\" and python_version == \"3.9\"',\n    }\n\n\ndef test_propagate_markers_for_groups2(package: ProjectPackage, solver: Solver) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    c = Package(\"c\", \"1\")\n    d = Package(\"d\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"', groups=[\"main\"]))\n    package.add_dependency(dep(\"b\", 'sys_platform == \"linux\"', groups=[\"dev\"]))\n    package.add_dependency(dep(\"c\", 'sys_platform == \"darwin\"', groups=[\"main\", \"dev\"]))\n    a.add_dependency(dep(\"d\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"d\", 'python_version == \"3.9\"'))\n    c.add_dependency(dep(\"d\", 'python_version == \"3.10\"'))\n\n    packages = [package, a, b, c, d]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert result[package].groups == set()\n    assert result[a].groups == {\"main\"}\n    assert result[b].groups == {\"dev\"}\n    assert result[c].groups == {\"main\", \"dev\"}\n    assert result[d].groups == {\"main\", \"dev\"}\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": 'sys_platform == \"win32\"'}\n    assert tm(result[b]) == {\"dev\": 'sys_platform == \"linux\"'}\n    assert tm(result[c]) == {\n        \"main\": 'sys_platform == \"darwin\"',\n        \"dev\": 'sys_platform == \"darwin\"',\n    }\n    assert tm(result[d]) == {\n        \"main\": (\n            'sys_platform == \"win32\" and python_version == \"3.8\"'\n            ' or sys_platform == \"darwin\" and python_version == \"3.10\"'\n        ),\n        \"dev\": (\n            'sys_platform == \"darwin\" and python_version == \"3.10\"'\n            ' or sys_platform == \"linux\" and python_version == \"3.9\"'\n        ),\n    }\n\n\ndef test_propagate_markers_for_groups_same_dep(\n    package: ProjectPackage, solver: Solver\n) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"', groups=[\"main\"]))\n    package.add_dependency(dep(\"a\", 'sys_platform == \"linux\"', groups=[\"dev\"]))\n    a.add_dependency(dep(\"b\", 'python_version == \"3.8\"'))\n\n    packages = [package, a, b]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert result[package].groups == set()\n    assert result[a].groups == {\"main\", \"dev\"}\n    assert result[b].groups == {\"main\", \"dev\"}\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\n        \"main\": 'sys_platform == \"win32\"',\n        \"dev\": 'sys_platform == \"linux\"',\n    }\n    assert tm(result[b]) == {\n        \"main\": 'sys_platform == \"win32\" and python_version == \"3.8\"',\n        \"dev\": 'sys_platform == \"linux\" and python_version == \"3.8\"',\n    }\n\n\ndef test_propagate_markers_for_groups_with_extra(\n    package: ProjectPackage, solver: Solver\n) -> None:\n    a = Package(\"a\", \"1\")\n    package.add_dependency(dep(\"a\", groups=[\"main\"], in_extras=[\"foo\"]))\n    package.add_dependency(dep(\"a\", groups=[\"dev\"]))\n\n    packages = [package, a]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == len(packages)\n    assert result[package].groups == set()\n    assert result[a].groups == {\"main\", \"dev\"}\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\"main\": 'extra == \"foo\"', \"dev\": \"\"}\n\n\ndef test_propagate_markers_with_cycle(package: ProjectPackage, solver: Solver) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n    package.add_dependency(dep(\"a\", 'sys_platform == \"win32\"'))\n    package.add_dependency(dep(\"b\", 'sys_platform == \"linux\"'))\n    a.add_dependency(dep(\"b\", 'python_version == \"3.8\"'))\n    b.add_dependency(dep(\"a\", 'python_version == \"3.9\"'))\n\n    packages = [package, a, b]\n    result = solver._aggregate_solved_packages(packages)\n\n    assert len(result) == 3\n    assert tm(result[package]) == {}\n    assert tm(result[a]) == {\n        \"main\": (\n            'sys_platform == \"linux\" and python_version == \"3.9\"'\n            ' or sys_platform == \"win32\"'\n        )\n    }\n    assert tm(result[b]) == {\n        \"main\": (\n            'sys_platform == \"win32\" and python_version == \"3.8\"'\n            ' or sys_platform == \"linux\"'\n        )\n    }\n\n\ndef test_merge_override_packages_restricted(package: ProjectPackage) -> None:\n    \"\"\"Markers of dependencies should be intersected with override markers.\"\"\"\n    a = Package(\"a\", \"1\")\n\n    packages = merge_override_packages(\n        [\n            (\n                {package: {\"a\": dep(\"b\", 'python_version < \"3.9\"')}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {MAIN_GROUP: parse_marker(\"sys_platform == 'win32'\")},\n                    )\n                },\n            ),\n            (\n                {package: {\"a\": dep(\"b\", 'python_version >= \"3.9\"')}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {MAIN_GROUP: parse_marker(\"sys_platform == 'linux'\")},\n                    )\n                },\n            ),\n        ],\n        parse_constraint(\"*\"),\n    )\n    assert len(packages) == 1\n    assert packages[a].groups == {\"main\"}\n    assert tm(packages[a]) == {\n        \"main\": (\n            'python_version < \"3.9\" and sys_platform == \"win32\"'\n            ' or sys_platform == \"linux\" and python_version >= \"3.9\"'\n        )\n    }\n\n\ndef test_merge_override_packages_extras(package: ProjectPackage) -> None:\n    \"\"\"Extras from overrides should not be visible in the resulting marker.\"\"\"\n    a = Package(\"a\", \"1\")\n\n    packages = merge_override_packages(\n        [\n            (\n                {package: {\"a\": dep(\"b\", 'python_version < \"3.9\" and extra == \"foo\"')}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {MAIN_GROUP: parse_marker(\"sys_platform == 'win32'\")},\n                    )\n                },\n            ),\n            (\n                {\n                    package: {\n                        \"a\": dep(\"b\", 'python_version >= \"3.9\" and extra == \"foo\"')\n                    }\n                },\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {MAIN_GROUP: parse_marker(\"sys_platform == 'linux'\")},\n                    )\n                },\n            ),\n        ],\n        parse_constraint(\"*\"),\n    )\n    assert len(packages) == 1\n    assert packages[a].groups == {\"main\"}\n    assert tm(packages[a]) == {\n        \"main\": (\n            'python_version < \"3.9\" and sys_platform == \"win32\"'\n            ' or sys_platform == \"linux\" and python_version >= \"3.9\"'\n        )\n    }\n\n\n@pytest.mark.parametrize(\n    (\"python_constraint\", \"expected\"),\n    [\n        (\">=3.8\", 'python_version > \"3.8\" or sys_platform != \"linux\"'),\n        (\">=3.9\", \"\"),\n    ],\n)\ndef test_merge_override_packages_python_constraint(\n    package: ProjectPackage, python_constraint: str, expected: str\n) -> None:\n    \"\"\"The resulting marker depends on the project's python constraint.\"\"\"\n    a = Package(\"a\", \"1\")\n\n    packages = merge_override_packages(\n        [\n            (\n                {\n                    package: {\n                        \"a\": dep(\n                            \"b\", \"sys_platform == 'linux' and python_version > '3.8'\"\n                        )\n                    }\n                },\n                {a: TransitivePackageInfo(0, {MAIN_GROUP}, {MAIN_GROUP: AnyMarker()})},\n            ),\n            (\n                {package: {\"a\": dep(\"b\", \"sys_platform != 'linux'\")}},\n                {a: TransitivePackageInfo(0, {MAIN_GROUP}, {MAIN_GROUP: AnyMarker()})},\n            ),\n        ],\n        parse_constraint(python_constraint),\n    )\n    assert len(packages) == 1\n    assert packages[a].groups == {\"main\"}\n    assert tm(packages[a]) == {\"main\": expected}\n\n\ndef test_merge_override_packages_multiple_deps(package: ProjectPackage) -> None:\n    \"\"\"All override markers should be intersected.\"\"\"\n    a = Package(\"a\", \"1\")\n\n    packages = merge_override_packages(\n        [\n            (\n                {\n                    package: {\n                        \"a\": dep(\"b\", 'python_version < \"3.9\"'),\n                        \"c\": dep(\"d\", 'sys_platform == \"linux\"'),\n                    },\n                    a: {\"e\": dep(\"f\", 'python_version >= \"3.8\"')},\n                },\n                {a: TransitivePackageInfo(0, {MAIN_GROUP}, {MAIN_GROUP: AnyMarker()})},\n            ),\n        ],\n        parse_constraint(\"*\"),\n    )\n\n    assert len(packages) == 1\n    assert packages[a].groups == {\"main\"}\n    assert tm(packages[a]) == {\n        \"main\": 'python_version == \"3.8\" and sys_platform == \"linux\"'\n    }\n\n\ndef test_merge_override_packages_groups(package: ProjectPackage) -> None:\n    a = Package(\"a\", \"1\")\n    b = Package(\"b\", \"1\")\n\n    packages = merge_override_packages(\n        [\n            (\n                {package: {\"a\": dep(\"b\", 'python_version < \"3.9\"')}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {MAIN_GROUP: parse_marker(\"sys_platform == 'win32'\")},\n                    ),\n                    b: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP, DEV_GROUP},\n                        {\n                            MAIN_GROUP: parse_marker(\"sys_platform == 'win32'\"),\n                            DEV_GROUP: parse_marker(\"sys_platform == 'linux'\"),\n                        },\n                    ),\n                },\n            ),\n            (\n                {package: {\"a\": dep(\"b\", 'python_version >= \"3.9\"')}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {DEV_GROUP},\n                        {DEV_GROUP: parse_marker(\"sys_platform == 'linux'\")},\n                    ),\n                    b: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP, DEV_GROUP},\n                        {\n                            MAIN_GROUP: parse_marker(\"platform_machine == 'amd64'\"),\n                            DEV_GROUP: parse_marker(\"platform_machine == 'aarch64'\"),\n                        },\n                    ),\n                },\n            ),\n        ],\n        parse_constraint(\"*\"),\n    )\n    assert len(packages) == 2\n    assert packages[a].groups == {\"main\", \"dev\"}\n    assert tm(packages[a]) == {\n        \"main\": 'python_version < \"3.9\" and sys_platform == \"win32\"',\n        \"dev\": 'python_version >= \"3.9\" and sys_platform == \"linux\"',\n    }\n    assert packages[b].groups == {\"main\", \"dev\"}\n    assert tm(packages[b]) == {\n        \"main\": (\n            'python_version < \"3.9\" and sys_platform == \"win32\"'\n            ' or python_version >= \"3.9\" and platform_machine == \"amd64\"'\n        ),\n        \"dev\": (\n            'python_version < \"3.9\" and sys_platform == \"linux\"'\n            ' or python_version >= \"3.9\" and platform_machine == \"aarch64\"'\n        ),\n    }\n\n\ndef test_merge_override_packages_shortcut(package: ProjectPackage) -> None:\n    a = Package(\"a\", \"1\")\n    common_marker = (\n        'extra == \"test\" and sys_platform == \"win32\" or platform_system == \"Windows\"'\n        ' or sys_platform == \"linux\" and extra == \"stretch\"'\n    )\n    override_marker1 = 'python_version >= \"3.12\" and platform_system != \"Emscripten\"'\n    override_marker2 = 'python_version >= \"3.12\" and platform_system == \"Emscripten\"'\n\n    packages = merge_override_packages(\n        [\n            (\n                {package: {\"a\": dep(\"b\", override_marker1)}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {\n                            MAIN_GROUP: parse_marker(\n                                f\"{override_marker1} and ({common_marker})\"\n                            )\n                        },\n                    )\n                },\n            ),\n            (\n                {package: {\"a\": dep(\"b\", override_marker2)}},\n                {\n                    a: TransitivePackageInfo(\n                        0,\n                        {MAIN_GROUP},\n                        {\n                            MAIN_GROUP: parse_marker(\n                                f\"{override_marker2} and ({common_marker})\"\n                            )\n                        },\n                    )\n                },\n            ),\n        ],\n        parse_constraint(\"*\"),\n    )\n    assert len(packages) == 1\n    assert packages[a].groups == {\"main\"}\n    assert tm(packages[a]) == {\n        \"main\": f'({common_marker}) and python_version >= \"3.12\"'\n    }\n\n\n# TODO: root extras\n"
  },
  {
    "path": "tests/puzzle/test_transaction.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.project_package import ProjectPackage\nfrom poetry.core.version.markers import AnyMarker\nfrom poetry.core.version.markers import parse_marker\n\nfrom poetry.installation.operations.update import Update\nfrom poetry.packages.transitive_package_info import TransitivePackageInfo\nfrom poetry.puzzle.transaction import Transaction\n\n\nif TYPE_CHECKING:\n    from poetry.installation.operations.operation import Operation\n\n\nDEV_GROUP = canonicalize_name(\"dev\")\n\n\ndef get_transitive_info(depth: int) -> TransitivePackageInfo:\n    return TransitivePackageInfo(depth, set(), {})\n\n\ndef check_operations(ops: list[Operation], expected: list[dict[str, Any]]) -> None:\n    for e in expected:\n        if \"skipped\" not in e:\n            e[\"skipped\"] = False\n\n    result = []\n    for op in ops:\n        if op.job_type == \"update\":\n            assert isinstance(op, Update)\n            result.append(\n                {\n                    \"job\": \"update\",\n                    \"from\": op.initial_package,\n                    \"to\": op.target_package,\n                    \"skipped\": op.skipped,\n                }\n            )\n        else:\n            job = \"install\"\n            if op.job_type == \"uninstall\":\n                job = \"remove\"\n\n            result.append({\"job\": job, \"package\": op.package, \"skipped\": op.skipped})\n\n    assert result == expected\n\n\ndef test_it_should_calculate_operations_in_correct_order() -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1.0.0\"), Package(\"b\", \"2.0.0\"), Package(\"c\", \"3.0.0\")],\n        {\n            Package(\"a\", \"1.0.0\"): get_transitive_info(1),\n            Package(\"b\", \"2.1.0\"): get_transitive_info(2),\n            Package(\"d\", \"4.0.0\"): get_transitive_info(0),\n        },\n    )\n\n    check_operations(\n        transaction.calculate_operations(),\n        [\n            {\"job\": \"install\", \"package\": Package(\"b\", \"2.1.0\")},\n            {\"job\": \"install\", \"package\": Package(\"a\", \"1.0.0\")},\n            {\"job\": \"install\", \"package\": Package(\"d\", \"4.0.0\")},\n        ],\n    )\n\n\ndef test_it_should_calculate_operations_for_installed_packages() -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1.0.0\"), Package(\"b\", \"2.0.0\"), Package(\"c\", \"3.0.0\")],\n        {\n            Package(\"a\", \"1.0.0\"): get_transitive_info(1),\n            Package(\"b\", \"2.1.0\"): get_transitive_info(2),\n            Package(\"d\", \"4.0.0\"): get_transitive_info(0),\n        },\n        installed_packages=[\n            Package(\"a\", \"1.0.0\"),\n            Package(\"b\", \"2.0.0\"),\n            Package(\"c\", \"3.0.0\"),\n            Package(\"e\", \"5.0.0\"),\n        ],\n    )\n\n    check_operations(\n        transaction.calculate_operations(),\n        [\n            {\"job\": \"remove\", \"package\": Package(\"c\", \"3.0.0\")},\n            {\n                \"job\": \"update\",\n                \"from\": Package(\"b\", \"2.0.0\"),\n                \"to\": Package(\"b\", \"2.1.0\"),\n            },\n            {\"job\": \"install\", \"package\": Package(\"a\", \"1.0.0\"), \"skipped\": True},\n            {\"job\": \"install\", \"package\": Package(\"d\", \"4.0.0\")},\n        ],\n    )\n\n\ndef test_it_should_remove_installed_packages_if_required() -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1.0.0\"), Package(\"b\", \"2.0.0\"), Package(\"c\", \"3.0.0\")],\n        {\n            Package(\"a\", \"1.0.0\"): get_transitive_info(1),\n            Package(\"b\", \"2.1.0\"): get_transitive_info(2),\n            Package(\"d\", \"4.0.0\"): get_transitive_info(0),\n        },\n        installed_packages=[\n            Package(\"a\", \"1.0.0\"),\n            Package(\"b\", \"2.0.0\"),\n            Package(\"c\", \"3.0.0\"),\n            Package(\"e\", \"5.0.0\"),\n        ],\n    )\n\n    check_operations(\n        transaction.calculate_operations(synchronize=True),\n        [\n            {\"job\": \"remove\", \"package\": Package(\"c\", \"3.0.0\")},\n            {\"job\": \"remove\", \"package\": Package(\"e\", \"5.0.0\")},\n            {\n                \"job\": \"update\",\n                \"from\": Package(\"b\", \"2.0.0\"),\n                \"to\": Package(\"b\", \"2.1.0\"),\n            },\n            {\"job\": \"install\", \"package\": Package(\"a\", \"1.0.0\"), \"skipped\": True},\n            {\"job\": \"install\", \"package\": Package(\"d\", \"4.0.0\")},\n        ],\n    )\n\n\ndef test_it_should_not_remove_system_site_packages() -> None:\n    \"\"\"\n    Different types of uninstalls:\n    - c: tracked but not required\n    - e: not tracked\n    - f: root extra that is not requested\n    \"\"\"\n    extra_name = canonicalize_name(\"foo\")\n    package = ProjectPackage(\"root\", \"1.0\")\n    dep_f = Dependency(\"f\", \"1\", optional=True)\n    dep_f._in_extras = [extra_name]\n    package.add_dependency(dep_f)\n    package.extras = {extra_name: [dep_f]}\n    opt_f = Package(\"f\", \"6.0.0\")\n    opt_f.optional = True\n    transaction = Transaction(\n        [Package(\"a\", \"1.0.0\"), Package(\"b\", \"2.0.0\"), Package(\"c\", \"3.0.0\")],\n        {\n            Package(\"a\", \"1.0.0\"): get_transitive_info(1),\n            Package(\"b\", \"2.1.0\"): get_transitive_info(2),\n            Package(\"d\", \"4.0.0\"): get_transitive_info(0),\n            opt_f: get_transitive_info(0),\n        },\n        installed_packages=[\n            Package(\"a\", \"1.0.0\"),\n            Package(\"b\", \"2.0.0\"),\n            Package(\"c\", \"3.0.0\"),\n            Package(\"e\", \"5.0.0\"),\n            Package(\"f\", \"6.0.0\"),\n        ],\n        root_package=package,\n    )\n\n    check_operations(\n        transaction.calculate_operations(\n            synchronize=True,\n            extras=set(),\n            system_site_packages={\n                canonicalize_name(name) for name in (\"a\", \"b\", \"c\", \"e\", \"f\")\n            },\n        ),\n        [\n            {\n                \"job\": \"update\",\n                \"from\": Package(\"b\", \"2.0.0\"),\n                \"to\": Package(\"b\", \"2.1.0\"),\n            },\n            {\"job\": \"install\", \"package\": Package(\"a\", \"1.0.0\"), \"skipped\": True},\n            {\"job\": \"install\", \"package\": Package(\"d\", \"4.0.0\")},\n        ],\n    )\n\n\ndef test_it_should_not_remove_installed_packages_that_are_in_result() -> None:\n    transaction = Transaction(\n        [],\n        {\n            Package(\"a\", \"1.0.0\"): get_transitive_info(1),\n            Package(\"b\", \"2.0.0\"): get_transitive_info(2),\n            Package(\"c\", \"3.0.0\"): get_transitive_info(0),\n        },\n        installed_packages=[\n            Package(\"a\", \"1.0.0\"),\n            Package(\"b\", \"2.0.0\"),\n            Package(\"c\", \"3.0.0\"),\n        ],\n    )\n\n    check_operations(\n        transaction.calculate_operations(synchronize=True),\n        [\n            {\"job\": \"install\", \"package\": Package(\"a\", \"1.0.0\"), \"skipped\": True},\n            {\"job\": \"install\", \"package\": Package(\"b\", \"2.0.0\"), \"skipped\": True},\n            {\"job\": \"install\", \"package\": Package(\"c\", \"3.0.0\"), \"skipped\": True},\n        ],\n    )\n\n\ndef test_it_should_update_installed_packages_if_sources_are_different() -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1.0.0\")],\n        {\n            Package(\n                \"a\",\n                \"1.0.0\",\n                source_url=\"https://github.com/demo/demo.git\",\n                source_type=\"git\",\n                source_reference=\"main\",\n                source_resolved_reference=\"123456\",\n            ): get_transitive_info(1)\n        },\n        installed_packages=[Package(\"a\", \"1.0.0\")],\n    )\n\n    check_operations(\n        transaction.calculate_operations(synchronize=True),\n        [\n            {\n                \"job\": \"update\",\n                \"from\": Package(\"a\", \"1.0.0\"),\n                \"to\": Package(\n                    \"a\",\n                    \"1.0.0\",\n                    source_url=\"https://github.com/demo/demo.git\",\n                    source_type=\"git\",\n                    source_reference=\"main\",\n                    source_resolved_reference=\"123456\",\n                ),\n            }\n        ],\n    )\n\n\n@pytest.mark.parametrize(\n    (\"groups\", \"expected\"),\n    [\n        (set(), []),\n        ({\"main\"}, [\"a\", \"c\"]),\n        ({\"dev\"}, [\"b\", \"c\"]),\n        ({\"main\", \"dev\"}, [\"a\", \"b\", \"c\"]),\n    ],\n)\n@pytest.mark.parametrize(\"installed\", [False, True])\n@pytest.mark.parametrize(\"with_uninstalls\", [False, True])\n@pytest.mark.parametrize(\"sync\", [False, True])\ndef test_calculate_operations_with_groups(\n    installed: bool,\n    with_uninstalls: bool,\n    sync: bool,\n    groups: set[str],\n    expected: list[str],\n) -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1\"), Package(\"b\", \"1\"), Package(\"c\", \"1\"), Package(\"d\", \"1\")],\n        {\n            Package(\"a\", \"1\"): TransitivePackageInfo(\n                0, {MAIN_GROUP}, {MAIN_GROUP: AnyMarker()}\n            ),\n            Package(\"b\", \"1\"): TransitivePackageInfo(\n                0, {DEV_GROUP}, {DEV_GROUP: AnyMarker()}\n            ),\n            Package(\"c\", \"1\"): TransitivePackageInfo(\n                0,\n                {MAIN_GROUP, DEV_GROUP},\n                {MAIN_GROUP: AnyMarker(), DEV_GROUP: AnyMarker()},\n            ),\n        },\n        (\n            [Package(\"a\", \"1\"), Package(\"b\", \"1\"), Package(\"c\", \"1\"), Package(\"d\", \"1\")]\n            if installed\n            else []\n        ),\n        None,\n        {\"python_version\": \"3.8\"},\n        {canonicalize_name(g) for g in groups},\n    )\n\n    expected_ops = [\n        {\"job\": \"install\", \"package\": Package(name, \"1\")} for name in expected\n    ]\n    if installed:\n        for op in expected_ops:\n            op[\"skipped\"] = True\n        if with_uninstalls:\n            expected_ops.insert(0, {\"job\": \"remove\", \"package\": Package(\"d\", \"1\")})\n            if sync:\n                for name in sorted({\"a\", \"b\", \"c\"}.difference(expected), reverse=True):\n                    expected_ops.insert(\n                        0, {\"job\": \"remove\", \"package\": Package(name, \"1\")}\n                    )\n\n    check_operations(\n        transaction.calculate_operations(\n            with_uninstalls=with_uninstalls, synchronize=sync\n        ),\n        expected_ops,\n    )\n\n\n@pytest.mark.parametrize(\n    (\"python_version\", \"expected\"), [(\"3.8\", [\"a\"]), (\"3.9\", [\"b\"])]\n)\n@pytest.mark.parametrize(\"installed\", [False, True])\n@pytest.mark.parametrize(\"sync\", [False, True])\ndef test_calculate_operations_with_markers(\n    installed: bool, sync: bool, python_version: str, expected: list[str]\n) -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1\"), Package(\"b\", \"1\")],\n        {\n            Package(\"a\", \"1\"): TransitivePackageInfo(\n                0, {MAIN_GROUP}, {MAIN_GROUP: parse_marker(\"python_version < '3.9'\")}\n            ),\n            Package(\"b\", \"1\"): TransitivePackageInfo(\n                0, {MAIN_GROUP}, {MAIN_GROUP: parse_marker(\"python_version >= '3.9'\")}\n            ),\n        },\n        [Package(\"a\", \"1\"), Package(\"b\", \"1\")] if installed else [],\n        None,\n        {\"python_version\": python_version},\n        {MAIN_GROUP},\n    )\n\n    expected_ops = [\n        {\"job\": \"install\", \"package\": Package(name, \"1\")} for name in expected\n    ]\n    if installed:\n        for op in expected_ops:\n            op[\"skipped\"] = True\n        if sync:\n            for name in sorted({\"a\", \"b\"}.difference(expected), reverse=True):\n                expected_ops.insert(0, {\"job\": \"remove\", \"package\": Package(name, \"1\")})\n\n    check_operations(\n        transaction.calculate_operations(with_uninstalls=sync, synchronize=sync),\n        expected_ops,\n    )\n\n\n@pytest.mark.parametrize(\n    (\"python_version\", \"sys_platform\", \"groups\", \"expected\"),\n    [\n        (\"3.8\", \"win32\", {\"main\"}, True),\n        (\"3.9\", \"linux\", {\"main\"}, False),\n        (\"3.9\", \"linux\", {\"dev\"}, True),\n        (\"3.8\", \"win32\", {\"dev\"}, False),\n        (\"3.9\", \"linux\", {\"main\", \"dev\"}, True),\n        (\"3.8\", \"win32\", {\"main\", \"dev\"}, True),\n        (\"3.8\", \"linux\", {\"main\", \"dev\"}, True),\n        (\"3.9\", \"win32\", {\"main\", \"dev\"}, False),\n    ],\n)\ndef test_calculate_operations_with_groups_and_markers(\n    python_version: str,\n    sys_platform: str,\n    groups: set[str],\n    expected: bool,\n) -> None:\n    transaction = Transaction(\n        [Package(\"a\", \"1\")],\n        {\n            Package(\"a\", \"1\"): TransitivePackageInfo(\n                0,\n                {MAIN_GROUP, DEV_GROUP},\n                {\n                    MAIN_GROUP: parse_marker(\"python_version < '3.9'\"),\n                    DEV_GROUP: parse_marker(\"sys_platform == 'linux'\"),\n                },\n            ),\n        },\n        [],\n        None,\n        {\"python_version\": python_version, \"sys_platform\": sys_platform},\n        {canonicalize_name(g) for g in groups},\n    )\n\n    expected_ops = (\n        [{\"job\": \"install\", \"package\": Package(\"a\", \"1\")}] if expected else []\n    )\n\n    check_operations(transaction.calculate_operations(), expected_ops)\n\n\n@pytest.mark.parametrize(\"extras\", [False, True])\n@pytest.mark.parametrize(\"marker_env\", [False, True])\n@pytest.mark.parametrize(\"installed\", [False, True])\n@pytest.mark.parametrize(\"with_uninstalls\", [False, True])\n@pytest.mark.parametrize(\"sync\", [False, True])\ndef test_calculate_operations_extras(\n    extras: bool,\n    marker_env: bool,\n    installed: bool,\n    with_uninstalls: bool,\n    sync: bool,\n) -> None:\n    extra_name = canonicalize_name(\"foo\")\n    package = ProjectPackage(\"root\", \"1.0\")\n    dep_a = Dependency(\"a\", \"1\", optional=True)\n    dep_a._in_extras = [extra_name]\n    package.add_dependency(dep_a)\n    package.extras = {extra_name: [dep_a]}\n    opt_a = Package(\"a\", \"1\")\n    opt_a.optional = True\n\n    transaction = Transaction(\n        [Package(\"a\", \"1\")],\n        {\n            opt_a: TransitivePackageInfo(\n                0,\n                {MAIN_GROUP} if marker_env else set(),\n                {MAIN_GROUP: parse_marker(\"extra == 'foo'\")} if marker_env else {},\n            )\n        },\n        [Package(\"a\", \"1\")] if installed else [],\n        package,\n        {\"python_version\": \"3.8\"} if marker_env else None,\n        {MAIN_GROUP} if marker_env else None,\n    )\n\n    if extras:\n        ops = [{\"job\": \"install\", \"package\": Package(\"a\", \"1\"), \"skipped\": installed}]\n    elif installed:\n        if with_uninstalls and sync:\n            ops = [{\"job\": \"remove\", \"package\": Package(\"a\", \"1\")}]\n        else:\n            ops = []\n    else:\n        ops = [{\"job\": \"install\", \"package\": Package(\"a\", \"1\"), \"skipped\": True}]\n\n    check_operations(\n        transaction.calculate_operations(\n            with_uninstalls=with_uninstalls,\n            synchronize=sync,\n            extras={extra_name} if extras else set(),\n        ),\n        ops,\n    )\n\n\n@pytest.mark.parametrize(\"extra\", [\"\", \"foo\", \"bar\"])\ndef test_calculate_operations_extras_no_redundant_uninstall(extra: str) -> None:\n    extra1 = canonicalize_name(\"foo\")\n    extra2 = canonicalize_name(\"bar\")\n    package = ProjectPackage(\"root\", \"1.0\")\n    dep_a1 = Dependency(\"a\", \"1\", optional=True)\n    dep_a1._in_extras = [canonicalize_name(\"foo\")]\n    dep_a1.marker = parse_marker(\"extra != 'bar'\")\n    dep_a2 = Dependency(\"a\", \"2\", optional=True)\n    dep_a2._in_extras = [canonicalize_name(\"bar\")]\n    dep_a2.marker = parse_marker(\"extra != 'foo'\")\n    package.add_dependency(dep_a1)\n    package.add_dependency(dep_a2)\n    package.extras = {extra1: [dep_a1], extra2: [dep_a2]}\n    opt_a1 = Package(\"a\", \"1\")\n    opt_a1.optional = True\n    opt_a2 = Package(\"a\", \"2\")\n    opt_a2.optional = True\n\n    transaction = Transaction(\n        [Package(\"a\", \"1\")],\n        {\n            opt_a1: TransitivePackageInfo(\n                0,\n                {MAIN_GROUP},\n                {MAIN_GROUP: parse_marker(\"extra == 'foo' and extra != 'bar'\")},\n            ),\n            opt_a2: TransitivePackageInfo(\n                0,\n                {MAIN_GROUP},\n                {MAIN_GROUP: parse_marker(\"extra == 'bar' and extra != 'foo'\")},\n            ),\n        },\n        [Package(\"a\", \"1\")],\n        package,\n        {\"python_version\": \"3.9\"},\n        {MAIN_GROUP},\n    )\n\n    if not extra:\n        ops = [{\"job\": \"remove\", \"package\": Package(\"a\", \"1\")}]\n    elif extra == \"foo\":\n        ops = [{\"job\": \"install\", \"package\": Package(\"a\", \"1\"), \"skipped\": True}]\n    elif extra == \"bar\":\n        ops = [{\"job\": \"update\", \"from\": Package(\"a\", \"1\"), \"to\": Package(\"a\", \"2\")}]\n    else:\n        raise NotImplementedError\n\n    check_operations(\n        transaction.calculate_operations(\n            synchronize=True,\n            extras=set() if not extra else {canonicalize_name(extra)},\n        ),\n        ops,\n    )\n"
  },
  {
    "path": "tests/pyproject/__init__.py",
    "content": ""
  },
  {
    "path": "tests/pyproject/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\n@pytest.fixture\ndef pyproject_toml(tmp_path: Path) -> Path:\n    path = tmp_path / \"pyproject.toml\"\n    with path.open(mode=\"w\", encoding=\"utf-8\"):\n        pass\n    return path\n\n\n@pytest.fixture\ndef build_system_section(pyproject_toml: Path) -> str:\n    content = \"\"\"\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\"\"\"\n    with pyproject_toml.open(mode=\"a\", encoding=\"utf-8\") as f:\n        f.write(content)\n    return content\n\n\n@pytest.fixture\ndef poetry_section(pyproject_toml: Path) -> str:\n    content = \"\"\"\n[tool.poetry]\nname = \"poetry\"\n\n[tool.poetry.dependencies]\npython = \"^3.5\"\n\"\"\"\n    with pyproject_toml.open(mode=\"a\", encoding=\"utf-8\") as f:\n        f.write(content)\n    return content\n"
  },
  {
    "path": "tests/pyproject/test_pyproject_toml.py",
    "content": "from __future__ import annotations\n\nimport uuid\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.pyproject.toml import PyProjectTOML\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\ndef test_pyproject_toml_reload(pyproject_toml: Path, poetry_section: str) -> None:\n    pyproject = PyProjectTOML(pyproject_toml)\n    name_original = pyproject.poetry_config[\"name\"]\n    name_new = str(uuid.uuid4())\n\n    pyproject.poetry_config[\"name\"] = name_new\n    assert isinstance(pyproject.poetry_config[\"name\"], str)\n    assert pyproject.poetry_config[\"name\"] == name_new\n\n    pyproject.reload()\n    assert pyproject.poetry_config[\"name\"] == name_original\n\n\ndef test_pyproject_toml_save(\n    pyproject_toml: Path, poetry_section: str, build_system_section: str\n) -> None:\n    pyproject = PyProjectTOML(pyproject_toml)\n\n    name = str(uuid.uuid4())\n    build_backend = str(uuid.uuid4())\n    build_requires = str(uuid.uuid4())\n\n    pyproject.poetry_config[\"name\"] = name\n    pyproject.build_system.build_backend = build_backend\n    pyproject.build_system.requires.append(build_requires)\n\n    pyproject.save()\n\n    pyproject = PyProjectTOML(pyproject_toml)\n\n    assert isinstance(pyproject.poetry_config[\"name\"], str)\n    assert pyproject.poetry_config[\"name\"] == name\n    assert pyproject.build_system.build_backend == build_backend\n    assert build_requires in pyproject.build_system.requires\n"
  },
  {
    "path": "tests/pyproject/test_pyproject_toml_file.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.exceptions import PoetryCoreError\n\nfrom poetry.toml import TOMLFile\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\ndef test_pyproject_toml_file_invalid(pyproject_toml: Path) -> None:\n    with pyproject_toml.open(mode=\"a\", encoding=\"utf-8\") as f:\n        f.write(\"<<<<<<<<<<<\")\n\n    with pytest.raises(PoetryCoreError) as excval:\n        _ = TOMLFile(pyproject_toml).read()\n\n    assert f\"Invalid TOML file {pyproject_toml.as_posix()}\" in str(excval.value)\n"
  },
  {
    "path": "tests/repositories/__init__.py",
    "content": ""
  },
  {
    "path": "tests/repositories/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from tests.types import HTMLPageGetter\n\n\n@pytest.fixture\ndef html_page_content() -> HTMLPageGetter:\n    def _fixture(content: str, base_url: str | None = None) -> str:\n        base = f'<base href=\"{base_url}\"' if base_url else \"\"\n        return f\"\"\"\n        <!DOCTYPE html>\n        <html>\n          <head>\n            {base}\n            <meta name=\"pypi:repository-version\" content=\"1.0\">\n            <title>Links for demo</title>\n          </head>\n          <body>\n            <h1>Links for demo</h1>\n            {content}\n            </body>\n        </html>\n        \"\"\"\n\n    return _fixture\n"
  },
  {
    "path": "tests/repositories/fixtures/__init__.py",
    "content": "from __future__ import annotations\n\n\npytest_plugins = [\n    \"tests.repositories.fixtures.distribution_hashes\",\n    \"tests.repositories.fixtures.legacy\",\n    \"tests.repositories.fixtures.pypi\",\n    \"tests.repositories.fixtures.python_hosted\",\n]\n"
  },
  {
    "path": "tests/repositories/fixtures/distribution_hashes.py",
    "content": "# this file is generated by tests/repositories/fixtures/pypi.org/generate.py\nfrom __future__ import annotations\n\nimport dataclasses\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from tests.types import DistributionHashGetter\n\n\n@dataclasses.dataclass\nclass DistributionHash:\n    sha256: str = \"\"\n    md5: str = \"\"\n\n\nKNOWN_DISTRIBUTION_HASHES = {\n    \"SQLAlchemy-1.2.12.tar.gz\": DistributionHash(\n        \"b5a127599b3f27847fba6119de0fcb70832a8041b103701a708b7c7d044faa38\",\n        \"4a2617b5254748828d09349fc4eff6bd\",\n    ),\n    \"Twisted-18.9.0.tar.bz2\": DistributionHash(\n        \"4335327da58be11dd6e482ec6b85eb055bcc953a9570cd59e7840a2ce9419a8e\",\n        \"35ff4705ea90a76bf972ff3b229546ca\",\n    ),\n    \"attrs-17.4.0-py2.py3-none-any.whl\": DistributionHash(\n        \"1fbfc10ebc8c876dcbab17f016b80ae1a4f0c1413461a695871427960795beb4\",\n        \"7fe37931797b16c7fa158017457a9ea9\",\n    ),\n    \"attrs-17.4.0.tar.gz\": DistributionHash(\n        \"eb7536a1e6928190b3008c5b350bdf9850d619fff212341cd096f87a27a5e564\",\n        \"c03e5b3608d9071fbd098850d8922668\",\n    ),\n    \"black-19.10b0-py36-none-any.whl\": DistributionHash(\n        \"13001c5b7dbc81137164b43137320a1785e95ce84e4db849279786877ac6d7f6\",\n        \"acc537b0f3f7ebf575616490d7cc14f4\",\n    ),\n    \"black-19.10b0.tar.gz\": DistributionHash(\n        \"6cada614d5d2132698c6d5fff384657273d922c4fffa6a2f0de9e03e25b8913a\",\n        \"c383543109a66a5a99113e6326db5251\",\n    ),\n    \"black-21.11b0-py3-none-any.whl\": DistributionHash(\n        \"38f6ad54069912caf2fa2d4f25d0c5dedef4b2338a0cb545dbe2fdf54a6a8891\",\n        \"92942a9efabf8e321a11360667ad2494\",\n    ),\n    \"black-21.11b0.tar.gz\": DistributionHash(\n        \"f23c482185d842e2f19d506e55c004061167e3c677c063ecd721042c62086ada\",\n        \"f01267bf2613f825dd6684629c1c829e\",\n    ),\n    \"cleo-1.0.0a5-py3-none-any.whl\": DistributionHash(\n        \"d0cfea878b77be28be027033e6af419b705abe47278067a7c3a298f39cf825c5\",\n        \"19ed7de77063e8f16bc459276ccbe197\",\n    ),\n    \"cleo-1.0.0a5.tar.gz\": DistributionHash(\n        \"88f0a4275a17f2ab4d013786b8b9522d4c60bd37d8fc9b3def0fb27f4ac1e694\",\n        \"92e181952976e09b9d1c583da6c3e2fc\",\n    ),\n    \"clikit-0.2.4-py2.py3-none-any.whl\": DistributionHash(\n        \"27316bf6382b04be8fb2f60c85d538fd2b2b03f0f1eba5c88f7d7eddbefc2778\",\n        \"93a51e8bf259c29692e51a7cbca6d664\",\n    ),\n    \"clikit-0.2.4.tar.gz\": DistributionHash(\n        \"0fdd41e86e8b118a8b1e94ef2835925ada541d481c9b3b2fc635fa68713e6125\",\n        \"f7cdbad3508038a04561f646aae68146\",\n    ),\n    \"colorama-0.3.9-py2.py3-none-any.whl\": DistributionHash(\n        \"78a441d2e984c790526cdef1cfd8415a366979ef5b3186771a055b35886953bf\",\n        \"8021c861015b5f590be41190bc3f8eed\",\n    ),\n    \"colorama-0.3.9.tar.gz\": DistributionHash(\n        \"4c5a15209723ce1330a5c193465fe221098f761e9640d823a2ce7c03f983137f\",\n        \"8323a5b84fdf7ad810804e51fc256b39\",\n    ),\n    \"demo-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a\",\n        \"15507846fd4299596661d0197bfb4f90\",\n    ),\n    \"demo-0.1.0.tar.gz\": DistributionHash(\n        \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\",\n        \"d1912c917363a64e127318655f7d1fe7\",\n    ),\n    \"demo-0.1.2-py2.py3-none-any.whl\": DistributionHash(\n        \"55dde4e6828081de7a1e429f33180459c333d9da593db62a3d75a8f5e505dde1\",\n        \"53b4e10d2bfa81a4206221c4b87843d9\",\n    ),\n    \"demo_invalid_record-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"d1e5a3f18f24a2ad3717c6f9c55f8c26060f39b2cddf28b18c355786728cb4dd\",\n        \"18041168d415370d5019ec7e2b1ed0b5\",\n    ),\n    \"demo_invalid_record2-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"e730fca385b52e77fc58d73812f0dc236fad489ef6026716d1a4317ab4738c3c\",\n        \"a21ee67e833f50e9f0ecdfe1c0484b93\",\n    ),\n    \"demo_metadata_version_23-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"7592aa158137726d9579e5d4347bd03a88f9fc82e11061303215feaaf000d32c\",\n        \"434114a36f986671d132033e130f26b7\",\n    ),\n    \"demo_metadata_version_24-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"f0d306c48d665e4a0051c660cc39f5ed7b7d51427050bfbca525e95d9fad2587\",\n        \"c0cbc2e5f2736a487ff960a8c39defbe\",\n    ),\n    \"demo_metadata_version_299-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"9678f9e59454a281bf7780661d719122766111dc9432ad20823ce6569d10edb2\",\n        \"2eb53ee23408e65de909e20d9575afe3\",\n    ),\n    \"demo_metadata_version_unknown-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"d716cd66546468ec3d4d40f4a4ecc813e3e4c661e155ecbc3a932f47d46d6e05\",\n        \"749f823ff755a2f46bfb5ab25fdf9810\",\n    ),\n    \"demo_missing_dist_info-0.1.0-py2.py3-none-any.whl\": DistributionHash(\n        \"cf8eaade81dd1db42f60c0e9c8610c1c12006baa9f7ad994b1c2bae92ea4b426\",\n        \"da33c6088e72fbaaf873999606767353\",\n    ),\n    \"demo_no_pkg_info-0.1.0.tar.gz\": DistributionHash(\n        \"f1e2a977c506dfb6b43495e2ffeee618b90029bac92fcb3038a53268197afa0c\",\n        \"eeaf257d6b2c3b01def567751b21c1e8\",\n    ),\n    \"discord.py-2.0.0-py3-none-any.whl\": DistributionHash(\n        \"25b9739ba456622655203a0925b354c0ba96ac6c740562e7c37791c2f6b594fb\",\n        \"65394fc868632423cedb6be7259db970\",\n    ),\n    \"discord.py-2.0.0.tar.gz\": DistributionHash(\n        \"b86fa9dd562684f7a52564e6dfe0216f6c172a009c0d86b8dea8bdd6ffa6b1f4\",\n        \"6c0505a6032342b29f31f9979f37d277\",\n    ),\n    \"futures-3.2.0-py2-none-any.whl\": DistributionHash(\n        \"41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\",\n        \"f81c5c27f3ba2efc008cc96363a81c5e\",\n    ),\n    \"futures-3.2.0.tar.gz\": DistributionHash(\n        \"baf0d469c9e541b747986b7404cd63a5496955bd0c43a3cc068c449b09b7d4a4\",\n        \"40eb168dab84e606df3fdb7e67fe27b7\",\n    ),\n    \"hbmqtt-0.9.6.tar.gz\": DistributionHash(\n        \"379f1d9044997c69308ac2e01621c817b5394e1fbe0696e62538ae2dd0aa7e07\",\n        \"b284e3118882f169aa618a856cd91c5f\",\n    ),\n    \"ipython-5.7.0-py2-none-any.whl\": DistributionHash(\n        \"4608e3e0500fe8142659d149891400fc0b9fa250051814b569457ae4688943dc\",\n        \"20da5e0b1f79dccb37f033a885d798d7\",\n    ),\n    \"ipython-5.7.0-py3-none-any.whl\": DistributionHash(\n        \"4292c026552a77b2edc0543941516eddd6fe1a4b681a76ac40b3f585d2fca76f\",\n        \"2844fa01618fe27ab99ad455d605b47d\",\n    ),\n    \"ipython-5.7.0.tar.gz\": DistributionHash(\n        \"4e7fb265e0264498bd0d62c6261936a658bf3d38beb8a7b10cd2c6327c62ac2a\",\n        \"01f2808ebe78ff2f28dc39be3aa635ca\",\n    ),\n    \"ipython-7.5.0-py3-none-any.whl\": DistributionHash(\n        \"1b4c76bf1e8dd9067a4f5ab4695d4c5ad81c30d7d06f7592f4c069c389e37f37\",\n        \"f40ea889fb7adf989760c5e7a38bd112\",\n    ),\n    \"ipython-7.5.0.tar.gz\": DistributionHash(\n        \"cd2a17ac273fea8bf8953118a2d83bad94f592f0db3e83fff9129a1842e36dbe\",\n        \"0e8c1d7c14f309f6cd2dfd4e48e75cb1\",\n    ),\n    \"isodate-0.7.0-py3-none-any.whl\": DistributionHash(\n        \"04505f97eb100b66dff1239859e6e04ab913714c453d6ab9591adbf418285847\",\n        \"1af9e3ee3f5669186356afd2dbe7ce81\",\n    ),\n    \"isodate-0.7.0.tar.gz\": DistributionHash(\n        \"167c3615c0bd2e498c9bae7a1aba5863a17e52299aafd89f17a3a091187dca74\",\n        \"5668b7b7120797f03330363000afc35a\",\n    ),\n    \"isort-4.3.4-py2-none-any.whl\": DistributionHash(\n        \"383c39c10b5db83e8d150ac5b84d74bda96e3a1b06a30257f022dcbcd21f54b9\",\n        \"42bccda292eca3c91eadf3eb781a224f\",\n    ),\n    \"isort-4.3.4-py3-none-any.whl\": DistributionHash(\n        \"5668dce9fb48544c57ed626982e190c8ea99e3a612850453e9c3b193b9fa2edc\",\n        \"6c3b582d7782633ec23917b00a97a2fe\",\n    ),\n    \"isort-4.3.4.tar.gz\": DistributionHash(\n        \"234ad07e1e2780c27fa56364eefa734bee991b0d744337ef7e7ce3d5b1b59f39\",\n        \"9244631852cf8bd8559f7ab78bf4ec78\",\n    ),\n    \"jupyter-1.0.0-py2.py3-none-any.whl\": DistributionHash(\n        \"1de1f2be45629dd6f7f9558e2385ddf6901849699ef1044c52d171a9b520a420\",\n        \"27f411f164e0878104d76d868127f76f\",\n    ),\n    \"jupyter-1.0.0.tar.gz\": DistributionHash(\n        \"3ef1e86ba0556ea5922b846416a41acfd2625830d996c7d06d80c90bed1dc193\",\n        \"78acaec88533ea6b6e761e7d086a1d04\",\n    ),\n    \"jupyter-1.0.0.zip\": DistributionHash(\n        \"4a855b9717c3ea24fd8ca4fd91ab5995894aecc4d20e7f39c28786a2c1869fae\",\n        \"7b7a957694a73ac0c19fe46c216c0ea0\",\n    ),\n    \"more-itertools-4.1.0.tar.gz\": DistributionHash(\n        \"bab2dc6f4be8f9a4a72177842c5283e2dff57c167439a03e3d8d901e854f0f2e\",\n        \"bf351a1050242ce3af7e475a4da1a26b\",\n    ),\n    \"more_itertools-4.1.0-py2-none-any.whl\": DistributionHash(\n        \"0f461c2cd4ec16611396f9ee57f40433de3d59e95475d84c0c829cde02f746cd\",\n        \"703e1e0922de1f11823da60af1488b7a\",\n    ),\n    \"more_itertools-4.1.0-py3-none-any.whl\": DistributionHash(\n        \"580b6002d1f28feb5bcb8303278d59cf17dfbd19a63a5c2375112dae72c9bf98\",\n        \"ae17a45d13e9dc319794c40fa739c38f\",\n    ),\n    \"pastel-0.1.0-py3-none-any.whl\": DistributionHash(\n        \"754d192c088e256d52a3f825c3b9e14252d5adc70f53656453f6431e50a70b99\",\n        \"cf7c53ab0a5d7e7c721425b24b486124\",\n    ),\n    \"pastel-0.1.0.tar.gz\": DistributionHash(\n        \"22f14474c4120b37c54ac2173b49b0ac1de9283ca714be6eb3ea8b39296285a9\",\n        \"43ea5f07660f630da18ae1827f5b4333\",\n    ),\n    \"pluggy-0.6.0-py2-none-any.whl\": DistributionHash(\n        \"f5f767d398f18aa177976bf9c4d0c05d96487a7d8f07062251585803aaf56246\",\n        \"095eed084713c9b2a9a01520485e20fb\",\n    ),\n    \"pluggy-0.6.0-py3-none-any.whl\": DistributionHash(\n        \"d34798b80853ab688de1a3ca5b99ba4de91c459c19c76a555dc939979ae67eb0\",\n        \"2b6dc266f54023dfb26726686ee6b227\",\n    ),\n    \"pluggy-0.6.0.tar.gz\": DistributionHash(\n        \"a982e208d054867661d27c6d2a86b17ba05fbb6b1bdc01f42660732dd107f865\",\n        \"ef8a88abcd501afd47cb22245fe4315a\",\n    ),\n    \"poetry_core-1.5.0-py3-none-any.whl\": DistributionHash(\n        \"e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\",\n        \"be7589b4902793e66d7d979bd8581591\",\n    ),\n    \"poetry_core-1.5.0.tar.gz\": DistributionHash(\n        \"0ae8d28caf5c12ec1714b16d2e7157ddd52397ea6bfdeba5a9432e449a0184da\",\n        \"3f9b36a7a94cd235bfd5f05794828445\",\n    ),\n    \"poetry_core-2.0.1-py3-none-any.whl\": DistributionHash(\n        \"a3c7009536522cda4eb0fb3805c9dc935b5537f8727dd01efb9c15e51a17552b\",\n        \"a52cf4beef0de009e0a9a36c9e6962f5\",\n    ),\n    \"poetry_core-2.0.1.tar.gz\": DistributionHash(\n        \"d2acdaec3b93dc1ab43adaeb0e9a8a6a6b3701c4535b5baab4b718ab12c8993c\",\n        \"1b1bb959cd760ac509de9b38ae67fc3b\",\n    ),\n    \"py-1.5.3-py2.py3-none-any.whl\": DistributionHash(\n        \"ef4a94f47156178e42ef8f2b131db420e0f4b6aa0b3936b6dbde6ad6487476a5\",\n        \"b316b380701661cb67732ecdaef30eeb\",\n    ),\n    \"py-1.5.3.tar.gz\": DistributionHash(\n        \"2df2c513c3af11de15f58189ba5539ddc4768c6f33816dc5c03950c8bd6180fa\",\n        \"623e80cfc06df930414a9ce4bf0fd6c9\",\n    ),\n    \"pytest-3.5.0-py2.py3-none-any.whl\": DistributionHash(\n        \"427b4582bda18e92ad1967e8b1e071e2c53e6cb7e3e5f090cb3ca443455be23f\",\n        \"4a8651dec151e76f283bf59e333286f9\",\n    ),\n    \"pytest-3.5.0.tar.gz\": DistributionHash(\n        \"677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68\",\n        \"ccd78dac54112045f561c4df86631f19\",\n    ),\n    \"pytest-3.5.1-py2.py3-none-any.whl\": DistributionHash(\n        \"d327df3686046c5b374a9776d9e11606f7dba6fb3db5cf5d60ebc78a31e0768e\",\n        \"1e81fba94885bef80170545d045924eb\",\n    ),\n    \"pytest-3.5.1.tar.gz\": DistributionHash(\n        \"b8fe151f3e181801dd38583a1c03818fbc662a8fce96c9063a0af624613e78f8\",\n        \"961104636090457187851ccb9ef0f677\",\n    ),\n    \"python-language-server-0.21.2.tar.gz\": DistributionHash(\n        \"91b564e092f3135b2bac70dbd23d283da5ad50269766a76648787b69fe702c7e\",\n        \"677602ec38bc1c7b72de6128d90d846b\",\n    ),\n    \"requests-2.18.4-py2.py3-none-any.whl\": DistributionHash(\n        \"098be851f30be5bcb2c7537798d44314f576e53818ba9def25141ae4dce8b25d\",\n        \"e770e65750c42f40b97b0ed738d0f859\",\n    ),\n    \"requests-2.18.4.tar.gz\": DistributionHash(\n        \"ec62f7e0e9d4814656b0172dbd592fea06127c6556ff5651eb5d2c8768671fd4\",\n        \"942a6a383dc94da90cf58f5adcf028a4\",\n    ),\n    \"setuptools-67.6.1-py3-none-any.whl\": DistributionHash(\n        \"e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078\",\n        \"3b5b846e000da033d54eeaaf7915126e\",\n    ),\n    \"setuptools-67.6.1.tar.gz\": DistributionHash(\n        \"a737d365c957dd3fced9ddd246118e95dce7a62c3dc49f37e7fdd9e93475d785\",\n        \"ee2562f783544d1f95022c906dd3cf98\",\n    ),\n    \"six-1.11.0-py2.py3-none-any.whl\": DistributionHash(\n        \"534e9875e44a507adec601c29b3cbd2ca6dae7df92bf3dd20c7289b2f99f7466\",\n        \"9500094701f7201ddd065c60abcefef1\",\n    ),\n    \"six-1.11.0.tar.gz\": DistributionHash(\n        \"268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d\",\n        \"25d3568604f921dd23532b88a0ce17e7\",\n    ),\n    \"tomlkit-0.5.2-py2.py3-none-any.whl\": DistributionHash(\n        \"dea8ff39e9e2170f1b2f465520482eec71e7909cfff53dcb076b585d50f8ccc8\",\n        \"4045c5f6848fbc93c38df2296a441f07\",\n    ),\n    \"tomlkit-0.5.2.tar.gz\": DistributionHash(\n        \"4a226ccf11ee5a2e76bfc185747b54ee7718706aeb3aabb981327249dbe2b1d4\",\n        \"7c31987ef6fba2cd64715cae27fade64\",\n    ),\n    \"tomlkit-0.5.3-py2.py3-none-any.whl\": DistributionHash(\n        \"35f06da5835e85f149a4701d43e730adcc09f1b362e5fc2300d77bdd26280908\",\n        \"3a90c70a5067d5727110838094ab8674\",\n    ),\n    \"tomlkit-0.5.3.tar.gz\": DistributionHash(\n        \"e2f785651609492c771d9887ccb2369d891d16595d2d97972e2cbe5e8fb3439f\",\n        \"cdbdc302a184d1f1e38d5e0810e3b212\",\n    ),\n    \"wheel-0.40.0-py3-none-any.whl\": DistributionHash(\n        \"d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247\",\n        \"517d39f133bd7b1ff17caf09784b7543\",\n    ),\n    \"wheel-0.40.0.tar.gz\": DistributionHash(\n        \"5cb7e75751aa82e1b7db3fd52f5a9d59e7b06905630bed135793295931528740\",\n        \"5f175a8d693f74878964d4fd29729ab7\",\n    ),\n    \"zipp-3.5.0-py3-none-any.whl\": DistributionHash(\n        \"ec508cd5a3ed3d126293cafb34611469f2aef7342f575c3b6e072b995dc9da1f\",\n        \"da62cbd850ba32ba93817aab0f03a855\",\n    ),\n    \"zipp-3.5.0.tar.gz\": DistributionHash(\n        \"239d50954a15aa4b283023f18dc451ba811fb4d263f4dd6855642e4d1c80cc9f\",\n        \"16bf2a24fae340052e8565c264d21092\",\n    ),\n}\n\n\n@pytest.fixture\ndef dist_hash_getter() -> DistributionHashGetter:\n    def get_hash(name: str) -> DistributionHash:\n        return KNOWN_DISTRIBUTION_HASHES.get(name, DistributionHash())\n\n    return get_hash\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/cleo-0.7.6.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: cleo\nVersion: 0.7.6\nSummary: Cleo allows you to create beautiful and testable command-line interfaces.\nHome-page: https://github.com/sdispater/cleo\nLicense: MIT\nKeywords: cli,commands\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nRequires-Dist: clikit (>=0.4.0,<0.5.0)\nDescription-Content-Type: text/x-rst\n\nCleo\n####\n\n.. image:: https://travis-ci.org/sdispater/cleo.png\n   :alt: Cleo Build status\n   :target: https://travis-ci.org/sdispater/cleo\n\nCreate beautiful and testable command-line interfaces.\n\nCleo is mostly a higher level wrapper for `CliKit <https://github.com/sdispater/clikit>`_, so\na lot of the components and utilities comes from it. Refer to its documentation for more\ninformation.\n\nResources\n=========\n\n* `Documentation <http://cleo.readthedocs.io>`_\n* `Issue Tracker <https://github.com/sdispater/cleo/issues>`_\n\n\nUsage\n=====\n\nTo make a command that greets you from the command line,\ncreate ``greet_command.py`` and add the following to it:\n\n.. code-block:: python\n\n    from cleo import Command\n\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {name? : Who do you want to greet?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n        \"\"\"\n\n        def handle(self):\n            name = self.argument('name')\n\n            if name:\n                text = 'Hello {}'.format(name)\n            else:\n                text = 'Hello'\n\n            if self.option('yell'):\n                text = text.upper()\n\n            self.line(text)\n\n\nYou also need to create the file to run at the command line which creates\nan ``Application`` and adds commands to it:\n\n.. code-block:: python\n\n    #!/usr/bin/env python\n\n    from greet_command import GreetCommand\n    from cleo import Application\n\n    application = Application()\n    application.add(GreetCommand())\n\n    if __name__ == '__main__':\n        application.run()\n\nTest the new command by running the following\n\n.. code-block:: bash\n\n    $ python application.py greet John\n\nThis will print the following to the command line:\n\n.. code-block:: text\n\n    Hello John\n\nYou can also use the ``--yell`` option to make everything uppercase:\n\n.. code-block:: bash\n\n    $ python application.py greet John --yell\n\nThis prints:\n\n.. code-block:: text\n\n    HELLO JOHN\n\nAs you may have already seen, Cleo uses the command docstring to determine\nthe command definition.\nThe docstring must be in the following form :\n\n.. code-block:: python\n\n    \"\"\"\n    Command description\n\n    Command signature\n    \"\"\"\n\nThe signature being in the following form:\n\n.. code-block:: python\n\n    \"\"\"\n    command:name {argument : Argument description} {--option : Option description}\n    \"\"\"\n\nThe signature can span multiple lines.\n\n.. code-block:: python\n\n    \"\"\"\n    command:name\n        {argument : Argument description}\n        {--option : Option description}\n    \"\"\"\n\nColoring the Output\n-------------------\n\nWhenever you output text, you can surround the text with tags to color its\noutput. For example:\n\n.. code-block:: python\n\n    # green text\n    self.line('<info>foo</info>')\n\n    # yellow text\n    self.line('<comment>foo</comment>')\n\n    # black text on a cyan background\n    self.line('<question>foo</question>')\n\n    # white text on a red background\n    self.line('<error>foo</error>')\n\nThe closing tag can be replaced by ``</>``, which revokes all formatting options established by the last opened tag.\n\nIt is possible to define your own styles using the ``add_style()`` method:\n\n.. code-block:: python\n\n    self.add_style('fire', fg='red', bg='yellow', options=['bold', 'blink'])\n    self.line('<fire>foo</fire>')\n\nAvailable foreground and background colors are: ``black``, ``red``, ``green``,\n``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``.\n\nAnd available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` and ``conceal``.\n\nYou can also set these colors and options inside the tag name:\n\n.. code-block:: python\n\n    # green text\n    self.line('<fg=green>foo</>')\n\n    # black text on a cyan background\n    self.line('<fg=black;bg=cyan>foo</>')\n\n    # bold text on a yellow background\n    self.line('<bg=yellow;options=bold>foo</>')\n\n\nVerbosity Levels\n----------------\n\nCleo has four verbosity levels. These are defined in the ``Output`` class:\n\n=======================================  ================================== ======================\nMode                                     Meaning                            Console option\n=======================================  ================================== ======================\n``NA``                                   Do not output any messages         ``-q`` or ``--quiet``\n``clikit.VERBOSITY_NORMAL``              The default verbosity level        (none)\n``clikit.VERBOSITY_VERBOSE``             Increased verbosity of messages    ``-v``\n``clikit.VERBOSITY_VERY_VERBOSE``        Informative non essential messages ``-vv``\n``clikit.VERBOSITY_DEBUG``               Debug messages                     ``-vvv``\n=======================================  ================================== ======================\n\nIt is possible to print a message in a command for only a specific verbosity\nlevel. For example:\n\n.. code-block:: python\n\n    if clikit.VERBOSITY_VERBOSE <= self.io.verbosity:\n        self.line(...)\n\nThere are also more semantic methods you can use to test for each of the\nverbosity levels:\n\n.. code-block:: python\n\n    if self.output.is_quiet():\n        # ...\n\n    if self.output.is_verbose():\n        # ...\n\nYou can also pass the verbosity flag directly to `line()`.\n\n.. code-block:: python\n\n    self.line(\"\", verbosity=clikit.VERBOSITY_VERBOSE)\n\nWhen the quiet level is used, all output is suppressed.\n\n\nUsing Arguments\n---------------\n\nThe most interesting part of the commands are the arguments and options that\nyou can make available. Arguments are the strings - separated by spaces - that\ncome after the command name itself. They are ordered, and can be optional\nor required. For example, add an optional ``last_name`` argument to the command\nand make the ``name`` argument required:\n\n.. code-block:: python\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {name : Who do you want to greet?}\n            {last_name? : Your last name?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n        \"\"\"\n\nYou now have access to a ``last_name`` argument in your command:\n\n.. code-block:: python\n\n    last_name = self.argument('last_name')\n    if last_name:\n        text += ' {}'.format(last_name)\n\nThe command can now be used in either of the following ways:\n\n.. code-block:: bash\n\n    $ python application.py greet John\n    $ python application.py greet John Doe\n\nIt is also possible to let an argument take a list of values (imagine you want\nto greet all your friends). For this it must be specified at the end of the\nargument list:\n\n.. code-block:: python\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {names* : Who do you want to greet?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n        \"\"\"\n\nTo use this, just specify as many names as you want:\n\n.. code-block:: bash\n\n    $ python application.py demo:greet John Jane\n\nYou can access the ``names`` argument as a list:\n\n.. code-block:: python\n\n    names = self.argument('names')\n    if names:\n        text += ' {}'.format(', '.join(names))\n\nThere are 3 argument variants you can use:\n\n================================ ==================================== ===============================================================================================================\nMode                             Notation                             Value\n================================ ==================================== ===============================================================================================================\n``clikit.ARGUMENT_REQUIRED``     none (just write the argument name)  The argument is required\n``clikit.ARGUMENT_OPTIONAL``     ``argument?``                        The argument is optional and therefore can be omitted\n``clikit.ARGUMENT_MULTI_VALUED`` ``argument*``                        The argument can contain an indefinite number of arguments and must be used at the end of the argument list\n================================ ==================================== ===============================================================================================================\n\nYou can combine them like this:\n\n.. code-block:: python\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {names?* : Who do you want to greet?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n        \"\"\"\n\nIf you want to set a default value, you can it like so:\n\n.. code-block:: text\n\n    argument=default\n\nThe argument will then be considered optional.\n\n\nUsing Options\n-------------\n\nUnlike arguments, options are not ordered (meaning you can specify them in any\norder) and are specified with two dashes (e.g. ``--yell`` - you can also\ndeclare a one-letter shortcut that you can call with a single dash like\n``-y``). Options are *always* optional, and can be setup to accept a value\n(e.g. ``--dir=src``) or simply as a boolean flag without a value (e.g.\n``--yell``).\n\n.. tip::\n\n    It is also possible to make an option *optionally* accept a value (so that\n    ``--yell`` or ``--yell=loud`` work). Options can also be configured to\n    accept a list of values.\n\nFor example, add a new option to the command that can be used to specify\nhow many times in a row the message should be printed:\n\n.. code-block:: python\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {name? : Who do you want to greet?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n            {--iterations=1 : How many times should the message be printed?}\n        \"\"\"\n\n\nNext, use this in the command to print the message multiple times:\n\n.. code-block:: python\n\n    for _ in range(0, self.option('iterations')):\n        self.line(text)\n\nNow, when you run the task, you can optionally specify a ``--iterations``\nflag:\n\n.. code-block:: bash\n\n    $ python application.py demo:greet John\n    $ python application.py demo:greet John --iterations=5\n\nThe first example will only print once, since ``iterations`` is empty and\ndefaults to ``1``. The second example will print five times.\n\nRecall that options don't care about their order. So, either of the following\nwill work:\n\n.. code-block:: bash\n\n    $ python application.py demo:greet John --iterations=5 --yell\n    $ python application.py demo:greet John --yell --iterations=5\n\nThere are 4 option variants you can use:\n\n================================  =================================== ======================================================================================\nOption                            Notation                            Value\n================================  =================================== ======================================================================================\n``clikit.OPTION_MULTI_VALUED``    ``--option=*``                      This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``)\n``clikit.OPTION_NO_VALUE``        ``--option``                        Do not accept input for this option (e.g. ``--yell``)\n``clikit.OPTION_REQUIRED_VALUE``  ``--option=``                       This value is required (e.g. ``--iterations=5``), the option itself is still optional\n``clikit.OPTION_OPTIONAL_VALUE``  ``--option=?``                      This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``)\n================================  =================================== ======================================================================================\n\nYou can combine them like this:\n\n.. code-block:: python\n\n    class GreetCommand(Command):\n        \"\"\"\n        Greets someone\n\n        greet\n            {name? : Who do you want to greet?}\n            {--y|yell : If set, the task will yell in uppercase letters}\n            {--iterations=?*1 : How many times should the message be printed?}\n        \"\"\"\n\n\nTesting Commands\n----------------\n\nCleo provides several tools to help you test your commands. The most\nuseful one is the ``CommandTester`` class.\nIt uses a special IO class to ease testing without a real\nconsole:\n\n.. code-block:: python\n\n    import pytest\n\n    from cleo import Application\n    from cleo import CommandTester\n\n    def test_execute(self):\n        application = Application()\n        application.add(GreetCommand())\n\n        command = application.find('demo:greet')\n        command_tester = CommandTester(command)\n        command_tester.execute()\n\n        assert \"...\" == tester.io.fetch_output()\n\nThe ``CommandTester.io.fetch_output()`` method returns what would have been displayed\nduring a normal call from the console. ``CommandTester.io.fetch_error()`` is also available\nto get what you have been written to the stderr.\n\nYou can test sending arguments and options to the command by passing them\nas a string to the ``CommandTester.execute()`` method:\n\n.. code-block:: python\n\n    import pytest\n\n    from cleo import Application\n    from cleo import CommandTester\n\n    def test_execute(self):\n        application = Application()\n        application.add(GreetCommand())\n\n        command = application.find('demo:greet')\n        command_tester = CommandTester(command)\n        command_tester.execute(\"John\")\n\n        assert \"John\" in tester.io.fetch_output()\n\nYou can also test a whole console application by using the ``ApplicationTester`` class.\n\n\nCalling an existing Command\n---------------------------\n\nIf a command depends on another one being run before it, instead of asking the\nuser to remember the order of execution, you can call it directly yourself.\nThis is also useful if you want to create a \"meta\" command that just runs a\nbunch of other commands.\n\nCalling a command from another one is straightforward:\n\n.. code-block:: python\n\n    def handle(self):\n        return_code = self.call('demo:greet', \"John --yell\")\n\n        # ...\n\nIf you want to suppress the output of the executed command,\nyou can use the ``call_silent()`` method instead.\n\n\n\nAutocompletion\n--------------\n\nCleo supports automatic (tab) completion in ``bash``, ``zsh`` and ``fish``.\n\nTo activate support for autocompletion, pass a ``complete`` keyword when initializing\nyour application:\n\n.. code-block:: python\n\n    application = Application('My Application', '0.1', complete=True)\n\nNow, register completion for your application by running one of the following in a terminal,\nreplacing ``[program]`` with the command you use to run your application:\n\n.. code-block:: bash\n\n    # BASH - Ubuntu / Debian\n    [program] completions bash | sudo tee /etc/bash_completion.d/[program].bash-completion\n\n    # BASH - Mac OSX (with Homebrew \"bash-completion\")\n    [program] completions bash > $(brew --prefix)/etc/bash_completion.d/[program].bash-completion\n\n    # ZSH - Config file\n    mkdir ~/.zfunc\n    echo \"fpath+=~/.zfunc\" >> ~/.zshrc\n    [program] completions zsh > ~/.zfunc/_test\n\n    # FISH\n    [program] completions fish > ~/.config/fish/completions/[program].fish\n\n    # FISH - Mac OSX (with Homebrew \"fish\")\n    [program] completions fish > $(brew --prefix)/share/fish/vendor_completions.d/[program].fish\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/directory_pep_610-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: directory-pep-610\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/directory_pep_610-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"file:///path/to/distributions/directory-pep-610\",\n  \"dir_info\": {}\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable-2.3.4.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: editable\nVersion: 2.3.4\nSummary: Editable description.\nLicense: MIT\nKeywords: cli,commands\nAuthor: Foo Bar\nAuthor-email: foo@bar.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n\nEditable\n####\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable-src-dir-2.3.4.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: editable-src-dir\nVersion: 2.3.4\nSummary: Editable description.\nLicense: MIT\nKeywords: cli,commands\nAuthor: Foo Bar\nAuthor-email: foo@bar.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n\nEditable\n####\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable-src-dir.pth",
    "content": "/path/to/editable/src\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable-with-import-2.3.4.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: editable-with-import\nVersion: 2.3.4\nSummary: Editable description.\nLicense: MIT\nKeywords: cli,commands\nAuthor: Foo Bar\nAuthor-email: foo@bar.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n\nEditable\n####\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable-with-import.pth",
    "content": "import os\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable.pth",
    "content": "/path/to/editable\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable_directory_pep_610-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: editable-directory-pep-610\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/editable_directory_pep_610-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"file:///path/to/distributions/directory-pep-610\",\n  \"dir_info\": {\n    \"editable\": true\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/file_pep_610-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: file-pep-610\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/file_pep_610-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"file:///path/to/distributions/file-pep-610-1.2.3.tar.gz\",\n  \"archive_info\": {\n    \"hash\": \"sha256=2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\"\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: git-pep-610\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"https://github.com/demo/git-pep-610.git\",\n  \"vcs_info\": {\n    \"vcs\": \"git\",\n    \"requested_revision\": \"my-branch\",\n    \"commit_id\": \"123456\"\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610_no_requested_version-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: git-pep-610-no-requested-version\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610_no_requested_version-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"https://github.com/demo/git-pep-610-no-requested-version.git\",\n  \"vcs_info\": {\n    \"vcs\": \"git\",\n    \"commit_id\": \"123456\"\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610_subdirectory-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: git-pep-610-subdirectory\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/git_pep_610_subdirectory-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"https://github.com/demo/git-pep-610-subdirectory.git\",\n  \"vcs_info\": {\n    \"vcs\": \"git\",\n    \"requested_revision\": \"my-branch\",\n    \"commit_id\": \"123456\"\n  },\n  \"subdirectory\": \"subdir\"\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: standard\nVersion: 1.2.3\nSummary: Standard description.\nLicense: MIT\nKeywords: cli,commands\nAuthor: Foo Bar\nAuthor-email: foo@bar.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n\nEditable\n####\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard.pth",
    "content": "standard\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/url_pep_610-1.2.3.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: url-pep-610\nVersion: 1.2.3\nSummary: Foo\nLicense: MIT\nRequires-Python: >=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib/python3.7/site-packages/url_pep_610-1.2.3.dist-info/direct_url.json",
    "content": "{\n  \"url\": \"https://mock.pythonhosted.org/distributions/url-pep-610-1.2.3.tar.gz\",\n  \"archive_info\": {}\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib64/python3.7/site-packages/bender-2.0.5.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: bender\nVersion: 2.0.5\nSummary: Python datetimes made easy\nLicense: MIT\nKeywords: cli,commands\nAuthor: Leela\nAuthor-email: leela@planetexpress.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib64/python3.7/site-packages/bender.pth",
    "content": "../../../src/bender\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/lib64/python3.7/site-packages/lib64-2.3.4.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: lib64\nVersion: 2.3.4\nSummary: lib64 description.\nLicense: MIT\nKeywords: cli,commands\nAuthor: Foo Bar\nAuthor-email: foo@bar.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n\nlib64\n####\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/src/bender/bender.egg-info/PKG-INFO",
    "content": "Metadata-Version: 2.1\nName: bender\nVersion: 2.0.5\nSummary: Python datetimes made easy\nLicense: MIT\nKeywords: cli,commands\nAuthor: Leela\nAuthor-email: leela@planetexpress.com\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nDescription-Content-Type: text/x-rst\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/src/pendulum/pendulum.egg-info/PKG-INFO",
    "content": "Metadata-Version: 1.2\nName: pendulum\nVersion: 2.0.5\nSummary: Python datetimes made easy\nHome-page: https://pendulum.eustace.io\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: UNKNOWN\nDescription: Pendulum\n        ########\n\n        .. image:: https://img.shields.io/pypi/v/pendulum.svg\n            :target: https://pypi.python.org/pypi/pendulum\n\n        .. image:: https://img.shields.io/pypi/l/pendulum.svg\n            :target: https://pypi.python.org/pypi/pendulum\n\n        .. image:: https://img.shields.io/codecov/c/github/sdispater/pendulum/master.svg\n            :target: https://codecov.io/gh/sdispater/pendulum/branch/master\n\n        .. image:: https://travis-ci.org/sdispater/pendulum.svg\n            :alt: Pendulum Build status\n            :target: https://travis-ci.org/sdispater/pendulum\n\n        Python datetimes made easy.\n\n        Supports Python **2.7** and **3.4+**.\n\n\n        .. code-block:: python\n\n           >>> import pendulum\n\n           >>> now_in_paris = pendulum.now('Europe/Paris')\n           >>> now_in_paris\n           '2016-07-04T00:49:58.502116+02:00'\n\n           # Seamless timezone switching\n           >>> now_in_paris.in_timezone('UTC')\n           '2016-07-03T22:49:58.502116+00:00'\n\n           >>> tomorrow = pendulum.now().add(days=1)\n           >>> last_week = pendulum.now().subtract(weeks=1)\n\n           >>> past = pendulum.now().subtract(minutes=2)\n           >>> past.diff_for_humans()\n           >>> '2 minutes ago'\n\n           >>> delta = past - last_week\n           >>> delta.hours\n           23\n           >>> delta.in_words(locale='en')\n           '6 days 23 hours 58 minutes'\n\n           # Proper handling of datetime normalization\n           >>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris')\n           '2013-03-31T03:30:00+02:00' # 2:30 does not exist (Skipped time)\n\n           # Proper handling of dst transitions\n           >>> just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris')\n           '2013-03-31T01:59:59.999999+01:00'\n           >>> just_before.add(microseconds=1)\n           '2013-03-31T03:00:00+02:00'\n\n\n        Why Pendulum?\n        =============\n\n        Native ``datetime`` instances are enough for basic cases but when you face more complex use-cases\n        they often show limitations and are not so intuitive to work with.\n        ``Pendulum`` provides a cleaner and more easy to use API while still relying on the standard library.\n        So it's still ``datetime`` but better.\n\n        Unlike other datetime libraries for Python, Pendulum is a drop-in replacement\n        for the standard ``datetime`` class (it inherits from it), so, basically, you can replace all your ``datetime``\n        instances by ``DateTime`` instances in you code (exceptions exist for libraries that check\n        the type of the objects by using the ``type`` function like ``sqlite3`` or ``PyMySQL`` for instance).\n\n        It also removes the notion of naive datetimes: each ``Pendulum`` instance is timezone-aware\n        and by default in ``UTC`` for ease of use.\n\n        Pendulum also improves the standard ``timedelta`` class by providing more intuitive methods and properties.\n\n\n        Why not Arrow?\n        ==============\n\n        Arrow is the most popular datetime library for Python right now, however its behavior\n        and API can be erratic and unpredictable. The ``get()`` method can receive pretty much anything\n        and it will try its best to return something while silently failing to handle some cases:\n\n        .. code-block:: python\n\n            arrow.get('2016-1-17')\n            # <Arrow [2016-01-01T00:00:00+00:00]>\n\n            pendulum.parse('2016-1-17')\n            # <Pendulum [2016-01-17T00:00:00+00:00]>\n\n            arrow.get('20160413')\n            # <Arrow [1970-08-22T08:06:53+00:00]>\n\n            pendulum.parse('20160413')\n            # <Pendulum [2016-04-13T00:00:00+00:00]>\n\n            arrow.get('2016-W07-5')\n            # <Arrow [2016-01-01T00:00:00+00:00]>\n\n            pendulum.parse('2016-W07-5')\n            # <Pendulum [2016-02-19T00:00:00+00:00]>\n\n            # Working with DST\n            just_before = arrow.Arrow(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')\n            just_after = just_before.replace(microseconds=1)\n            '2013-03-31T02:00:00+02:00'\n            # Should be 2013-03-31T03:00:00+02:00\n\n            (just_after.to('utc') - just_before.to('utc')).total_seconds()\n            -3599.999999\n            # Should be 1e-06\n\n            just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')\n            just_after = just_before.add(microseconds=1)\n            '2013-03-31T03:00:00+02:00'\n\n            (just_after.in_timezone('utc') - just_before.in_timezone('utc')).total_seconds()\n            1e-06\n\n        Those are a few examples showing that Arrow cannot always be trusted to have a consistent\n        behavior with the data you are passing to it.\n\n\n        Limitations\n        ===========\n\n        Even though the ``DateTime`` class is a subclass of ``datetime`` there are some rare cases where\n        it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with\n        a possible solution, if any:\n\n        * ``sqlite3`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:\n\n        .. code-block:: python\n\n            from pendulum import DateTime\n            from sqlite3 import register_adapter\n\n            register_adapter(DateTime, lambda val: val.isoformat(' '))\n\n        * ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:\n\n        .. code-block:: python\n\n            import MySQLdb.converters\n            import pymysql.converters\n\n            from pendulum import DateTime\n\n            MySQLdb.converters.conversions[DateTime] = MySQLdb.converters.DateTime2literal\n            pymysql.converters.conversions[DateTime] = pymysql.converters.escape_datetime\n\n        * ``django`` will use the ``isoformat()`` method to store datetimes in the database. However since ``pendulum`` is always timezone aware the offset information will always be returned by ``isoformat()`` raising an error, at least for MySQL databases. To work around it you can either create your own ``DateTimeField`` or use the previous workaround for ``MySQLdb``:\n\n        .. code-block:: python\n\n            from django.db.models import DateTimeField as BaseDateTimeField\n            from pendulum import DateTime\n\n\n            class DateTimeField(BaseDateTimeField):\n\n                def value_to_string(self, obj):\n                    val = self.value_from_object(obj)\n\n                    if isinstance(value, DateTime):\n                        return value.to_datetime_string()\n\n                    return '' if val is None else val.isoformat()\n\n\n        Resources\n        =========\n\n        * `Official Website <https://pendulum.eustace.io>`_\n        * `Documentation <https://pendulum.eustace.io/docs/>`_\n        * `Issue Tracker <https://github.com/sdispater/pendulum/issues>`_\n\n\n        Contributing\n        ============\n\n        Contributions are welcome, especially with localization.\n\n        Getting started\n        ---------------\n\n        To work on the Pendulum codebase, you'll want to clone the project locally\n        and install the required depedendencies via `poetry <https://python-poetry.org>`_.\n\n        .. code-block:: bash\n\n            $ git clone git@github.com:sdispater/pendulum.git\n            $ poetry install\n\n        Localization\n        ------------\n\n        If you want to help with localization, there are two different cases: the locale already exists\n        or not.\n\n        If the locale does not exist you will need to create it by using the ``clock`` utility:\n\n        .. code-block:: bash\n\n            ./clock locale create <your-locale>\n\n        It will generate a directory in ``pendulum/locales`` named after your locale, with the following\n        structure:\n\n        .. code-block:: text\n\n            <your-locale>/\n                - custom.py\n                - locale.py\n\n        The ``locale.py`` file must not be modified. It contains the translations provided by\n        the CLDR database.\n\n        The ``custom.py`` file is the one you want to modify. It contains the data needed\n        by Pendulum that are not provided by the CLDR database. You can take the `en <https://github.com/sdispater/pendulum/tree/master/pendulum/locales/en/custom.py>`_\n        data as a reference to see which data is needed.\n\n        You should also add tests for the created or modified locale.\n\nPlatform: UNKNOWN\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/src/pendulum/pendulum.egg-info/requires.txt",
    "content": "python-dateutil<3.0,>=2.6\npytzdata>=2018.3\n\n[:python_version < \"3.5\"]\ntyping<4.0,>=3.6\n"
  },
  {
    "path": "tests/repositories/fixtures/installed/vendor/py3.7/attrs-19.3.0.dist-info/METADATA",
    "content": "Metadata-Version: 2.1\nName: attrs\nVersion: 19.3.0\nSummary: Classes Without Boilerplate\nHome-page: https://www.attrs.org/\nAuthor: Hynek Schlawack\nAuthor-email: hs@ox.cx\nMaintainer: Hynek Schlawack\nMaintainer-email: hs@ox.cx\nLicense: MIT\nProject-URL: Documentation, https://www.attrs.org/\nProject-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues\nProject-URL: Source Code, https://github.com/python-attrs/attrs\nKeywords: class,attribute,boilerplate\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nDescription-Content-Type: text/x-rst\nProvides-Extra: azure-pipelines\nRequires-Dist: coverage ; extra == 'azure-pipelines'\nRequires-Dist: hypothesis ; extra == 'azure-pipelines'\nRequires-Dist: pympler ; extra == 'azure-pipelines'\nRequires-Dist: pytest (>=4.3.0) ; extra == 'azure-pipelines'\nRequires-Dist: six ; extra == 'azure-pipelines'\nRequires-Dist: zope.interface ; extra == 'azure-pipelines'\nRequires-Dist: pytest-azurepipelines ; extra == 'azure-pipelines'\nProvides-Extra: dev\nRequires-Dist: coverage ; extra == 'dev'\nRequires-Dist: hypothesis ; extra == 'dev'\nRequires-Dist: pympler ; extra == 'dev'\nRequires-Dist: pytest (>=4.3.0) ; extra == 'dev'\nRequires-Dist: six ; extra == 'dev'\nRequires-Dist: zope.interface ; extra == 'dev'\nRequires-Dist: sphinx ; extra == 'dev'\nRequires-Dist: pre-commit ; extra == 'dev'\nProvides-Extra: docs\nRequires-Dist: sphinx ; extra == 'docs'\nRequires-Dist: zope.interface ; extra == 'docs'\nProvides-Extra: tests\nRequires-Dist: coverage ; extra == 'tests'\nRequires-Dist: hypothesis ; extra == 'tests'\nRequires-Dist: pympler ; extra == 'tests'\nRequires-Dist: pytest (>=4.3.0) ; extra == 'tests'\nRequires-Dist: six ; extra == 'tests'\nRequires-Dist: zope.interface ; extra == 'tests'\n\n.. image:: https://www.attrs.org/en/latest/_static/attrs_logo.png\n   :alt: attrs Logo\n\n======================================\n``attrs``: Classes Without Boilerplate\n======================================\n\n.. image:: https://readthedocs.org/projects/attrs/badge/?version=stable\n   :target: https://www.attrs.org/en/stable/?badge=stable\n   :alt: Documentation Status\n\n.. image:: https://attrs.visualstudio.com/attrs/_apis/build/status/python-attrs.attrs?branchName=master\n   :target: https://attrs.visualstudio.com/attrs/_build/latest?definitionId=1&branchName=master\n   :alt: CI Status\n\n.. image:: https://codecov.io/github/python-attrs/attrs/branch/master/graph/badge.svg\n   :target: https://codecov.io/github/python-attrs/attrs\n   :alt: Test Coverage\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n   :target: https://github.com/psf/black\n   :alt: Code style: black\n\n.. teaser-begin\n\n``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 <https://nedbatchelder.com/blog/200605/dunder.html>`_ methods).\n\nIts main goal is to help you to write **concise** and **correct** software without slowing down your code.\n\n.. -spiel-end-\n\nFor that, it gives you a class decorator and a way to declaratively define the attributes on that class:\n\n.. -code-begin-\n\n.. code-block:: pycon\n\n   >>> import attr\n\n   >>> @attr.s\n   ... class SomeClass(object):\n   ...     a_number = attr.ib(default=42)\n   ...     list_of_numbers = attr.ib(factory=list)\n   ...\n   ...     def hard_math(self, another_number):\n   ...         return self.a_number + sum(self.list_of_numbers) * another_number\n\n\n   >>> sc = SomeClass(1, [1, 2, 3])\n   >>> sc\n   SomeClass(a_number=1, list_of_numbers=[1, 2, 3])\n\n   >>> sc.hard_math(3)\n   19\n   >>> sc == SomeClass(1, [1, 2, 3])\n   True\n   >>> sc != SomeClass(2, [3, 2, 1])\n   True\n\n   >>> attr.asdict(sc)\n   {'a_number': 1, 'list_of_numbers': [1, 2, 3]}\n\n   >>> SomeClass()\n   SomeClass(a_number=42, list_of_numbers=[])\n\n   >>> C = attr.make_class(\"C\", [\"a\", \"b\"])\n   >>> C(\"foo\", \"bar\")\n   C(a='foo', b='bar')\n\n\nAfter *declaring* your attributes ``attrs`` gives you:\n\n- a concise and explicit overview of the class's attributes,\n- a nice human-readable ``__repr__``,\n- a complete set of comparison methods (equality and ordering),\n- an initializer,\n- and much more,\n\n*without* writing dull boilerplate code again and again and *without* runtime performance penalties.\n\nOn Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations <https://www.attrs.org/en/latest/types.html>`_.\n\nThis gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\\ s or `confusingly behaving <https://www.attrs.org/en/stable/why.html#namedtuples>`_ ``namedtuple``\\ s.\nWhich in turn encourages you to write *small classes* that do `one thing well <https://www.destroyallsoftware.com/talks/boundaries>`_.\nNever again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.\n\n\n.. -testimonials-\n\nTestimonials\n============\n\n**Amber Hawkie Brown**, Twisted Release Manager and Computer Owl:\n\n  Writing a fully-functional class using attrs takes me less time than writing this testimonial.\n\n\n**Glyph Lefkowitz**, creator of `Twisted <https://twistedmatrix.com/>`_, `Automat <https://pypi.org/project/Automat/>`_, and other open source software, in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_:\n\n  I’m looking forward to is being able to program in Python-with-attrs everywhere.\n  It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.\n\n\n**Kenneth Reitz**, creator of `Requests <https://github.com/psf/requests>`_ (`on paper no less <https://twitter.com/hynek/status/866817877650751488>`_!):\n\n  attrs—classes for humans.  I like it.\n\n\n**Łukasz Langa**, creator of `Black <https://github.com/psf/black>`_, prolific Python core developer, and release manager for Python 3.8 and 3.9:\n\n  I'm increasingly digging your attr.ocity. Good job!\n\n\n.. -end-\n\n.. -project-information-\n\nGetting Help\n============\n\nPlease use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ to get help.\n\nAnswering questions of your fellow developers is also great way to help the project!\n\n\nProject Information\n===================\n\n``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,\nits documentation lives at `Read the Docs <https://www.attrs.org/>`_,\nthe code on `GitHub <https://github.com/python-attrs/attrs>`_,\nand the latest release on `PyPI <https://pypi.org/project/attrs/>`_.\nIt’s rigorously tested on Python 2.7, 3.4+, and PyPy.\n\nWe collect information on **third-party extensions** in our `wiki <https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs>`_.\nFeel free to browse and add your own!\n\nIf you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide <https://www.attrs.org/en/latest/contributing.html>`_ to get you started!\n\n\nRelease Information\n===================\n\n19.3.0 (2019-10-15)\n-------------------\n\nChanges\n^^^^^^^\n\n- Fixed ``auto_attribs`` usage when default values cannot be compared directly with ``==``, such as ``numpy`` arrays.\n  `#585 <https://github.com/python-attrs/attrs/issues/585>`_\n\n`Full changelog <https://www.attrs.org/en/stable/changelog.html>`_.\n\nCredits\n=======\n\n``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.\n\nThe development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.\n\nA full list of contributors can be found in `GitHub's overview <https://github.com/python-attrs/attrs/graphs/contributors>`_.\n\nIt’s the spiritual successor of `characteristic <https://characteristic.readthedocs.io/>`_ and aspires to fix some of it clunkiness and unfortunate decisions.\nBoth were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `subclassing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/absolute.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for poetry</title>\n  </head>\n  <body>\n    <h1>Links for poetry</h1>\n    <a href=\"https://files.pythonhosted.org/packages/e9/df/0ab4afa9c5d9e6b690c5c27c9f50330b98a7ecfe1185ce2dc1b19188b064/poetry-0.1.0-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0-py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/d5/c5/4efe096ce56505435ccbe8aeefcd5c8c3bb0da211ef8fe58934d087daef2/poetry-0.1.0.tar.gz#sha256=db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 3907384-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/black.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Links for black</title></head>\n<body>\n    <h1>Links for black</h1>\n    <a href=\"https://files.pythonhosted.org/packages/fd/bb/ad34bbc93d1bea3de086d7c59e528d4a503ac8fe318bd1fa48605584c3d2/black-19.10b0-py36-none-any.whl#sha256=13001c5b7dbc81137164b43137320a1785e95ce84e4db849279786877ac6d7f6\" data-requires-python=\">=3.6\">black-19.10b0-py36-none-any.whl</a>\n    <a href=\"https://files.pythonhosted.org/packages/b0/dc/ecd83b973fb7b82c34d828aad621a6e5865764d52375b8ac1d7a45e23c8d/black-19.10b0.tar.gz#sha256=c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539\" data-requires-python=\"&gt;=3.6\" >black-19.10b0.tar.gz</a>\n    <a href=\"https://files.pythonhosted.org/packages/3d/ad/1cf514e7f9ee4c3d8df7c839d7977f7605ad76557f3fca741ec67f76dba6/black-21.11b0-py3-none-any.whl#sha256=38f6ad54069912caf2fa2d4f25d0c5dedef4b2338a0cb545dbe2fdf54a6a8891\" data-requires-python=\">=3.6.2\" data-yanked=\"Broken regex dependency. Use 21.11b1 instead.\">black-21.11b0-py3-none-any.whl</a>\n    <a href=\"https://files.pythonhosted.org/packages/2f/db/03e8cef689ab0ff857576ee2ee288d1ff2110ef7f3a77cac62e61f18acaf/black-21.11b0.tar.gz#sha256=83f3852301c8dcb229e9c444dd79f573c8d31c7c2dad9bbaaa94c808630e32aa\" data-requires-python=\"&gt;=3.6.2\" data-yanked=\"Broken regex dependency. Use 21.11b1 instead.\" >black-21.11b0.tar.gz</a>\n    </body>\n</html>\n<!--SERIAL 6044498-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/clikit.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for clikit</title>\n  </head>\n  <body>\n    <h1>Links for clikit</h1>\n    <a href=\"https://files.pythonhosted.org/packages/6a/35/3f36466a5b90bc92e3f20dee4a058285d8551afbe7a3d075b08b2abf0429/clikit-0.2.3-py2.py3-none-any.whl#sha256=e5061859212a32093e638c80307923330ab67d4356f1dfa0d1822dcc1853042f\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">clikit-0.2.3-py2.py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/04/b0/ec6c05c0e182e54e5829390df0bafb8c5b2308973decf60b9fd84ab397c7/clikit-0.2.3.tar.gz#sha256=e5061859212a32093e638c80307923330ab67d4356f1dfa0d1822dcc1853042f\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">clikit-0.2.3.tar.gz</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/7b/0d/bb4c8a2d0edca8c300373ed736fb4680cf73be5be2ff84544dee5f979c14/clikit-0.2.4-py2.py3-none-any.whl#sha256=27316bf6382b04be8fb2f60c85d538fd2b2b03f0f1eba5c88f7d7eddbefc2778\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">clikit-0.2.4-py2.py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/c5/33/14fad4c82f256b0ef60dd25d4b6d8145b463da5274fd9cd842f06af318ed/clikit-0.2.4.tar.gz#sha256=0fdd41e86e8b118a8b1e94ef2835925ada541d481c9b3b2fc635fa68713e6125\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">clikit-0.2.4.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 5256718-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/demo.html",
    "content": "<html>\n<head><title>Simple Index</title><meta name=\"api-version\" value=\"2\" /></head>\n<body>\n<a href=\"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz#md5=d1912c917363a64e127318655f7d1fe7\" rel=\"internal\">demo-0.1.0.tar.gz</a><br/>\n</body>\n</html>\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/discord-py.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Links for discord-py</title>\n<body>\n    <h1>Links for discord-py</h1>\n    <a href=\"https://files.pythonhosted.org/packages/0e/d9/7b057cab41c16144925ba4f96dab576a8ebb7b80a98d40e06bd94298eb3b/discord.py-2.0.0-py3-none-any.whl#sha256=25b9739ba456622655203a0925b354c0ba96ac6c740562e7c37791c2f6b594fb\" data-requires-python=\"&gt;=3.8.0\" >discord.py-2.0.0-py3-none-any.whl</a><br />\n    <a href=\"https://files.pythonhosted.org/packages/4c/73/fb89115b07588bf7a46e9eca972b89dd62b5856abd52297fe130b41d9d63/discord.py-2.0.0.tar.gz#sha256=b86fa9dd562684f7a52564e6dfe0216f6c172a009c0d86b8dea8bdd6ffa6b1f4\" data-requires-python=\"&gt;=3.8.0\" >discord.py-2.0.0.tar.gz</a><br />\n    </body>\n</html>\n<!--SERIAL 14796560-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/futures-partial-yank.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for futures</title>\n  </head>\n  <body>\n    <h1>Links for futures</h1>\n    <a href=\"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl#sha256=41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\" data-requires-python=\"&gt;=2.6, &lt;3\" data-yanked>futures-3.2.0-py2-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/1f/9e/7b2ff7e965fc654592269f2906ade1c7d705f1bf25b7d469fa153f7d19eb/futures-3.2.0.tar.gz#sha256=baf0d469c9e541b747986b7404cd63a5496955bd0c43a3cc068c449b09b7d4a4\" data-requires-python=\"&gt;=2.6, &lt;3\">futures-3.2.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 3865286-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/futures.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for futures</title>\n  </head>\n  <body>\n    <h1>Links for futures</h1>\n    <a href=\"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl#sha256=41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\" data-requires-python=\"&gt;=2.6, &lt;3\">futures-3.2.0-py2-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/1f/9e/7b2ff7e965fc654592269f2906ade1c7d705f1bf25b7d469fa153f7d19eb/futures-3.2.0.tar.gz#sha256=baf0d469c9e541b747986b7404cd63a5496955bd0c43a3cc068c449b09b7d4a4\" data-requires-python=\"&gt;=2.6, &lt;3\">futures-3.2.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 3865286-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/invalid-version.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for poetry</title>\n  </head>\n  <body>\n    <h1>Links for poetry</h1>\n    <a href=\"poetry-21.07.28.5ffb65e2ff8067c732e2b178d03b707c7fb27855-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\" data-requires-python=\"&gt;=3.6.0\">poetry-21.07.28.5ffb65e2ff8067c732e2b178d03b707c7fb27855-py3-none-any.whl</a><br/>\n    <a href=\"poetry-0.1.0-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0-py3-none-any.whl</a><br/>\n    </body>\n</html>\n<!--SERIAL 3907384-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/ipython.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for ipython</title>\n  </head>\n  <body>\n    <h1>Links for ipython</h1>\n    <a href=\"https://files.pythonhosted.org/packages/ac/02/04a5d372b4e64f9c97b2846646aec1ce4532885005aa4ba51eb20b80e17f/ipython-4.1.0rc1-py2.py3-none-any.whl#md5=2aff56d8e78341f64663bcbc81366376\" data-requires-python=\"&gt;=3.5\">ipython-4.1.0rc1-py2.py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/a0/de/f71f0c8b8a26ef28cc968fbf1859729ad3e68146cd2eb4a759e9c88da218/ipython-4.1.0rc1.tar.gz#sha256=efa3a5a676648cb18e2a2d3cd6353f3c83f0f704df8eb0eb6ae7d0dcbf187ea1\" data-requires-python=\"&gt;=3.5\">ipython-4.1.0rc1.tar.gz</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/52/19/aadde98d6bde1667d0bf431fb2d22451f880aaa373e0a241c7e7cb5815a0/ipython-5.7.0-py2-none-any.whl#md5=20da5e0b1f79dccb37f033a885d798d7\">ipython-5.7.0-py2-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/c7/b6/03e0b5b0972e6161d16c4cec8d41a20372bd0634f8cb4cc0c984b8a91db6/ipython-5.7.0-py3-none-any.whl#sha256=4292c026552a77b2edc0543941516eddd6fe1a4b681a76ac40b3f585d2fca76f\">ipython-5.7.0-py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/3c/fd/559fead731a29eaa55cc235c8029807b2520976a937c30e9ee603f3bb566/ipython-5.7.0.tar.gz#sha256=4e7fb265e0264498bd0d62c6261936a658bf3d38beb8a7b10cd2c6327c62ac2a\">ipython-5.7.0.tar.gz</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/a9/2e/41dce4ed129057e05a555a7f9629aa2d5f81fdcd4d16568bc24b75a1d2c9/ipython-7.5.0-py3-none-any.whl#md5=f40ea889fb7adf989760c5e7a38bd112\" data-requires-python=\"&gt;=3.5\">ipython-7.5.0-py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/75/74/9b0ef91c8e356c907bb12297000951acb804583b54eeaddc342c5bad4d96/ipython-7.5.0.tar.gz#sha256=cd2a17ac273fea8bf8953118a2d83bad94f592f0db3e83fff9129a1842e36dbe\" data-requires-python=\"&gt;=3.5\">ipython-7.5.0.tar.gz</a><br/>\n  </body>\n</html>\n<!--SERIAL 4837213-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/isort-metadata.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Links for isort</title>\n</head>\n<body>\n<h1>Links for isort</h1>\n<a href=\"https://files.pythonhosted.org/packages/1f/2c/non-existent/isort-metadata-4.3.4-py3-none-any.whl#sha256=1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af\"\n   data-dist-info-metadata=\"sha256=e360bf0ed8a06390513d50dd5b7e9d635c789853a93b84163f9de4ae0647580c\">isort-metadata-4.3.4-py3-none-any.whl</a><br/>\n</body>\n</html>\n<!--SERIAL 3575149-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/isort.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for isort</title>\n  </head>\n  <body>\n    <h1>Links for isort</h1>\n    <a href=\"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/isort-4.3.4-py2-none-any.whl#sha256=383c39c10b5db83e8d150ac5b84d74bda96e3a1b06a30257f022dcbcd21f54b9\">isort-4.3.4-py2-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/1f/2c/22eee714d7199ae0464beda6ad5fedec8fee6a2f7ffd1e8f1840928fe318/isort-4.3.4-py3-none-any.whl#sha256=5668dce9fb48544c57ed626982e190c8ea99e3a612850453e9c3b193b9fa2edc\">isort-4.3.4-py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/b1/de/a628d16fdba0d38cafb3d7e34d4830f2c9cb3881384ce5c08c44762e1846/isort-4.3.4.tar.gz#sha256=234ad07e1e2780c27fa56364eefa734bee991b0d744337ef7e7ce3d5b1b59f39\">isort-4.3.4.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 3575149-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/_readme",
    "content": "Files from tests/repositories/fixtures/pypi.org/json are used as fallback!\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/absolute.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"https://files.pythonhosted.org/packages/e9/df/0ab4afa9c5d9e6b690c5c27c9f50330b98a7ecfe1185ce2dc1b19188b064/poetry-0.1.0-py3-none-any.whl\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"https://files.pythonhosted.org/packages/d5/c5/4efe096ce56505435ccbe8aeefcd5c8c3bb0da211ef8fe58934d087daef2/poetry-0.1.0.tar.gz\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"poetry\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/demo.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"demo-0.1.0.tar.gz\",\n      \"url\": \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"demo\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/invalid-version.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-21.07.28.5ffb65e2ff8067c732e2b178d03b707c7fb27855-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"poetry-21.07.28.5ffb65e2ff8067c732e2b178d03b707c7fb27855-py3-none-any.whl\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"poetry-0.1.0-py3-none-any.whl\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"poetry\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\",\n    \"21.07.28.5ffb65e2ff8067c732e2b178d03b707c7fb27855\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/isort-metadata.json",
    "content": "{\n  \"name\": \"isort-metadata\",\n  \"files\": [\n    {\n      \"filename\": \"isort-metadata-4.3.4-py3-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/2c/non-existent/isort-metadata-4.3.4-py3-none-any.whl\",\n      \"dist-info-metadata\": {\n        \"sha256\": \"e360bf0ed8a06390513d50dd5b7e9d635c789853a93b84163f9de4ae0647580c\"\n      },\n      \"hashes\": {\n        \"sha256\": \"1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"api-version\": \"1.0\",\n    \"_last-serial\": 3575149\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/jupyter.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"jupyter-1.0.0.tar.gz\",\n      \"url\": \"https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"jupyter\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.0.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/poetry-test-py2-py3-metadata-merge.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/52/19/poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/c7/b6/poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"poetry-test-py2-py3-metadata-merge\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/relative.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"poetry-0.1.0-py3-none-any.whl\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"poetry-0.1.0.tar.gz\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry-0.1.1.tar.bz2\",\n      \"hashes\": {\n        \"sha256\": \"db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\"\n      },\n      \"requires-python\": \">=3.6.0\",\n      \"url\": \"poetry-0.1.1.tar.bz2\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"poetry\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\",\n    \"0.1.1\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/json/sqlalchemy-legacy.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"sqlalchemy-legacy-4.3.4-py2-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/sqlalchemy-legacy-4.3.4-py2-none-any.whl\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.0\"\n  },\n  \"name\": \"sqlalchemy-legacy\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"4.3.4\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/jupyter.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for jupyter</title>\n  </head>\n  <body>\n    <h1>Links for jupyter</h1>\n    <a href=\"https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz\">jupyter-1.0.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 1673841-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/missing-version.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for poetry</title>\n  </head>\n  <body>\n    <h1>Links for poetry</h1>\n    <a href=\"poetry-0.1.0-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0-py3-none-any.whl</a><br/>\n    </body>\n</html>\n<!--SERIAL 3907384-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/pastel.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for pastel</title>\n  </head>\n  <body>\n    <h1>Links for pastel</h1>\n    <a href=\"https://files.pythonhosted.org/packages/bd/13/a68f2e448b471e8c49e9b596d569ae167a5135ac672b1dc5f24f62f9c15f/pastel-0.1.0.tar.gz#sha256=22f14474c4120b37c54ac2173b49b0ac1de9283ca714be6eb3ea8b39296285a9\">pastel-0.1.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 2612998-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/poetry-test-py2-py3-metadata-merge.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for poetry-test-py2-py3-metadata-merge</title>\n  </head>\n  <body>\n    <h1>Links for ipython</h1>\n    <a href=\"https://files.pythonhosted.org/packages/52/19/poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl\">poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/c7/b6/poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl\">poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl</a><br/>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/pytest-with-extra-packages.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <title>Links for pytest</title>\n</head>\n\n<body>\n  <h1>Links for pytest</h1>\n  <a href=\"https://files.pythonhosted.org/packages/ed/96/271c93f75212c06e2a7ec3e2fa8a9c90acee0a4838dc05bf379ea09aae31/pytest-3.5.0-py2.py3-none-any.whl#sha256=427b4582bda18e92ad1967e8b1e071e2c53e6cb7e3e5f090cb3ca443455be23f\"\n    data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">pytest-3.5.0-py2.py3-none-any.whl</a><br />\n  <a href=\"https://files.pythonhosted.org/packages/2d/56/6019153cdd743300c5688ab3b07702355283e53c83fbf922242c053ffb7b/pytest-3.5.0.tar.gz#sha256=677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68\"\n    data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">pytest-3.5.0.tar.gz</a><br />\n  <a href=\"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl#sha256=41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\"\n    data-requires-python=\"&gt;=2.6, &lt;3\">futures-3.2.0-py2-none-any.whl</a><br />\n  <a href=\"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl#sha256=41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\"\n    data-requires-python=\"&gt;=2.6, &lt;3\">pytest-3.10.0-py2.py3-none-any.whl</a><br />\n  <a href=\"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl#sha256=41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\"\n    data-requires-python=\"&gt;=2.6, &lt;3\">pytest-3.5.0-py2.py3-none-any.whl</a><br />\n</body>\n\n</html>\n<!--SERIAL 7198641-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/pytest.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for pytest</title>\n  </head>\n  <body>\n    <h1>Links for pytest</h1><a href=\"https://files.pythonhosted.org/packages/ed/96/271c93f75212c06e2a7ec3e2fa8a9c90acee0a4838dc05bf379ea09aae31/pytest-3.5.0-py2.py3-none-any.whl#sha256=427b4582bda18e92ad1967e8b1e071e2c53e6cb7e3e5f090cb3ca443455be23f\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">pytest-3.5.0-py2.py3-none-any.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/2d/56/6019153cdd743300c5688ab3b07702355283e53c83fbf922242c053ffb7b/pytest-3.5.0.tar.gz#sha256=677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">pytest-3.5.0.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 7198641-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/python-language-server.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for python-language-server</title>\n  </head>\n  <body>\n    <h1>Links for python-language-server</h1>\n    <a href=\"https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz#sha256=91b564e092f3135b2bac70dbd23d283da5ad50269766a76648787b69fe702c7e\">python-language-server-0.21.2.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 4245719-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/pyyaml.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for python-language-server</title>\n  </head>\n  <body>\n    <h1>Links for python-language-server</h1>\n    <a href=\"https://files.pythonhosted.org/packages/5c/ed/d6557f70daaaab6ee5cd2f8ccf7bedd63081e522e38679c03840e1acc114/PyYAML-3.13-cp37-cp37m-win32.whl#sha256=a0184bd61052fa6942d65c294468116eaf00f43669437d46ad5530f7005c7f37\">PyYAML-3.13-cp37-cp37m-win32.whl</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz#sha256=e6c07f136ba90afc1b049d7577b4f955845d796f359324662758f403929fd481\">PyYAML-3.13.tar.gz</a><br/>\n    <a href=\"https://files.pythonhosted.org/packages/0f/9d/f98ed0a460dc540f720bbe5c6e076f025595cdfa3e318fad27165db13cf9/PyYAML-4.2b2.tar.gz#sha256=3bb5ac01c26ea2d1745a491da8c31d35a70ad69e236ddcf9fbcb493d464db101\">PyYAML-4.2b2.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 4245719-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/relative.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for poetry</title>\n  </head>\n  <body>\n    <h1>Links for poetry</h1>\n    <a href=\"poetry-0.1.0-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0-py3-none-any.whl</a><br/>\n    <a href=\"poetry-0.1.0.tar.gz#sha256=db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.0.tar.gz</a><br/>\n    <a href=\"poetry-0.1.1.tar.bz2#sha256=db33179244321b0b86c6c3645225ff2062ed3495ca16d0d64b3a5df804a82273\" data-requires-python=\"&gt;=3.6.0\">poetry-0.1.1.tar.bz</a><br/>\n    </body>\n</html>\n<!--SERIAL 3907384-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/sqlalchemy-legacy.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for sqlalchemy-legacy</title>\n  </head>\n  <body>\n    <h1>Links for sqlalchemy-legacy</h1>\n    <a href=\"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/sqlalchemy-legacy-4.3.4-py2-none-any.whl#sha256=383c39c10b5db83e8d150ac5b84d74bda96e3a1b06a30257f022dcbcd21f54b9\">sqlalchemy-legacy-4.3.4-py2-none-any.whl</a><br/>\n    </body>\n</html>\n<!--SERIAL 3575149-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy/tomlkit.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Links for tomlkit</title>\n  </head>\n  <body>\n    <h1>Links for tomlkit</h1>\n    <a href=\"https://files.pythonhosted.org/packages/f6/8c/c27d292cf7c0f04f0e1b5c75ab95dc328542ccbe9a809a1eada66c897bd2/tomlkit-0.5.2.tar.gz#sha256=4a226ccf11ee5a2e76bfc185747b54ee7718706aeb3aabb981327249dbe2b1d4\" data-requires-python=\"&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\">tomlkit-0.5.2.tar.gz</a><br/>\n    </body>\n</html>\n<!--SERIAL 4504211-->\n"
  },
  {
    "path": "tests/repositories/fixtures/legacy.py",
    "content": "from __future__ import annotations\n\nimport json\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nimport pytest\nimport responses\n\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES_LEGACY\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES_PYPI\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from packaging.utils import NormalizedName\n    from pytest import FixtureRequest\n    from pytest_mock import MockerFixture\n    from requests import PreparedRequest\n\n    from poetry.repositories.link_sources.base import LinkSource\n    from tests.types import HttpRequestCallback\n    from tests.types import HttpResponse\n    from tests.types import NormalizedNameTransformer\n    from tests.types import SpecializedLegacyRepositoryMocker\n\npytest_plugins = [\n    \"tests.repositories.fixtures.python_hosted\",\n]\n\n\nclass TestLegacyRepository(LegacyRepository):\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        super().__init__(*args, **kwargs)\n        self.json = False\n\n\n@pytest.fixture\ndef legacy_repository_directory() -> Path:\n    return FIXTURE_PATH_REPOSITORIES_LEGACY\n\n\n@pytest.fixture\ndef legacy_package_json_locations() -> list[Path]:\n    return [\n        FIXTURE_PATH_REPOSITORIES_LEGACY / \"json\",\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"json\",\n    ]\n\n\n@pytest.fixture\ndef legacy_repository_package_names(legacy_repository_directory: Path) -> set[str]:\n    return {\n        package_html_file.stem\n        for package_html_file in legacy_repository_directory.glob(\"*.html\")\n    }\n\n\n@pytest.fixture\ndef legacy_repository_index_html(\n    legacy_repository_directory: Path, legacy_repository_package_names: set[str]\n) -> str:\n    hrefs = [\n        f'<a href=\"{name}/\">{name}</a><br>' for name in legacy_repository_package_names\n    ]\n\n    return f\"\"\"<!DOCTYPE html>\n    <html>\n        <head>\n            Legacy Repository\n        </head>\n        <body>\n        {\"\".join(hrefs)}\n        </body>\n    </html>\n    <!--TIMESTAMP 1709913893-->\n    \"\"\"\n\n\n@pytest.fixture\ndef legacy_repository_index_json(\n    legacy_repository_directory: Path, legacy_repository_package_names: set[str]\n) -> dict[str, Any]:\n    names = [{\"name\": name} for name in legacy_repository_package_names]\n    return {\"meta\": {\"api-version\": \"1.4\"}, \"projects\": names}\n\n\n@pytest.fixture\ndef legacy_repository_url() -> str:\n    return \"https://legacy.foo.bar\"\n\n\n@pytest.fixture\ndef legacy_repository_html_callback(\n    legacy_repository_directory: Path,\n    legacy_repository_index_html: str,\n) -> HttpRequestCallback:\n    def html_callback(request: PreparedRequest) -> HttpResponse:\n        assert request.url\n        if name := Path(urlparse(request.url).path).name:\n            fixture = legacy_repository_directory / f\"{name}.html\"\n\n            if not fixture.exists():\n                return 404, {}, b\"Not Found\"\n\n            return 200, {}, fixture.read_bytes()\n\n        return 200, {}, legacy_repository_index_html.encode(\"utf-8\")\n\n    return html_callback\n\n\n@pytest.fixture\ndef legacy_repository_json_callback(\n    legacy_package_json_locations: list[Path],\n    legacy_repository_index_json: dict[str, Any],\n) -> HttpRequestCallback:\n    def json_callback(request: PreparedRequest) -> HttpResponse:\n        assert request.url\n        headers = {\"Content-Type\": \"application/vnd.pypi.simple.v1+json\"}\n        if name := Path(urlparse(request.url).path).name:\n            fixture = Path()\n            for location in legacy_package_json_locations:\n                fixture = location / f\"{name}.json\"\n                if fixture.exists():\n                    break\n\n            if not fixture.exists():\n                return 404, {}, b\"Not Found\"\n\n            return 200, headers, fixture.read_bytes()\n\n        return 200, headers, json.dumps(legacy_repository_index_json).encode(\"utf-8\")\n\n    return json_callback\n\n\n@pytest.fixture\ndef legacy_repository_html(\n    http: responses.RequestsMock,\n    legacy_repository_url: str,\n    legacy_repository_html_callback: HttpRequestCallback,\n    mock_files_python_hosted: None,\n) -> TestLegacyRepository:\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"^https://legacy\\.(.*)+/?(.*)?$\"),\n        callback=legacy_repository_html_callback,\n    )\n\n    repo = TestLegacyRepository(\"legacy\", legacy_repository_url, disable_cache=True)\n    repo.json = False\n    return repo\n\n\n@pytest.fixture\ndef legacy_repository_json(\n    http: responses.RequestsMock,\n    legacy_repository_url: str,\n    legacy_repository_json_callback: HttpRequestCallback,\n    mock_files_python_hosted: None,\n) -> TestLegacyRepository:\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"^https://legacy\\.(.*)+/?(.*)?$\"),\n        callback=legacy_repository_json_callback,\n    )\n\n    repo = TestLegacyRepository(\"legacy\", legacy_repository_url, disable_cache=True)\n    repo.json = True\n    return repo\n\n\n@pytest.fixture(params=[\"legacy_repository_html\", \"legacy_repository_json\"])\ndef legacy_repository(request: FixtureRequest) -> TestLegacyRepository:\n    return request.getfixturevalue(request.param)  # type: ignore[no-any-return]\n\n\n@pytest.fixture\ndef specialized_legacy_repository_mocker(\n    legacy_repository_html: LegacyRepository,\n    legacy_repository_url: str,\n    mocker: MockerFixture,\n) -> SpecializedLegacyRepositoryMocker:\n    \"\"\"\n    This is a mocker factory that allows tests cases to intercept and redirect to special case legacy html files by\n    creating an instance of the mocked legacy repository and then mocking its get_page method for special cases.\n    \"\"\"\n\n    def mock(\n        transformer_or_suffix: NormalizedNameTransformer | str,\n        repository_name: str = \"special\",\n        repository_url: str = legacy_repository_url,\n    ) -> LegacyRepository:\n        specialized_repository = LegacyRepository(\n            repository_name, repository_url, disable_cache=True\n        )\n        original_get_page = specialized_repository._get_page\n\n        def _mocked_get_page(name: NormalizedName) -> LinkSource:\n            return original_get_page(\n                canonicalize_name(f\"{name}{transformer_or_suffix}\")\n                if isinstance(transformer_or_suffix, str)\n                else transformer_or_suffix(name)\n            )\n\n        mocker.patch.object(specialized_repository, \"get_page\", _mocked_get_page)\n\n        return specialized_repository\n\n    return mock\n\n\n@pytest.fixture\ndef legacy_repository_with_extra_packages(\n    specialized_legacy_repository_mocker: SpecializedLegacyRepositoryMocker,\n) -> LegacyRepository:\n    return specialized_legacy_repository_mocker(\"-with-extra-packages\")\n\n\n@pytest.fixture\ndef legacy_repository_partial_yank(\n    specialized_legacy_repository_mocker: SpecializedLegacyRepositoryMocker,\n) -> LegacyRepository:\n    return specialized_legacy_repository_mocker(\"-partial-yank\")\n\n\n@pytest.fixture\ndef get_legacy_dist_url(legacy_repository_directory: Path) -> Callable[[str], str]:\n    def get_url(name: str) -> str:\n        package_name = name.split(\"-\", 1)[0]\n        path = legacy_repository_directory / f\"{package_name}.html\"\n        if not path.exists():\n            raise RuntimeError(\n                f\"Fixture for {package_name}.html not found in legacy fixtures\"\n            )\n        content = path.read_text(encoding=\"utf-8\")\n        match = re.search(rf'<a href=\"(?P<url>[^\"]+{re.escape(name)}[^\"]*)\"', content)\n        if not match:\n            raise RuntimeError(\n                f\"No URL for {name} found in legacy fixture {package_name}.html\"\n            )\n        return match.group(\"url\").split(\"#\", 1)[0]\n\n    return get_url\n\n\n@pytest.fixture\ndef get_legacy_dist_size_and_upload_time(\n    legacy_package_json_locations: list[Path],\n) -> Callable[[str], tuple[int | None, str | None]]:\n    def get_size_and_upload_time(name: str) -> tuple[int | None, str | None]:\n        package_name = name.split(\"-\", 1)[0]\n        fixture_name = f\"{package_name}.json\"\n        path = Path()\n        for location in legacy_package_json_locations:\n            path = location / fixture_name\n            if path.exists():\n                break\n        if not path.exists():\n            raise RuntimeError(\n                f\"Fixture for {fixture_name} not found in legacy fixtures\"\n            )\n        with path.open(\"rb\") as f:\n            content = json.load(f)\n        for file in content[\"files\"]:\n            if file[\"filename\"] == name:\n                size = file.get(\"size\")\n                upload_time = file.get(\"upload-time\")\n                return int(size) if size else None, upload_time\n        raise RuntimeError(f\"No URL for {name} found in legacy fixture {fixture_name}\")\n\n    return get_size_and_upload_time\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/generate.py",
    "content": "\"\"\"\nThis is a helper script built to generate mocked PyPI json files and release files. Executing the script does the\nfollowing for a specified list of releases.\n\n1. Fetch relevant project json file from https://pypi.org/simple/<name>.\n2. Fetch relevant release json file from https://pypi.org/pypi/<name>/<version>/json.\n3. Download all files (if not otherwise specified) for each release, including <filename>.metadata files.\n4. Stub (zero-out) all files not relevant for test cases, only sdist and bdist metadata is retained.\n    a, We also retain `__init__.py` files as some packages use it for dynamic version detection when building sdist.\n    b. Some release bdist, notably that of setuptools, wheel and poetry-core are retained as is in the `dist/` directory\n        as these are required for some test cases.\n    c. All stubbed files are written out to `stubbed/` directory.\n    d. All stubbed files produce a consistent hash.\n5. New checksums (sha256 and md5) are calculated and replaced in the following locations.\n    a. All mocked json files.\n    b. Installation lock file fixtures.\n    c. Legacy Repository mocked html files.\n6. All unwanted files and metadata is removed from any json file written. This includes any release versions removed.\n7. A distribution hash getter fixture is generated.\n\nThe following also applies.\n\n1. Local json files are preferred over remote ones unless `refresh=True` is specified.\n    a. On removal or addition of a new version for a package, the base package must be refreshed. Otherwise,\n       the new files added will not reflect in the file.\n    b. You can also remove the existing file and re-run the script.\n2. Download of distributions already present in `dist/` is skipped.\n3. The `stubbed/` directory is cleared for each run.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport dataclasses\nimport hashlib\nimport io\nimport json\nimport logging\nimport os\nimport re\nimport shutil\nimport sys\nimport tarfile\nimport zipfile\n\nfrom functools import cached_property\nfrom gzip import GzipFile\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nfrom packaging.metadata import parse_email\nfrom poetry.core.masonry.utils.helpers import normalize_file_permissions\nfrom poetry.core.packages.package import Package\n\nfrom poetry.repositories.pypi_repository import PyPiRepository\nfrom tests.helpers import FIXTURE_PATH\nfrom tests.helpers import FIXTURE_PATH_DISTRIBUTIONS\nfrom tests.helpers import FIXTURE_PATH_INSTALLATION\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES_LEGACY\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES_PYPI\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n\n    import requests\n\n    from poetry.core.packages.utils.link import Link\n\nENABLE_RELEASE_JSON = True\n\nlogger = logging.getLogger(\"pypi.generator\")\nlogger.setLevel(logging.INFO)\nhandler = logging.StreamHandler(sys.stdout)\nhandler.setFormatter(logging.Formatter(\"%(asctime)s %(message)s\"))\nlogger.addHandler(handler)\n\n\n@dataclasses.dataclass(frozen=True)\nclass _ReleaseFileLocations:\n    dist: Path = dataclasses.field(\n        default=FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\"dist\")\n    )\n    mocked: Path = dataclasses.field(\n        default=FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\"dist\", \"mocked\")\n    )\n    stubbed: Path = dataclasses.field(\n        default=FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\"stubbed\")\n    )\n    demo: Path = dataclasses.field(default=FIXTURE_PATH_DISTRIBUTIONS)\n\n\nRELEASE_FILE_LOCATIONS = _ReleaseFileLocations()\n\n\n@dataclasses.dataclass\nclass ReleaseFileMetadata:\n    path: Path\n    md5: str = dataclasses.field(init=False)\n    sha256: str = dataclasses.field(init=False)\n\n    def __post_init__(self) -> None:\n        data = self.path.read_bytes()\n        self.sha256 = hashlib.sha256(data).hexdigest()\n        self.md5 = hashlib.md5(data).hexdigest()\n\n\nclass _ReleaseFileCollection:\n    def __init__(self, locations: list[Path] | None = None) -> None:\n        self.locations = locations or [\n            RELEASE_FILE_LOCATIONS.dist,\n            RELEASE_FILE_LOCATIONS.stubbed,\n        ]\n\n    def filename_exists(self, filename: str) -> bool:\n        return any(location.joinpath(filename).exists() for location in self.locations)\n\n    def find(self, filename: str) -> ReleaseFileMetadata | None:\n        for location in self.locations:\n            if location.joinpath(filename).exists():\n                return ReleaseFileMetadata(location)\n        return None\n\n    def list(self, location: Path | None = None) -> Iterator[ReleaseFileMetadata]:\n        locations = [location] if location is not None else self.locations\n        for candidate in locations:\n            for file in candidate.glob(\"*.tar.*\"):\n                yield ReleaseFileMetadata(file)\n\n            for file in candidate.glob(\"*.zip\"):\n                yield ReleaseFileMetadata(file)\n\n            for file in candidate.glob(\"*.whl\"):\n                yield ReleaseFileMetadata(file)\n\n\nRELEASE_FILE_COLLECTION = _ReleaseFileCollection()\n\n\ndef generate_distribution_hashes_fixture(files: list[ReleaseFileMetadata]) -> None:\n    fixture_py = FIXTURE_PATH_REPOSITORIES / \"distribution_hashes.py\"\n    files.sort(key=lambda f: f.path.name)\n\n    text = \",\\n\".join(\n        [\n            f'    \"{file.path.name}\": DistributionHash(\\n'\n            f'        \"{file.sha256}\",\\n'\n            f'        \"{file.md5}\",\\n'\n            f\"    )\"\n            for file in files\n        ]\n    )\n\n    logger.info(\n        \"Generating fixture file at %s\",\n        fixture_py.relative_to(FIXTURE_PATH.parent.parent),\n    )\n\n    fixture_py.write_text(\n        f\"\"\"# this file is generated by {Path(__file__).relative_to(FIXTURE_PATH.parent.parent).as_posix()}\nfrom __future__ import annotations\n\nimport dataclasses\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from tests.types import DistributionHashGetter\n\n\n@dataclasses.dataclass\nclass DistributionHash:\n    sha256: str = \"\"\n    md5: str = \"\"\n\n\nKNOWN_DISTRIBUTION_HASHES = {{\n{text},\n}}\n\n\n@pytest.fixture\ndef dist_hash_getter() -> DistributionHashGetter:\n    def get_hash(name: str) -> DistributionHash:\n        return KNOWN_DISTRIBUTION_HASHES.get(name, DistributionHash())\n\n    return get_hash\n\"\"\",\n        encoding=\"utf-8\",\n    )\n\n\ndef cleanup_legacy_html_hashes(metadata: ReleaseFileMetadata) -> None:\n    for index in FIXTURE_PATH_REPOSITORIES_LEGACY.glob(\"*.html\"):\n        existing_content = index.read_text(encoding=\"utf-8\")\n\n        content = re.sub(\n            f\"{metadata.path.name}#sha256=[A-Fa-f0-9]{{64}}\",\n            f\"{metadata.path.name}#sha256={metadata.sha256}\",\n            existing_content,\n        )\n        content = re.sub(\n            f'data-dist-info-metadata=\"sha256=[A-Fa-f0-9]{{64}}\">{metadata.path.name}<',\n            f'data-dist-info-metadata=\"sha256={metadata.sha256}\">{metadata.path.name}<',\n            content,\n        )\n        content = re.sub(\n            f\"{metadata.path.name}#md5=[A-Fa-f0-9]{{32}}\",\n            f\"{metadata.path.name}#md5={metadata.md5}\",\n            content,\n        )\n        content = re.sub(\n            f'data-dist-info-metadata=\"md5=[A-Fa-f0-9]{{32}}\">{metadata.path.name}<',\n            f'data-dist-info-metadata=\"md5={metadata.md5}\">{metadata.path.name}<',\n            content,\n        )\n\n        if existing_content != content:\n            logger.info(\"Rewriting hashes in %s\", index)\n            index.write_text(content, encoding=\"utf-8\")\n\n\ndef cleanup_installation_fixtures(metadata: ReleaseFileMetadata) -> None:\n    for file in FIXTURE_PATH_INSTALLATION.glob(\"*.test\"):\n        original_content = file.read_text(encoding=\"utf-8\")\n\n        content = re.sub(\n            f'file = \"{metadata.path.name}\", hash = \"sha256:[A-Fa-f0-9]{{64}}\"',\n            f'file = \"{metadata.path.name}\", hash = \"sha256:{metadata.sha256}\"',\n            original_content,\n        )\n        content = re.sub(\n            f'file = \"{metadata.path.name}\", hash = \"md5:[A-Fa-f0-9]{{32}}\"',\n            f'file = \"{metadata.path.name}\", hash = \"md5:{metadata.md5}\"',\n            content,\n        )\n\n        if content != original_content:\n            logger.info(\"Rewriting hashes in %s\", file)\n            file.write_text(content, encoding=\"utf-8\")\n\n\nclass FileManager:\n    def __init__(self, pypi: PyPiRepository) -> None:\n        self.pypi = pypi\n\n    @staticmethod\n    def should_preserve_file_content_check(link: Link) -> Callable[[str], bool]:\n        def sdist_check(filename: str) -> bool:\n            return filename in {\n                \"pyproject.toml\",\n                \"setup.py\",\n                \"setup.cfg\",\n                \"PKG-INFO\",\n                \"__init__.py\",\n                \"requires.txt\",\n                \"requirements.txt\",\n                \"entry_points.txt\",\n                \"top_level.txt\",\n            }\n\n        bdist_preserve_regex = re.compile(r\"^((?!/).)*\\.dist-info/((?!/).)*$\")\n\n        def bdist_check(filename: str) -> bool:\n            return bool(bdist_preserve_regex.match(filename))\n\n        if link.is_sdist:\n            return sdist_check\n\n        return bdist_check\n\n    def process_metadata_file(self, link: Link) -> None:\n        # we enforce the availability of the metadata file\n        link._metadata = True\n\n        logger.info(\"Processing metadata file for %s\", link.filename)\n\n        assert link.metadata_url is not None\n        response: requests.Response = self.pypi.session.get(\n            link.metadata_url, raise_for_status=False\n        )\n\n        if response.status_code != 200:\n            logger.info(\"Skipping metadata for %s\", link.filename)\n            return None\n\n        metadata, _ = parse_email(response.content)\n        content = response.content.decode(encoding=\"utf-8\").replace(\n            metadata[\"description\"], \"\"\n        )\n\n        FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\n            \"metadata\", f\"{link.filename}.metadata\"\n        ).write_text(content, encoding=\"utf-8\", newline=\"\\n\")\n\n    def copy_as_is(self, link: Link) -> ReleaseFileMetadata:\n        dst = FIXTURE_PATH_REPOSITORIES_PYPI / \"dists\" / link.filename\n        logger.info(\n            \"Preserving release file from %s to %s\",\n            link.url,\n            dst.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n        )\n\n        with self.pypi._cached_or_downloaded_file(link) as src:\n            shutil.copy(src, dst)\n\n        return ReleaseFileMetadata(dst)\n\n    def process_zipfile(self, link: Link) -> ReleaseFileMetadata:\n        dst = FIXTURE_PATH_REPOSITORIES_PYPI / \"stubbed\" / link.filename\n        is_protected = self.should_preserve_file_content_check(link)\n\n        logger.info(\n            \"Stubbing release file from %s to %s\",\n            link.url,\n            dst.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n        )\n\n        with (\n            self.pypi._cached_or_downloaded_file(link) as src,\n            zipfile.ZipFile(\n                dst, \"w\", compression=zipfile.ZIP_DEFLATED\n            ) as stubbed_sdist,\n            zipfile.ZipFile(src) as zf,\n        ):\n            for member in zf.infolist():\n                if not is_protected(member.filename):\n                    logger.debug(\"Stubbing file %s(%s)\", link.filename, member.filename)\n                    stubbed_sdist.writestr(member, io.BytesIO().getvalue())\n\n                elif Path(member.filename).name == \"RECORD\":\n                    # Since unprotected files are stubbed to be zero size, the RECORD file must\n                    # be updated to match.\n                    stubbed_content = io.StringIO()\n                    for line in zf.read(member.filename).decode(\"utf-8\").splitlines():\n                        filename = line.split(\",\")[0]\n                        if is_protected(filename):\n                            stubbed_content.write(f\"{line}\\n\")\n                            continue\n\n                        stubbed_line = re.sub(\n                            \",sha256=.*\",\n                            \",sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\",\n                            line,\n                        )\n                        stubbed_content.write(f\"{stubbed_line}\\n\")\n\n                    stubbed_sdist.writestr(member, stubbed_content.getvalue())\n\n                else:\n                    logger.debug(\n                        \"Preserving file %s(%s)\", link.filename, member.filename\n                    )\n                    stubbed_sdist.writestr(member, zf.read(member.filename))\n\n        return ReleaseFileMetadata(dst)\n\n    def process_tarfile(self, link: Link) -> ReleaseFileMetadata:\n        dst = FIXTURE_PATH_REPOSITORIES_PYPI / \"stubbed\" / link.filename\n        is_protected = self.should_preserve_file_content_check(link)\n\n        logger.info(\n            \"Stubbing release file from %s to %s\",\n            link.url,\n            dst.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n        )\n\n        with (\n            self.pypi._cached_or_downloaded_file(link) as src,\n            GzipFile(dst.as_posix(), mode=\"wb\", mtime=0) as gz,\n            tarfile.TarFile(\n                dst, mode=\"w\", fileobj=gz, format=tarfile.PAX_FORMAT\n            ) as dst_tf,\n            tarfile.open(src, \"r\") as src_tf,\n        ):\n            for member in src_tf.getmembers():\n                member.uid = 0\n                member.gid = 0\n                member.uname = \"\"\n                member.gname = \"\"\n                member.mtime = 0\n                member.mode = normalize_file_permissions(member.mode)\n\n                if member.isfile() and not is_protected(Path(member.name).name):\n                    logger.debug(\"Stubbing file %s(%s)\", link.filename, member.name)\n                    file_obj = io.BytesIO()\n                    member.size = file_obj.getbuffer().nbytes\n                    dst_tf.addfile(member, file_obj)\n                else:\n                    logger.debug(\"Preserving file %s(%s)\", link.filename, member.name)\n                    dst_tf.addfile(member, src_tf.extractfile(member))\n\n        os.utime(dst, (0, 0))\n\n        return ReleaseFileMetadata(dst)\n\n\nclass Project:\n    def __init__(self, name: str, releases: list[Release]):\n        self.name = name\n        self.releases: list[Release] = releases\n\n    @property\n    def filenames(self) -> list[str]:\n        filenames = []\n\n        for release in self.releases:\n            filenames.extend(release.filenames)\n\n        return filenames\n\n    @property\n    def files(self) -> list[ReleaseFileMetadata]:\n        files = []\n\n        for release in self.releases:\n            files.extend(release.files)\n\n        return files\n\n    @property\n    def versions(self) -> list[str]:\n        return [release.version for release in self.releases]\n\n    @cached_property\n    def json_path(self) -> Path:\n        return FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\"json\", f\"{self.name}.json\")\n\n    @staticmethod\n    def _finalise_file_item(\n        data: dict[str, Any], files: list[ReleaseFileMetadata] | None = None\n    ) -> dict[str, Any]:\n        filename = data[\"filename\"]\n\n        for file in files or []:\n            if file.path.name == filename:\n                data[\"hashes\"] = {\"md5\": file.md5, \"sha256\": file.sha256}\n                break\n\n        metadata_file = (\n            FIXTURE_PATH_REPOSITORIES_PYPI / \"metadata\" / f\"{filename}.metadata\"\n        )\n\n        if metadata_file.exists():\n            metadata = ReleaseFileMetadata(metadata_file)\n            for key in [\"core-metadata\", \"data-dist-info-metadata\"]:\n                data[key] = {\"sha256\": metadata.sha256}\n\n        return data\n\n    def _finalise(self, data: dict[str, Any]) -> None:\n        files = self.files\n\n        data[\"versions\"] = self.versions\n\n        data[\"files\"] = [\n            self._finalise_file_item(_file, files)\n            for _file in data[\"files\"]\n            if _file[\"filename\"] in self.filenames\n        ]\n\n        data[\"meta\"][\"_last-serial\"] = 0\n\n        logger.info(\n            \"Finalising up %s\",\n            self.json_path.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n        )\n        self.json_path.write_text(\n            json.dumps(data, ensure_ascii=False, indent=2) + \"\\n\", encoding=\"utf-8\"\n        )\n\n        for file in files:\n            cleanup_installation_fixtures(file)\n            cleanup_legacy_html_hashes(file)\n\n    def populate(self, pypi: PyPiRepository) -> None:\n        logger.info(\"Fetching remote json via https://pypi.org/simple/%s\", self.name)\n        data = (\n            pypi._get(\n                f\"simple/{self.name}/\",\n                headers={\"Accept\": \"application/vnd.pypi.simple.v1+json\"},\n            )\n            or {}\n        )\n\n        for release in self.releases:\n            release.populate(pypi)\n\n        self._finalise(data)\n\n\nclass Release:\n    def __init__(\n        self,\n        name: str,\n        version: str,\n        download_files: bool = True,\n        stub: bool = True,\n        preserved_files: list[str] | None = None,\n    ):\n        self.name = name\n        self.version = version\n        self.filenames: list[str] = preserved_files or []\n        self.download_files: bool = download_files\n        self.stub: bool = stub\n        self.files: list[ReleaseFileMetadata] = []\n\n    @cached_property\n    def json_path(self) -> Path:\n        return (\n            FIXTURE_PATH_REPOSITORIES_PYPI / \"json\" / self.name / f\"{self.version}.json\"\n        )\n\n    @staticmethod\n    def _finalise_file_item(\n        data: dict[str, Any], files: list[ReleaseFileMetadata] | None = None\n    ) -> dict[str, Any]:\n        filename = data[\"filename\"]\n\n        for file in files or []:\n            if file.path.name == filename:\n                data[\"digests\"] = {\"md5\": file.md5, \"sha256\": file.sha256}\n                data[\"md5_digest\"] = file.md5\n                break\n\n        return data\n\n    def _finalise(self, data: dict[str, Any]) -> None:\n        data.get(\"info\", {\"description\": \"\"})[\"description\"] = \"\"\n\n        if \"vulnerabilities\" in data:\n            data[\"vulnerabilities\"] = []\n\n        data[\"urls\"] = [\n            self._finalise_file_item(item, self.files)\n            for item in data[\"urls\"]\n            if item[\"filename\"] in self.filenames\n        ]\n\n        for item in data[\"urls\"]:\n            self._finalise_file_item(item, self.files)\n\n        data[\"last_serial\"] = 0\n\n        logger.info(\n            \"Finalising up %s\",\n            self.json_path.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n        )\n\n        if not self.json_path.parent.exists():\n            self.json_path.parent.mkdir(parents=True, exist_ok=True)\n\n        self.json_path.write_text(\n            json.dumps(data, ensure_ascii=False, indent=2) + \"\\n\", encoding=\"utf-8\"\n        )\n\n    def populate(self, pypi: PyPiRepository) -> None:\n        fm = FileManager(pypi)\n\n        links = pypi.find_links_for_package(Package(self.name, self.version))\n\n        for link in links:\n            self.filenames.append(link.filename)\n\n            fm.process_metadata_file(link)\n\n            if self.download_files:\n                if not self.stub and link.is_wheel:\n                    file = fm.copy_as_is(link)\n                elif link.is_wheel or (\n                    link.is_sdist and link.filename.endswith(\".zip\")\n                ):\n                    file = fm.process_zipfile(link)\n                else:\n                    file = fm.process_tarfile(link)\n\n                self.files.append(file)\n\n        if ENABLE_RELEASE_JSON:\n            logger.info(\n                \"Fetching remote json via https://pypi.org/pypi/%s/%s/json\",\n                self.name,\n                self.version,\n            )\n            data = pypi._get(f\"pypi/{self.name}/{self.version}/json\") or {}\n            self._finalise(data)\n\n\ndef cleanup_old_files(releases: dict[str, list[str]]) -> None:\n    json_fixture_path = FIXTURE_PATH_REPOSITORIES_PYPI / \"json\"\n\n    for json_file in json_fixture_path.glob(\"*.json\"):\n        if json_file.stem not in releases and json_file.stem not in {\"isort-metadata\"}:\n            json_file.unlink()\n\n    for json_file in json_fixture_path.glob(\"*/*.json\"):\n        if json_file.parent.name == \"mocked\":\n            continue\n\n        if (\n            json_file.parent.name not in releases\n            or json_file.stem not in releases[json_file.parent.name]\n        ):\n            logger.info(\n                \"Removing unmanaged release file %s\",\n                json_file.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n            )\n            json_file.unlink()\n\n            if len(list(json_file.parent.iterdir())) == 0:\n                logger.info(\n                    \"Removing empty directory %s\",\n                    json_file.parent.relative_to(FIXTURE_PATH_REPOSITORIES_PYPI),\n                )\n                json_file.parent.rmdir()\n\n\nPROJECTS = [\n    Project(\"attrs\", releases=[Release(\"attrs\", \"17.4.0\")]),\n    Project(\n        \"black\", releases=[Release(\"black\", \"19.10b0\"), Release(\"black\", \"21.11b0\")]\n    ),\n    Project(\"cleo\", releases=[Release(\"cleo\", \"1.0.0a5\")]),\n    Project(\"clikit\", releases=[Release(\"clikit\", \"0.2.4\")]),\n    # tests.installation.test_installer.test_installer_with_pypi_repository on windows\n    Project(\"colorama\", releases=[Release(\"colorama\", \"0.3.9\")]),\n    Project(\"discord-py\", releases=[Release(\"discord-py\", \"2.0.0\")]),\n    Project(\"funcsigs\", releases=[Release(\"funcsigs\", \"1.0.2\", download_files=False)]),\n    Project(\"filecache\", releases=[Release(\"filecache\", \"0.81\", download_files=False)]),\n    Project(\"futures\", releases=[Release(\"futures\", \"3.2.0\")]),\n    # tests.repositories.test_pypi_repository.test_get_release_info_includes_only_supported_types\n    Project(\n        \"hbmqtt\",\n        releases=[\n            Release(\n                \"hbmqtt\",\n                \"0.9.6\",\n                preserved_files=[\n                    \"hbmqtt-0.9.6.linux-x86_64.tar.gz\",\n                    \"hbmqtt-0.9.6-py3.8.egg\",\n                ],\n            )\n        ],\n    ),\n    Project(\n        \"importlib-metadata\",\n        releases=[Release(\"importlib-metadata\", \"1.7.0\", download_files=False)],\n    ),\n    Project(\n        \"ipython\",\n        releases=[\n            Release(\"ipython\", \"4.1.0rc1\", download_files=False),\n            # tests.repositories.test_legacy_repository.test_get_package_from_both_py2_and_py3_specific_wheels\n            # tests.repositories.test_legacy_repository.test_get_package_retrieves_non_sha256_hashes_mismatching_known_hash\n            Release(\"ipython\", \"5.7.0\"),\n            # tests.repositories.test_legacy_repository.test_get_package_retrieves_non_sha256_hashes\n            # tests.repositories.test_legacy_repository.test_get_package_with_dist_and_universal_py3_wheel\n            Release(\"ipython\", \"7.5.0\"),\n        ],\n    ),\n    # yanked, no dependencies\n    Project(\"isodate\", releases=[Release(\"isodate\", \"0.7.0\")]),\n    Project(\"isort\", releases=[Release(\"isort\", \"4.3.4\")]),\n    Project(\"jupyter\", releases=[Release(\"jupyter\", \"1.0.0\")]),\n    Project(\"more-itertools\", releases=[Release(\"more-itertools\", \"4.1.0\")]),\n    Project(\"pastel\", releases=[Release(\"pastel\", \"0.1.0\")]),\n    Project(\"pluggy\", releases=[Release(\"pluggy\", \"0.6.0\")]),\n    Project(\n        \"poetry-core\",\n        releases=[\n            Release(\"poetry-core\", \"1.5.0\", stub=False),\n            Release(\"poetry-core\", \"2.0.1\", stub=False),\n        ],\n    ),\n    Project(\"py\", releases=[Release(\"py\", \"1.5.3\")]),\n    Project(\"pylev\", releases=[Release(\"pylev\", \"1.3.0\", download_files=False)]),\n    Project(\n        \"pytest\", releases=[Release(\"pytest\", \"3.5.0\"), Release(\"pytest\", \"3.5.1\")]\n    ),\n    # tests.repositories.test_legacy_repository.test_get_package_information_skips_dependencies_with_invalid_constraints\n    Project(\n        \"python-language-server\", releases=[Release(\"python-language-server\", \"0.21.2\")]\n    ),\n    Project(\"pyyaml\", releases=[Release(\"pyyaml\", \"3.13.0\", download_files=False)]),\n    # tests.repositories.test_pypi_repository.test_find_packages\n    Project(\n        \"requests\",\n        releases=[\n            Release(\"requests\", \"2.18.0\", download_files=False),\n            Release(\"requests\", \"2.18.1\", download_files=False),\n            Release(\"requests\", \"2.18.2\", download_files=False),\n            Release(\"requests\", \"2.18.3\", download_files=False),\n            # tests.repositories.test_pypi_repository.test_package\n            Release(\"requests\", \"2.18.4\", download_files=True),\n            Release(\"requests\", \"2.19.0\", download_files=False),\n        ],\n    ),\n    Project(\n        \"setuptools\",\n        releases=[\n            Release(\"setuptools\", \"39.2.0\", download_files=False),\n            Release(\"setuptools\", \"67.6.1\", stub=False),\n        ],\n    ),\n    Project(\"six\", releases=[Release(\"six\", \"1.11.0\")]),\n    Project(\"sqlalchemy\", releases=[Release(\"sqlalchemy\", \"1.2.12\")]),\n    # tests.repositories.test_pypi_repository.test_find_packages_with_prereleases\n    Project(\n        \"toga\",\n        releases=[\n            Release(\"toga\", \"0.3.0\", download_files=False),\n            Release(\"toga\", \"0.3.0dev1\", download_files=False),\n            Release(\"toga\", \"0.3.0dev2\", download_files=False),\n            Release(\"toga\", \"0.4.0\", download_files=False),\n        ],\n    ),\n    Project(\n        \"tomlkit\", releases=[Release(\"tomlkit\", \"0.5.2\"), Release(\"tomlkit\", \"0.5.3\")]\n    ),\n    Project(\"twisted\", releases=[Release(\"twisted\", \"18.9.0\")]),\n    Project(\"wheel\", releases=[Release(\"wheel\", \"0.40.0\", stub=False)]),\n    Project(\"zipp\", releases=[Release(\"zipp\", \"3.5.0\")]),\n]\n\n\ndef main() -> None:\n    pypi = PyPiRepository(disable_cache=False)\n    files: list[ReleaseFileMetadata] = []\n    releases: dict[str, list[str]] = {}\n\n    for project in PROJECTS:\n        project = Project(project.name, releases=project.releases)\n        project.populate(pypi)\n\n        releases[project.name] = project.versions\n        files.extend(project.files)\n\n    rfc = _ReleaseFileCollection(\n        [RELEASE_FILE_LOCATIONS.demo, RELEASE_FILE_LOCATIONS.mocked]\n    )\n    files.extend(rfc.list())\n\n    generate_distribution_hashes_fixture(files)\n\n    cleanup_old_files(releases)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/attrs/17.4.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Hynek Schlawack\",\n    \"author_email\": \"hs@ox.cx\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Natural Language :: English\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://www.attrs.org/\",\n    \"keywords\": \"class,attribute,boilerplate\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"attrs\",\n    \"package_url\": \"https://pypi.org/project/attrs/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/attrs/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://www.attrs.org/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/attrs/17.4.0/\",\n    \"requires_dist\": [\n      \"coverage; extra == 'dev'\",\n      \"hypothesis; extra == 'dev'\",\n      \"pympler; extra == 'dev'\",\n      \"pytest; extra == 'dev'\",\n      \"six; extra == 'dev'\",\n      \"zope.interface; extra == 'dev'\",\n      \"sphinx; extra == 'dev'\",\n      \"zope.interface; extra == 'dev'\",\n      \"sphinx; extra == 'docs'\",\n      \"zope.interface; extra == 'docs'\",\n      \"coverage; extra == 'tests'\",\n      \"hypothesis; extra == 'tests'\",\n      \"pympler; extra == 'tests'\",\n      \"pytest; extra == 'tests'\",\n      \"six; extra == 'tests'\",\n      \"zope.interface; extra == 'tests'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Classes Without Boilerplate\",\n    \"version\": \"17.4.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"7fe37931797b16c7fa158017457a9ea9\",\n        \"sha256\": \"1fbfc10ebc8c876dcbab17f016b80ae1a4f0c1413461a695871427960795beb4\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"attrs-17.4.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"7fe37931797b16c7fa158017457a9ea9\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 31658,\n      \"upload_time\": \"2017-12-30T08:20:05\",\n      \"upload_time_iso_8601\": \"2017-12-30T08:20:05.582456Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b5/60/4e178c1e790fd60f1229a9b3cb2f8bc2f4cc6ff2c8838054c142c70b5adc/attrs-17.4.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"c03e5b3608d9071fbd098850d8922668\",\n        \"sha256\": \"eb7536a1e6928190b3008c5b350bdf9850d619fff212341cd096f87a27a5e564\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"attrs-17.4.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"c03e5b3608d9071fbd098850d8922668\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 97071,\n      \"upload_time\": \"2017-12-30T08:20:08\",\n      \"upload_time_iso_8601\": \"2017-12-30T08:20:08.575620Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8b/0b/a06cfcb69d0cb004fde8bc6f0fd192d96d565d1b8aa2829f0f20adb796e5/attrs-17.4.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/attrs.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"a1828f9b7a019e96302759189410f380814be1dd57a201a56c078f9e8e11a2e5\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"a1828f9b7a019e96302759189410f380814be1dd57a201a56c078f9e8e11a2e5\"\n      },\n      \"filename\": \"attrs-17.4.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"7fe37931797b16c7fa158017457a9ea9\",\n        \"sha256\": \"1fbfc10ebc8c876dcbab17f016b80ae1a4f0c1413461a695871427960795beb4\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 31658,\n      \"upload-time\": \"2017-12-30T08:20:05.582456Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b5/60/4e178c1e790fd60f1229a9b3cb2f8bc2f4cc6ff2c8838054c142c70b5adc/attrs-17.4.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"attrs-17.4.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"c03e5b3608d9071fbd098850d8922668\",\n        \"sha256\": \"eb7536a1e6928190b3008c5b350bdf9850d619fff212341cd096f87a27a5e564\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 97071,\n      \"upload-time\": \"2017-12-30T08:20:08.575620Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8b/0b/a06cfcb69d0cb004fde8bc6f0fd192d96d565d1b8aa2829f0f20adb796e5/attrs-17.4.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"attrs\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"17.4.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/black/19.10b0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Łukasz Langa\",\n    \"author_email\": \"lukasz@langa.pl\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Environment :: Console\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: Software Development :: Quality Assurance\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/psf/black\",\n    \"keywords\": \"automation formatter yapf autopep8 pyfmt gofmt rustfmt\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"black\",\n    \"package_url\": \"https://pypi.org/project/black/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/black/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/psf/black\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/black/19.10b0/\",\n    \"requires_dist\": [\n      \"click (>=6.5)\",\n      \"attrs (>=18.1.0)\",\n      \"appdirs\",\n      \"toml (>=0.9.4)\",\n      \"typed-ast (>=1.4.0)\",\n      \"regex\",\n      \"pathspec (<1,>=0.6)\",\n      \"aiohttp (>=3.3.2) ; extra == 'd'\",\n      \"aiohttp-cors ; extra == 'd'\"\n    ],\n    \"requires_python\": \">=3.6\",\n    \"summary\": \"The uncompromising code formatter.\",\n    \"version\": \"19.10b0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"acc537b0f3f7ebf575616490d7cc14f4\",\n        \"sha256\": \"13001c5b7dbc81137164b43137320a1785e95ce84e4db849279786877ac6d7f6\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"black-19.10b0-py36-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"acc537b0f3f7ebf575616490d7cc14f4\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py36\",\n      \"requires_python\": \">=3.6\",\n      \"size\": 97525,\n      \"upload_time\": \"2019-10-28T23:53:54\",\n      \"upload_time_iso_8601\": \"2019-10-28T23:53:54.000711Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fd/bb/ad34bbc93d1bea3de086d7c59e528d4a503ac8fe318bd1fa48605584c3d2/black-19.10b0-py36-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"c383543109a66a5a99113e6326db5251\",\n        \"sha256\": \"6cada614d5d2132698c6d5fff384657273d922c4fffa6a2f0de9e03e25b8913a\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"black-19.10b0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"c383543109a66a5a99113e6326db5251\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.6\",\n      \"size\": 1019740,\n      \"upload_time\": \"2019-10-28T23:54:05\",\n      \"upload_time_iso_8601\": \"2019-10-28T23:54:05.455213Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b0/dc/ecd83b973fb7b82c34d828aad621a6e5865764d52375b8ac1d7a45e23c8d/black-19.10b0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/black/21.11b0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Łukasz Langa\",\n    \"author_email\": \"lukasz@langa.pl\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Environment :: Console\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: Software Development :: Quality Assurance\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/psf/black\",\n    \"keywords\": \"automation formatter yapf autopep8 pyfmt gofmt rustfmt\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"black\",\n    \"package_url\": \"https://pypi.org/project/black/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/black/\",\n    \"project_urls\": {\n      \"Changelog\": \"https://github.com/psf/black/blob/main/CHANGES.md\",\n      \"Homepage\": \"https://github.com/psf/black\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/black/21.11b0/\",\n    \"requires_dist\": [\n      \"click (>=7.1.2)\",\n      \"platformdirs (>=2)\",\n      \"tomli (<2.0.0,>=0.2.6)\",\n      \"regex (>=2020.1.8)\",\n      \"pathspec (<1,>=0.9.0)\",\n      \"typing-extensions (>=3.10.0.0)\",\n      \"mypy-extensions (>=0.4.3)\",\n      \"dataclasses (>=0.6) ; python_version < \\\"3.7\\\"\",\n      \"typed-ast (>=1.4.2) ; python_version < \\\"3.8\\\" and implementation_name == \\\"cpython\\\"\",\n      \"typing-extensions (!=3.10.0.1) ; python_version >= \\\"3.10\\\"\",\n      \"colorama (>=0.4.3) ; extra == 'colorama'\",\n      \"aiohttp (>=3.7.4) ; extra == 'd'\",\n      \"ipython (>=7.8.0) ; extra == 'jupyter'\",\n      \"tokenize-rt (>=3.2.0) ; extra == 'jupyter'\",\n      \"typed-ast (>=1.4.3) ; extra == 'python2'\",\n      \"uvloop (>=0.15.2) ; extra == 'uvloop'\"\n    ],\n    \"requires_python\": \">=3.6.2\",\n    \"summary\": \"The uncompromising code formatter.\",\n    \"version\": \"21.11b0\",\n    \"yanked\": true,\n    \"yanked_reason\": \"Broken regex dependency. Use 21.11b1 instead.\"\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"92942a9efabf8e321a11360667ad2494\",\n        \"sha256\": \"38f6ad54069912caf2fa2d4f25d0c5dedef4b2338a0cb545dbe2fdf54a6a8891\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"black-21.11b0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"92942a9efabf8e321a11360667ad2494\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.6.2\",\n      \"size\": 155131,\n      \"upload_time\": \"2021-11-17T02:32:14\",\n      \"upload_time_iso_8601\": \"2021-11-17T02:32:14.551680Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3d/ad/1cf514e7f9ee4c3d8df7c839d7977f7605ad76557f3fca741ec67f76dba6/black-21.11b0-py3-none-any.whl\",\n      \"yanked\": true,\n      \"yanked_reason\": \"Broken regex dependency. Use 21.11b1 instead.\"\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"f01267bf2613f825dd6684629c1c829e\",\n        \"sha256\": \"f23c482185d842e2f19d506e55c004061167e3c677c063ecd721042c62086ada\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"black-21.11b0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f01267bf2613f825dd6684629c1c829e\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.6.2\",\n      \"size\": 593164,\n      \"upload_time\": \"2021-11-17T02:32:16\",\n      \"upload_time_iso_8601\": \"2021-11-17T02:32:16.396821Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2f/db/03e8cef689ab0ff857576ee2ee288d1ff2110ef7f3a77cac62e61f18acaf/black-21.11b0.tar.gz\",\n      \"yanked\": true,\n      \"yanked_reason\": \"Broken regex dependency. Use 21.11b1 instead.\"\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/black.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"256c27c1f5cc5c7bad6049ff2c339ac9419f5fc00eddbb895cf4780d7b6752b3\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"256c27c1f5cc5c7bad6049ff2c339ac9419f5fc00eddbb895cf4780d7b6752b3\"\n      },\n      \"filename\": \"black-19.10b0-py36-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"acc537b0f3f7ebf575616490d7cc14f4\",\n        \"sha256\": \"13001c5b7dbc81137164b43137320a1785e95ce84e4db849279786877ac6d7f6\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6\",\n      \"size\": 97525,\n      \"upload-time\": \"2019-10-28T23:53:54.000711Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fd/bb/ad34bbc93d1bea3de086d7c59e528d4a503ac8fe318bd1fa48605584c3d2/black-19.10b0-py36-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"black-19.10b0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"c383543109a66a5a99113e6326db5251\",\n        \"sha256\": \"6cada614d5d2132698c6d5fff384657273d922c4fffa6a2f0de9e03e25b8913a\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6\",\n      \"size\": 1019740,\n      \"upload-time\": \"2019-10-28T23:54:05.455213Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b0/dc/ecd83b973fb7b82c34d828aad621a6e5865764d52375b8ac1d7a45e23c8d/black-19.10b0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"6b5b5209d6862dde7399150665655faa4526565c775f125ad004746fe96d4d4c\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"6b5b5209d6862dde7399150665655faa4526565c775f125ad004746fe96d4d4c\"\n      },\n      \"filename\": \"black-21.11b0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"92942a9efabf8e321a11360667ad2494\",\n        \"sha256\": \"38f6ad54069912caf2fa2d4f25d0c5dedef4b2338a0cb545dbe2fdf54a6a8891\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6.2\",\n      \"size\": 155131,\n      \"upload-time\": \"2021-11-17T02:32:14.551680Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3d/ad/1cf514e7f9ee4c3d8df7c839d7977f7605ad76557f3fca741ec67f76dba6/black-21.11b0-py3-none-any.whl\",\n      \"yanked\": \"Broken regex dependency. Use 21.11b1 instead.\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"black-21.11b0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"f01267bf2613f825dd6684629c1c829e\",\n        \"sha256\": \"f23c482185d842e2f19d506e55c004061167e3c677c063ecd721042c62086ada\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6.2\",\n      \"size\": 593164,\n      \"upload-time\": \"2021-11-17T02:32:16.396821Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2f/db/03e8cef689ab0ff857576ee2ee288d1ff2110ef7f3a77cac62e61f18acaf/black-21.11b0.tar.gz\",\n      \"yanked\": \"Broken regex dependency. Use 21.11b1 instead.\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"black\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"19.10b0\",\n    \"21.11b0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/cleo/1.0.0a5.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/python-poetry/cleo\",\n    \"keywords\": \"cli,commands\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"cleo\",\n    \"package_url\": \"https://pypi.org/project/cleo/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/cleo/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/python-poetry/cleo\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/cleo/1.0.0a5/\",\n    \"requires_dist\": [\n      \"pylev (>=1.3.0,<2.0.0)\",\n      \"crashtest (>=0.3.1,<0.4.0)\"\n    ],\n    \"requires_python\": \">=3.7,<4.0\",\n    \"summary\": \"Cleo allows you to create beautiful and testable command-line interfaces.\",\n    \"version\": \"1.0.0a5\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"19ed7de77063e8f16bc459276ccbe197\",\n        \"sha256\": \"d0cfea878b77be28be027033e6af419b705abe47278067a7c3a298f39cf825c5\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"cleo-1.0.0a5-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"19ed7de77063e8f16bc459276ccbe197\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.7,<4.0\",\n      \"size\": 78701,\n      \"upload_time\": \"2022-06-03T20:16:19\",\n      \"upload_time_iso_8601\": \"2022-06-03T20:16:19.386916Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/45/0c/3825603bf62f360829b1eea29a43dadce30829067e288170b3bf738aafd0/cleo-1.0.0a5-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"92e181952976e09b9d1c583da6c3e2fc\",\n        \"sha256\": \"88f0a4275a17f2ab4d013786b8b9522d4c60bd37d8fc9b3def0fb27f4ac1e694\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"cleo-1.0.0a5.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"92e181952976e09b9d1c583da6c3e2fc\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.7,<4.0\",\n      \"size\": 61431,\n      \"upload_time\": \"2022-06-03T20:16:21\",\n      \"upload_time_iso_8601\": \"2022-06-03T20:16:21.133890Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2f/16/1c1902b225756745f9860451a44a2e2a3c26ee91c72295e83c63df605ed1/cleo-1.0.0a5.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/cleo.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"5627f48cfca57f878bf73ea222fa8ca2f24ad248061a2f0151f22219386838f9\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"5627f48cfca57f878bf73ea222fa8ca2f24ad248061a2f0151f22219386838f9\"\n      },\n      \"filename\": \"cleo-1.0.0a5-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"19ed7de77063e8f16bc459276ccbe197\",\n        \"sha256\": \"d0cfea878b77be28be027033e6af419b705abe47278067a7c3a298f39cf825c5\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7,<4.0\",\n      \"size\": 78701,\n      \"upload-time\": \"2022-06-03T20:16:19.386916Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/45/0c/3825603bf62f360829b1eea29a43dadce30829067e288170b3bf738aafd0/cleo-1.0.0a5-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"cleo-1.0.0a5.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"92e181952976e09b9d1c583da6c3e2fc\",\n        \"sha256\": \"88f0a4275a17f2ab4d013786b8b9522d4c60bd37d8fc9b3def0fb27f4ac1e694\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7,<4.0\",\n      \"size\": 61431,\n      \"upload-time\": \"2022-06-03T20:16:21.133890Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2f/16/1c1902b225756745f9860451a44a2e2a3c26ee91c72295e83c63df605ed1/cleo-1.0.0a5.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"cleo\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.0.0a5\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/clikit/0.2.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/sdispater/clikit\",\n    \"keywords\": \"packaging,dependency,poetry\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"Sébastien Eustace\",\n    \"maintainer_email\": \"sebastien@eustace.io\",\n    \"name\": \"clikit\",\n    \"package_url\": \"https://pypi.org/project/clikit/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/clikit/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/sdispater/clikit\",\n      \"Repository\": \"https://github.com/sdispater/clikit\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/clikit/0.2.4/\",\n    \"requires_dist\": [\n      \"pastel (>=0.1.0,<0.2.0)\",\n      \"pylev (>=1.3,<2.0)\",\n      \"typing (>=3.6,<4.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\" or python_version >= \\\"3.4\\\" and python_version < \\\"3.5\\\"\",\n      \"enum34 (>=1.1,<2.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\"\"\n    ],\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"CliKit is a group of utilities to build beautiful and testable command line interfaces.\",\n    \"version\": \"0.2.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"93a51e8bf259c29692e51a7cbca6d664\",\n        \"sha256\": \"27316bf6382b04be8fb2f60c85d538fd2b2b03f0f1eba5c88f7d7eddbefc2778\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"clikit-0.2.4-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"93a51e8bf259c29692e51a7cbca6d664\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 85786,\n      \"upload_time\": \"2019-05-11T17:09:23\",\n      \"upload_time_iso_8601\": \"2019-05-11T17:09:23.516387Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7b/0d/bb4c8a2d0edca8c300373ed736fb4680cf73be5be2ff84544dee5f979c14/clikit-0.2.4-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"f7cdbad3508038a04561f646aae68146\",\n        \"sha256\": \"0fdd41e86e8b118a8b1e94ef2835925ada541d481c9b3b2fc635fa68713e6125\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"clikit-0.2.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f7cdbad3508038a04561f646aae68146\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 50980,\n      \"upload_time\": \"2019-05-11T17:09:25\",\n      \"upload_time_iso_8601\": \"2019-05-11T17:09:25.865051Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c5/33/14fad4c82f256b0ef60dd25d4b6d8145b463da5274fd9cd842f06af318ed/clikit-0.2.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/clikit.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bf1c55d7159f7b783967c3035898bcf4fee282a654fb70efd0fdafc6e782f8b8\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bf1c55d7159f7b783967c3035898bcf4fee282a654fb70efd0fdafc6e782f8b8\"\n      },\n      \"filename\": \"clikit-0.2.4-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"93a51e8bf259c29692e51a7cbca6d664\",\n        \"sha256\": \"27316bf6382b04be8fb2f60c85d538fd2b2b03f0f1eba5c88f7d7eddbefc2778\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 85786,\n      \"upload-time\": \"2019-05-11T17:09:23.516387Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7b/0d/bb4c8a2d0edca8c300373ed736fb4680cf73be5be2ff84544dee5f979c14/clikit-0.2.4-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"clikit-0.2.4.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"f7cdbad3508038a04561f646aae68146\",\n        \"sha256\": \"0fdd41e86e8b118a8b1e94ef2835925ada541d481c9b3b2fc635fa68713e6125\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 50980,\n      \"upload-time\": \"2019-05-11T17:09:25.865051Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c5/33/14fad4c82f256b0ef60dd25d4b6d8145b463da5274fd9cd842f06af318ed/clikit-0.2.4.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"clikit\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.2.4\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/colorama/0.3.9.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Arnon Yaari\",\n    \"author_email\": \"tartley@tartley.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Environment :: Console\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.5\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.1\",\n      \"Programming Language :: Python :: 3.2\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Topic :: Terminals\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"UNKNOWN\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/tartley/colorama\",\n    \"keywords\": \"color colour terminal text ansi windows crossplatform xplatform\",\n    \"license\": \"BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": null,\n    \"maintainer_email\": null,\n    \"name\": \"colorama\",\n    \"package_url\": \"https://pypi.org/project/colorama/\",\n    \"platform\": \"UNKNOWN\",\n    \"project_url\": \"https://pypi.org/project/colorama/\",\n    \"project_urls\": {\n      \"Download\": \"UNKNOWN\",\n      \"Homepage\": \"https://github.com/tartley/colorama\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/colorama/0.3.9/\",\n    \"requires_dist\": null,\n    \"requires_python\": null,\n    \"summary\": \"Cross-platform colored terminal text.\",\n    \"version\": \"0.3.9\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"8021c861015b5f590be41190bc3f8eed\",\n        \"sha256\": \"78a441d2e984c790526cdef1cfd8415a366979ef5b3186771a055b35886953bf\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"colorama-0.3.9-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8021c861015b5f590be41190bc3f8eed\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"2.7\",\n      \"requires_python\": null,\n      \"size\": 20181,\n      \"upload_time\": \"2017-04-27T07:12:36\",\n      \"upload_time_iso_8601\": \"2017-04-27T07:12:36.597052Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/db/c8/7dcf9dbcb22429512708fe3a547f8b6101c0d02137acbd892505aee57adf/colorama-0.3.9-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"8323a5b84fdf7ad810804e51fc256b39\",\n        \"sha256\": \"4c5a15209723ce1330a5c193465fe221098f761e9640d823a2ce7c03f983137f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"colorama-0.3.9.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8323a5b84fdf7ad810804e51fc256b39\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 25053,\n      \"upload_time\": \"2017-04-27T07:12:12\",\n      \"upload_time_iso_8601\": \"2017-04-27T07:12:12.351237Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e6/76/257b53926889e2835355d74fec73d82662100135293e17d382e2b74d1669/colorama-0.3.9.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/colorama.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"b44948f8400b680c05976397672aa7542b3bf013a46af8eff2ca8e3a7bef00d8\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"b44948f8400b680c05976397672aa7542b3bf013a46af8eff2ca8e3a7bef00d8\"\n      },\n      \"filename\": \"colorama-0.3.9-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"8021c861015b5f590be41190bc3f8eed\",\n        \"sha256\": \"78a441d2e984c790526cdef1cfd8415a366979ef5b3186771a055b35886953bf\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 20181,\n      \"upload-time\": \"2017-04-27T07:12:36.597052Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/db/c8/7dcf9dbcb22429512708fe3a547f8b6101c0d02137acbd892505aee57adf/colorama-0.3.9-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"colorama-0.3.9.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"8323a5b84fdf7ad810804e51fc256b39\",\n        \"sha256\": \"4c5a15209723ce1330a5c193465fe221098f761e9640d823a2ce7c03f983137f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 25053,\n      \"upload-time\": \"2017-04-27T07:12:12.351237Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e6/76/257b53926889e2835355d74fec73d82662100135293e17d382e2b74d1669/colorama-0.3.9.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"colorama\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.3.9\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/discord-py/2.0.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Rapptz\",\n    \"author_email\": \"\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Natural Language :: English\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Internet\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: Utilities\",\n      \"Typing :: Typed\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/Rapptz/discord.py\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"discord.py\",\n    \"package_url\": \"https://pypi.org/project/discord.py/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/discord.py/\",\n    \"project_urls\": {\n      \"Documentation\": \"https://discordpy.readthedocs.io/en/latest/\",\n      \"Homepage\": \"https://github.com/Rapptz/discord.py\",\n      \"Issue tracker\": \"https://github.com/Rapptz/discord.py/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/discord.py/2.0.0/\",\n    \"requires_dist\": [\n      \"aiohttp (<4,>=3.7.4)\",\n      \"sphinx (==4.4.0) ; extra == 'docs'\",\n      \"sphinxcontrib-trio (==1.1.2) ; extra == 'docs'\",\n      \"sphinxcontrib-websupport ; extra == 'docs'\",\n      \"typing-extensions (<5,>=4.3) ; extra == 'docs'\",\n      \"orjson (>=3.5.4) ; extra == 'speed'\",\n      \"aiodns (>=1.1) ; extra == 'speed'\",\n      \"Brotli ; extra == 'speed'\",\n      \"cchardet ; extra == 'speed'\",\n      \"coverage[toml] ; extra == 'test'\",\n      \"pytest ; extra == 'test'\",\n      \"pytest-asyncio ; extra == 'test'\",\n      \"pytest-cov ; extra == 'test'\",\n      \"pytest-mock ; extra == 'test'\",\n      \"typing-extensions (<5,>=4.3) ; extra == 'test'\",\n      \"PyNaCl (<1.6,>=1.3.0) ; extra == 'voice'\"\n    ],\n    \"requires_python\": \">=3.8.0\",\n    \"summary\": \"A Python wrapper for the Discord API\",\n    \"version\": \"2.0.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"65394fc868632423cedb6be7259db970\",\n        \"sha256\": \"25b9739ba456622655203a0925b354c0ba96ac6c740562e7c37791c2f6b594fb\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"discord.py-2.0.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"65394fc868632423cedb6be7259db970\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.8.0\",\n      \"size\": 1059049,\n      \"upload_time\": \"2022-08-18T03:47:52\",\n      \"upload_time_iso_8601\": \"2022-08-18T03:47:52.438785Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/0e/d9/7b057cab41c16144925ba4f96dab576a8ebb7b80a98d40e06bd94298eb3b/discord.py-2.0.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"6c0505a6032342b29f31f9979f37d277\",\n        \"sha256\": \"b86fa9dd562684f7a52564e6dfe0216f6c172a009c0d86b8dea8bdd6ffa6b1f4\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"discord.py-2.0.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"6c0505a6032342b29f31f9979f37d277\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.8.0\",\n      \"size\": 955054,\n      \"upload_time\": \"2022-08-18T03:47:54\",\n      \"upload_time_iso_8601\": \"2022-08-18T03:47:54.173712Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4c/73/fb89115b07588bf7a46e9eca972b89dd62b5856abd52297fe130b41d9d63/discord.py-2.0.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/discord-py.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"82a6364f64a513ebbd7108338b20ed5e84cbee20a93e3e02c2cbe2d824d1b54b\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"82a6364f64a513ebbd7108338b20ed5e84cbee20a93e3e02c2cbe2d824d1b54b\"\n      },\n      \"filename\": \"discord.py-2.0.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"65394fc868632423cedb6be7259db970\",\n        \"sha256\": \"25b9739ba456622655203a0925b354c0ba96ac6c740562e7c37791c2f6b594fb\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.8.0\",\n      \"size\": 1059049,\n      \"upload-time\": \"2022-08-18T03:47:52.438785Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/0e/d9/7b057cab41c16144925ba4f96dab576a8ebb7b80a98d40e06bd94298eb3b/discord.py-2.0.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"discord.py-2.0.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"6c0505a6032342b29f31f9979f37d277\",\n        \"sha256\": \"b86fa9dd562684f7a52564e6dfe0216f6c172a009c0d86b8dea8bdd6ffa6b1f4\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.8.0\",\n      \"size\": 955054,\n      \"upload-time\": \"2022-08-18T03:47:54.173712Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4c/73/fb89115b07588bf7a46e9eca972b89dd62b5856abd52297fe130b41d9d63/discord.py-2.0.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"discord-py\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"2.0.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/filecache/0.81.json",
    "content": "{\n  \"info\": {\n    \"author\": \"ubershmekel\",\n    \"author_email\": \"ubershmekel@gmail.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/ubershmekel/filecache\",\n    \"keywords\": \"\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"filecache\",\n    \"package_url\": \"https://pypi.org/project/filecache/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/filecache/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/ubershmekel/filecache\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/filecache/0.81/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Persistent caching decorator\",\n    \"version\": \"0.81\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"eb79f96a2addff21798ea11aa51ae15052514e9ac0ab4ab9470ddd1a0da6fd3e\",\n        \"md5\": \"0979123d410d2e411025d2e369a10179\",\n        \"sha256\": \"91ce1a42b532d0e9ad75364c13159bafc3015973d4a5a0dbf37e4b4feb194055\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"filecache-0.81-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"0979123d410d2e411025d2e369a10179\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 4449,\n      \"upload_time\": \"2020-05-29T20:07:06\",\n      \"upload_time_iso_8601\": \"2020-05-29T20:07:06.928906Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/eb/79/f96a2addff21798ea11aa51ae15052514e9ac0ab4ab9470ddd1a0da6fd3e/filecache-0.81-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"b3f5647f13b1cae32f8d3b84866f6bac688b7923c5d7643b994e5e89865c9a2a\",\n        \"md5\": \"f4c8b0e4aba2e37a4d2045a1470fa018\",\n        \"sha256\": \"be071ad64937b51f38b03ecd82b9b68c08d0f570cdddb30aa8f90150fe54b30a\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"filecache-0.81.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f4c8b0e4aba2e37a4d2045a1470fa018\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 6423,\n      \"upload_time\": \"2020-05-29T20:07:07\",\n      \"upload_time_iso_8601\": \"2020-05-29T20:07:07.751617Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b3/f5/647f13b1cae32f8d3b84866f6bac688b7923c5d7643b994e5e89865c9a2a/filecache-0.81.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/filecache.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"3fed106198f49b73a473e1322cf26f28137f215c8998209b684c20ce01971505\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"3fed106198f49b73a473e1322cf26f28137f215c8998209b684c20ce01971505\"\n      },\n      \"filename\": \"filecache-0.81-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"91ce1a42b532d0e9ad75364c13159bafc3015973d4a5a0dbf37e4b4feb194055\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4449,\n      \"upload-time\": \"2020-05-29T20:07:06.928906Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/eb/79/f96a2addff21798ea11aa51ae15052514e9ac0ab4ab9470ddd1a0da6fd3e/filecache-0.81-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"filecache-0.81.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"be071ad64937b51f38b03ecd82b9b68c08d0f570cdddb30aa8f90150fe54b30a\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 6423,\n      \"upload-time\": \"2020-05-29T20:07:07.751617Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b3/f5/647f13b1cae32f8d3b84866f6bac688b7923c5d7643b994e5e89865c9a2a/filecache-0.81.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"filecache\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.81\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/funcsigs/1.0.2.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Testing Cabal\",\n    \"author_email\": \"testing-in-python@lists.idyll.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"UNKNOWN\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://funcsigs.readthedocs.org\",\n    \"keywords\": null,\n    \"license\": \"ASL\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": null,\n    \"maintainer_email\": null,\n    \"name\": \"funcsigs\",\n    \"package_url\": \"https://pypi.org/project/funcsigs/\",\n    \"platform\": \"UNKNOWN\",\n    \"project_url\": \"https://pypi.org/project/funcsigs/\",\n    \"project_urls\": {\n      \"Download\": \"UNKNOWN\",\n      \"Homepage\": \"http://funcsigs.readthedocs.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/funcsigs/1.0.2/\",\n    \"requires_dist\": null,\n    \"requires_python\": null,\n    \"summary\": \"Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+\",\n    \"version\": \"1.0.2\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"69cbf5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328\",\n        \"md5\": \"701d58358171f34b6d1197de2923a35a\",\n        \"sha256\": \"330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"funcsigs-1.0.2-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"701d58358171f34b6d1197de2923a35a\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"2.7\",\n      \"requires_python\": null,\n      \"size\": 17697,\n      \"upload_time\": \"2016-04-25T22:22:05\",\n      \"upload_time_iso_8601\": \"2016-04-25T22:22:05.222685Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"944adb842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23\",\n        \"md5\": \"7e583285b1fb8a76305d6d68f4ccc14e\",\n        \"sha256\": \"a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"funcsigs-1.0.2.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"7e583285b1fb8a76305d6d68f4ccc14e\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 27947,\n      \"upload_time\": \"2016-04-25T22:22:33\",\n      \"upload_time_iso_8601\": \"2016-04-25T22:22:33.882246Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/funcsigs.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4288c421dc872125cc7bcfef2cbb953584a95dfc8fb21b766ac038af980559fe\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4288c421dc872125cc7bcfef2cbb953584a95dfc8fb21b766ac038af980559fe\"\n      },\n      \"filename\": \"funcsigs-1.0.2-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 17697,\n      \"upload-time\": \"2016-04-25T22:22:05.222685Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"funcsigs-1.0.2.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 27947,\n      \"upload-time\": \"2016-04-25T22:22:33.882246Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"funcsigs\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.0.2\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/futures/3.2.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Alex Grönholm\",\n    \"author_email\": \"alex.gronholm@nextday.fi\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Python Software Foundation License\",\n      \"Programming Language :: Python :: 2 :: Only\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": \"https://pythonhosted.org/futures/\",\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/agronholm/pythonfutures\",\n    \"keywords\": \"\",\n    \"license\": \"PSF\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"futures\",\n    \"package_url\": \"https://pypi.org/project/futures/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/futures/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/agronholm/pythonfutures\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/futures/3.2.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \">=2.6, <3\",\n    \"summary\": \"Backport of the concurrent.futures package from Python 3\",\n    \"version\": \"3.2.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"f81c5c27f3ba2efc008cc96363a81c5e\",\n        \"sha256\": \"41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"futures-3.2.0-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f81c5c27f3ba2efc008cc96363a81c5e\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2\",\n      \"requires_python\": \">=2.6, <3\",\n      \"size\": 15847,\n      \"upload_time\": \"2017-11-30T23:22:35\",\n      \"upload_time_iso_8601\": \"2017-11-30T23:22:35.590688Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"40eb168dab84e606df3fdb7e67fe27b7\",\n        \"sha256\": \"baf0d469c9e541b747986b7404cd63a5496955bd0c43a3cc068c449b09b7d4a4\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"futures-3.2.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"40eb168dab84e606df3fdb7e67fe27b7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.6, <3\",\n      \"size\": 27320,\n      \"upload_time\": \"2017-11-30T23:22:36\",\n      \"upload_time_iso_8601\": \"2017-11-30T23:22:36.994073Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/9e/7b2ff7e965fc654592269f2906ade1c7d705f1bf25b7d469fa153f7d19eb/futures-3.2.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/futures.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"e73888099c1e04d81a9824591e3efc584f5181ff1583e1ce2a90af6d1cc731f8\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"e73888099c1e04d81a9824591e3efc584f5181ff1583e1ce2a90af6d1cc731f8\"\n      },\n      \"filename\": \"futures-3.2.0-py2-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"f81c5c27f3ba2efc008cc96363a81c5e\",\n        \"sha256\": \"41353b36198757a766cfc82dc9b60e88ecb28e543dd92473b2cc74fc7bf205af\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.6, <3\",\n      \"size\": 15847,\n      \"upload-time\": \"2017-11-30T23:22:35.590688Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"futures-3.2.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"40eb168dab84e606df3fdb7e67fe27b7\",\n        \"sha256\": \"baf0d469c9e541b747986b7404cd63a5496955bd0c43a3cc068c449b09b7d4a4\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.6, <3\",\n      \"size\": 27320,\n      \"upload-time\": \"2017-11-30T23:22:36.994073Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/9e/7b2ff7e965fc654592269f2906ade1c7d705f1bf25b7d469fa153f7d19eb/futures-3.2.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"futures\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"3.2.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/hbmqtt/0.9.6.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Nicolas Jouanin\",\n    \"author_email\": \"nico@beerfactory.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 3 - Alpha\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: MacOS\",\n      \"Operating System :: Microsoft :: Windows\",\n      \"Operating System :: POSIX\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Topic :: Communications\",\n      \"Topic :: Internet\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/beerfactory/hbmqtt\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"hbmqtt\",\n    \"package_url\": \"https://pypi.org/project/hbmqtt/\",\n    \"platform\": \"all\",\n    \"project_url\": \"https://pypi.org/project/hbmqtt/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/beerfactory/hbmqtt\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/hbmqtt/0.9.6/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"MQTT client/broker using Python 3.4 asyncio library\",\n    \"version\": \"0.9.6\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"ee27912d1d8c307a72985edf0b6ad9fc1c32e22bfc42efbd0244902c43bd9307\",\n        \"md5\": \"e7ecbb2bf3aa2b3b5b2a47dc5289039a\",\n        \"sha256\": \"c83ba91dc5cf9a01f83afb5380701504f69bdf9ea1c071f4dfdc1cba412fcd63\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"hbmqtt-0.9.6.linux-x86_64.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"e7ecbb2bf3aa2b3b5b2a47dc5289039a\",\n      \"packagetype\": \"bdist_dumb\",\n      \"python_version\": \"any\",\n      \"requires_python\": null,\n      \"size\": 126180,\n      \"upload_time\": \"2020-01-25T14:12:53\",\n      \"upload_time_iso_8601\": \"2020-01-25T14:12:53.948778Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ee/27/912d1d8c307a72985edf0b6ad9fc1c32e22bfc42efbd0244902c43bd9307/hbmqtt-0.9.6.linux-x86_64.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"4f4b69014d0fd585b45bfdb77ef0e70bcf035fc8fc798c2a0610fd7cfae56cfd\",\n        \"md5\": \"d7896681b8d7d27b53302350b5b3c9c2\",\n        \"sha256\": \"57799a933500caadb472000ba0c1e043d4768608cd8142104f89c53930d8613c\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"hbmqtt-0.9.6-py3.8.egg\",\n      \"has_sig\": false,\n      \"md5_digest\": \"d7896681b8d7d27b53302350b5b3c9c2\",\n      \"packagetype\": \"bdist_egg\",\n      \"python_version\": \"3.8\",\n      \"requires_python\": null,\n      \"size\": 186560,\n      \"upload_time\": \"2020-01-25T14:13:44\",\n      \"upload_time_iso_8601\": \"2020-01-25T14:13:44.484402Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/4b/69014d0fd585b45bfdb77ef0e70bcf035fc8fc798c2a0610fd7cfae56cfd/hbmqtt-0.9.6-py3.8.egg\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"b284e3118882f169aa618a856cd91c5f\",\n        \"sha256\": \"379f1d9044997c69308ac2e01621c817b5394e1fbe0696e62538ae2dd0aa7e07\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"hbmqtt-0.9.6.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"b284e3118882f169aa618a856cd91c5f\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 74727,\n      \"upload_time\": \"2020-01-25T14:12:45\",\n      \"upload_time_iso_8601\": \"2020-01-25T14:12:45.640961Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b4/7c/7e1d47e740915bd628f4038083469c5919e759a638f45abab01e09e933cb/hbmqtt-0.9.6.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/hbmqtt.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"hbmqtt-0.9.6-py3.8.egg\",\n      \"hashes\": {\n        \"sha256\": \"57799a933500caadb472000ba0c1e043d4768608cd8142104f89c53930d8613c\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 186560,\n      \"upload-time\": \"2020-01-25T14:13:44.484402Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/4b/69014d0fd585b45bfdb77ef0e70bcf035fc8fc798c2a0610fd7cfae56cfd/hbmqtt-0.9.6-py3.8.egg\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"hbmqtt-0.9.6.linux-x86_64.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"c83ba91dc5cf9a01f83afb5380701504f69bdf9ea1c071f4dfdc1cba412fcd63\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 126180,\n      \"upload-time\": \"2020-01-25T14:12:53.948778Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ee/27/912d1d8c307a72985edf0b6ad9fc1c32e22bfc42efbd0244902c43bd9307/hbmqtt-0.9.6.linux-x86_64.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"hbmqtt-0.9.6.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"b284e3118882f169aa618a856cd91c5f\",\n        \"sha256\": \"379f1d9044997c69308ac2e01621c817b5394e1fbe0696e62538ae2dd0aa7e07\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 74727,\n      \"upload-time\": \"2020-01-25T14:12:45.640961Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b4/7c/7e1d47e740915bd628f4038083469c5919e759a638f45abab01e09e933cb/hbmqtt-0.9.6.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"hbmqtt\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.9.6\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/importlib-metadata/1.7.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Barry Warsaw\",\n    \"author_email\": \"barry@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 3 - Alpha\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://importlib-metadata.readthedocs.io/\",\n    \"keywords\": \"\",\n    \"license\": \"Apache Software License\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"importlib-metadata\",\n    \"package_url\": \"https://pypi.org/project/importlib-metadata/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/importlib-metadata/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://importlib-metadata.readthedocs.io/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/importlib-metadata/1.7.0/\",\n    \"requires_dist\": [\n      \"zipp (>=0.5)\",\n      \"pathlib2 ; python_version < \\\"3\\\"\",\n      \"contextlib2 ; python_version < \\\"3\\\"\",\n      \"configparser (>=3.5) ; python_version < \\\"3\\\"\",\n      \"sphinx ; extra == 'docs'\",\n      \"rst.linker ; extra == 'docs'\",\n      \"packaging ; extra == 'testing'\",\n      \"pep517 ; extra == 'testing'\",\n      \"importlib-resources (>=1.3) ; (python_version < \\\"3.9\\\") and extra == 'testing'\"\n    ],\n    \"requires_python\": \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\",\n    \"summary\": \"Read metadata from Python packages\",\n    \"version\": \"1.7.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"8e58cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929\",\n        \"md5\": \"8ae1f31228e29443c08e07501a99d1b8\",\n        \"sha256\": \"dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"importlib_metadata-1.7.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8ae1f31228e29443c08e07501a99d1b8\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\",\n      \"size\": 31809,\n      \"upload_time\": \"2020-06-26T21:38:16\",\n      \"upload_time_iso_8601\": \"2020-06-26T21:38:16.079439Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"e2ae0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38\",\n        \"md5\": \"4505ea85600cca1e693a4f8f5dd27ba8\",\n        \"sha256\": \"90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"importlib_metadata-1.7.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"4505ea85600cca1e693a4f8f5dd27ba8\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\",\n      \"size\": 29233,\n      \"upload_time\": \"2020-06-26T21:38:17\",\n      \"upload_time_iso_8601\": \"2020-06-26T21:38:17.338581Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e2/ae/0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38/importlib_metadata-1.7.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/importlib-metadata.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"d176e226c843ad287df418105af818b0545801d6827ab3e800e457913be05260\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"d176e226c843ad287df418105af818b0545801d6827ab3e800e457913be05260\"\n      },\n      \"filename\": \"importlib_metadata-1.7.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\",\n      \"size\": 31809,\n      \"upload-time\": \"2020-06-26T21:38:16.079439Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"importlib_metadata-1.7.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \"!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\",\n      \"size\": 29233,\n      \"upload-time\": \"2020-06-26T21:38:17.338581Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e2/ae/0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38/importlib_metadata-1.7.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"importlib-metadata\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.7.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/ipython/4.1.0rc1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"The IPython Development Team\",\n    \"author_email\": \"ipython-dev@scipy.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Framework :: IPython\",\n      \"Intended Audience :: Developers\",\n      \"Intended Audience :: Science/Research\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: System :: Shells\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"https://github.com/ipython/ipython/downloads\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://ipython.org\",\n    \"keywords\": \"Interactive,Interpreter,Shell,Parallel,Distributed,Web-based computing,Qt console,Embedding\",\n    \"license\": \"BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"ipython\",\n    \"package_url\": \"https://pypi.org/project/ipython/\",\n    \"platform\": \"Linux,Mac OSX,Windows XP/Vista/7/8\",\n    \"project_url\": \"https://pypi.org/project/ipython/\",\n    \"project_urls\": {\n      \"Download\": \"https://github.com/ipython/ipython/downloads\",\n      \"Homepage\": \"http://ipython.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/ipython/4.1.0rc1/\",\n    \"requires_dist\": [\n      \"pickleshare\",\n      \"setuptools (>=18.5decorator)\",\n      \"simplegeneric (>0.8)\",\n      \"traitlets\",\n      \"pexpect; sys_platform != \\\"win32\\\"\",\n      \"appnope; sys_platform == \\\"darwin\\\"\",\n      \"gnureadline; sys_platform == \\\"darwin\\\" and platform_python_implementation == \\\"CPython\\\"\",\n      \"Sphinx (>=1.3); extra == 'all'\",\n      \"ipykernel; extra == 'all'\",\n      \"ipyparallel; extra == 'all'\",\n      \"ipywidgets; extra == 'all'\",\n      \"nbconvert; extra == 'all'\",\n      \"nbformat; extra == 'all'\",\n      \"nose (>=0.10.1); extra == 'all'\",\n      \"notebook; extra == 'all'\",\n      \"qtconsole; extra == 'all'\",\n      \"requests; extra == 'all'\",\n      \"testpath; extra == 'all'\",\n      \"Sphinx (>=1.3); extra == 'doc'\",\n      \"ipykernel; extra == 'kernel'\",\n      \"nbconvert; extra == 'nbconvert'\",\n      \"nbformat; extra == 'nbformat'\",\n      \"ipywidgets; extra == 'notebook'\",\n      \"notebook; extra == 'notebook'\",\n      \"ipyparallel; extra == 'parallel'\",\n      \"qtconsole; extra == 'qtconsole'\",\n      \"pyreadline (>=2); sys_platform == \\\"win32\\\" and extra == 'terminal'\",\n      \"nose (>=0.10.1); extra == 'test'\",\n      \"requests; extra == 'test'\",\n      \"testpath; extra == 'test'\",\n      \"mock; python_version == \\\"2.7\\\" and extra == 'test'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"IPython: Productive Interactive Computing\",\n    \"version\": \"4.1.0rc1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"ac0204a5d372b4e64f9c97b2846646aec1ce4532885005aa4ba51eb20b80e17f\",\n        \"md5\": \"512f0431c850c75a12baa9f8c4a9f12f\",\n        \"sha256\": \"4d0a08f3fd8837502bf33e9497a5ab28fe63e2fa4201765f378cb139c7a60d5f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-4.1.0rc1-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"512f0431c850c75a12baa9f8c4a9f12f\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 736900,\n      \"upload_time\": \"2016-01-26T19:58:35\",\n      \"upload_time_iso_8601\": \"2016-01-26T19:58:35.544443Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ac/02/04a5d372b4e64f9c97b2846646aec1ce4532885005aa4ba51eb20b80e17f/ipython-4.1.0rc1-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"a0def71f0c8b8a26ef28cc968fbf1859729ad3e68146cd2eb4a759e9c88da218\",\n        \"md5\": \"2aff56d8e78341f64663bcbc81366376\",\n        \"sha256\": \"6244a8e3293088ee31c1854abe1a1e7a409cf3ac2fb7579aa9616bdfadd3d4dc\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-4.1.0rc1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"2aff56d8e78341f64663bcbc81366376\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 4933377,\n      \"upload_time\": \"2016-01-26T19:58:53\",\n      \"upload_time_iso_8601\": \"2016-01-26T19:58:53.938491Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/a0/de/f71f0c8b8a26ef28cc968fbf1859729ad3e68146cd2eb4a759e9c88da218/ipython-4.1.0rc1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"71f09d670266b840b8b921dc7106ecddd892f6fb893424883498e1ba3ec3a3a1\",\n        \"md5\": \"a9ff233f176dd99b076b81dc8904ab7a\",\n        \"sha256\": \"efa3a5a676648cb18e2a2d3cd6353f3c83f0f704df8eb0eb6ae7d0dcbf187ea1\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-4.1.0rc1.zip\",\n      \"has_sig\": false,\n      \"md5_digest\": \"a9ff233f176dd99b076b81dc8904ab7a\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 5100723,\n      \"upload_time\": \"2016-01-26T19:59:14\",\n      \"upload_time_iso_8601\": \"2016-01-26T19:59:14.449245Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/71/f0/9d670266b840b8b921dc7106ecddd892f6fb893424883498e1ba3ec3a3a1/ipython-4.1.0rc1.zip\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/ipython/5.7.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"The IPython Development Team\",\n    \"author_email\": \"ipython-dev@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Framework :: IPython\",\n      \"Intended Audience :: Developers\",\n      \"Intended Audience :: Science/Research\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: System :: Shells\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://ipython.org\",\n    \"keywords\": \"Interactive,Interpreter,Shell,Embedding\",\n    \"license\": \"BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"ipython\",\n    \"package_url\": \"https://pypi.org/project/ipython/\",\n    \"platform\": \"Linux\",\n    \"project_url\": \"https://pypi.org/project/ipython/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://ipython.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/ipython/5.7.0/\",\n    \"requires_dist\": [\n      \"setuptools (>=18.5)\",\n      \"decorator\",\n      \"pickleshare\",\n      \"simplegeneric (>0.8)\",\n      \"traitlets (>=4.2)\",\n      \"prompt-toolkit (<2.0.0,>=1.0.4)\",\n      \"pygments\",\n      \"backports.shutil-get-terminal-size; python_version == \\\"2.7\\\"\",\n      \"pathlib2; python_version == \\\"2.7\\\" or python_version == \\\"3.3\\\"\",\n      \"pexpect; sys_platform != \\\"win32\\\"\",\n      \"appnope; sys_platform == \\\"darwin\\\"\",\n      \"colorama; sys_platform == \\\"win32\\\"\",\n      \"win-unicode-console (>=0.5); sys_platform == \\\"win32\\\" and python_version < \\\"3.6\\\"\",\n      \"nbformat; extra == 'all'\",\n      \"ipykernel; extra == 'all'\",\n      \"pygments; extra == 'all'\",\n      \"testpath; extra == 'all'\",\n      \"notebook; extra == 'all'\",\n      \"nbconvert; extra == 'all'\",\n      \"ipyparallel; extra == 'all'\",\n      \"qtconsole; extra == 'all'\",\n      \"Sphinx (>=1.3); extra == 'all'\",\n      \"requests; extra == 'all'\",\n      \"nose (>=0.10.1); extra == 'all'\",\n      \"ipywidgets; extra == 'all'\",\n      \"Sphinx (>=1.3); extra == 'doc'\",\n      \"ipykernel; extra == 'kernel'\",\n      \"nbconvert; extra == 'nbconvert'\",\n      \"nbformat; extra == 'nbformat'\",\n      \"notebook; extra == 'notebook'\",\n      \"ipywidgets; extra == 'notebook'\",\n      \"ipyparallel; extra == 'parallel'\",\n      \"qtconsole; extra == 'qtconsole'\",\n      \"nose (>=0.10.1); extra == 'test'\",\n      \"requests; extra == 'test'\",\n      \"testpath; extra == 'test'\",\n      \"pygments; extra == 'test'\",\n      \"nbformat; extra == 'test'\",\n      \"ipykernel; extra == 'test'\",\n      \"mock; python_version == \\\"2.7\\\" and extra == 'test'\",\n      \"numpy; python_version >= \\\"3.4\\\" and extra == 'test'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"IPython: Productive Interactive Computing\",\n    \"version\": \"5.7.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"20da5e0b1f79dccb37f033a885d798d7\",\n        \"sha256\": \"4608e3e0500fe8142659d149891400fc0b9fa250051814b569457ae4688943dc\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-5.7.0-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"20da5e0b1f79dccb37f033a885d798d7\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2\",\n      \"requires_python\": null,\n      \"size\": 760413,\n      \"upload_time\": \"2018-05-10T18:56:05\",\n      \"upload_time_iso_8601\": \"2018-05-10T18:56:05.083695Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/52/19/aadde98d6bde1667d0bf431fb2d22451f880aaa373e0a241c7e7cb5815a0/ipython-5.7.0-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"2844fa01618fe27ab99ad455d605b47d\",\n        \"sha256\": \"4292c026552a77b2edc0543941516eddd6fe1a4b681a76ac40b3f585d2fca76f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-5.7.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"2844fa01618fe27ab99ad455d605b47d\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 760415,\n      \"upload_time\": \"2018-05-10T18:56:07\",\n      \"upload_time_iso_8601\": \"2018-05-10T18:56:07.665559Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c7/b6/03e0b5b0972e6161d16c4cec8d41a20372bd0634f8cb4cc0c984b8a91db6/ipython-5.7.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"01f2808ebe78ff2f28dc39be3aa635ca\",\n        \"sha256\": \"4e7fb265e0264498bd0d62c6261936a658bf3d38beb8a7b10cd2c6327c62ac2a\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-5.7.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"01f2808ebe78ff2f28dc39be3aa635ca\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 4977745,\n      \"upload_time\": \"2018-05-10T18:56:17\",\n      \"upload_time_iso_8601\": \"2018-05-10T18:56:17.132984Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3c/fd/559fead731a29eaa55cc235c8029807b2520976a937c30e9ee603f3bb566/ipython-5.7.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/ipython/7.5.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"The IPython Development Team\",\n    \"author_email\": \"ipython-dev@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Framework :: IPython\",\n      \"Intended Audience :: Developers\",\n      \"Intended Audience :: Science/Research\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Topic :: System :: Shells\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://ipython.org\",\n    \"keywords\": \"Interactive,Interpreter,Shell,Embedding\",\n    \"license\": \"BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"ipython\",\n    \"package_url\": \"https://pypi.org/project/ipython/\",\n    \"platform\": \"Linux\",\n    \"project_url\": \"https://pypi.org/project/ipython/\",\n    \"project_urls\": {\n      \"Documentation\": \"https://ipython.readthedocs.io/\",\n      \"Funding\": \"https://numfocus.org/\",\n      \"Homepage\": \"https://ipython.org\",\n      \"Source\": \"https://github.com/ipython/ipython\",\n      \"Tracker\": \"https://github.com/ipython/ipython/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/ipython/7.5.0/\",\n    \"requires_dist\": [\n      \"setuptools (>=18.5)\",\n      \"jedi (>=0.10)\",\n      \"decorator\",\n      \"pickleshare\",\n      \"traitlets (>=4.2)\",\n      \"prompt-toolkit (<2.1.0,>=2.0.0)\",\n      \"pygments\",\n      \"backcall\",\n      \"typing; python_version == \\\"3.4\\\"\",\n      \"pexpect; sys_platform != \\\"win32\\\"\",\n      \"appnope; sys_platform == \\\"darwin\\\"\",\n      \"colorama; sys_platform == \\\"win32\\\"\",\n      \"win-unicode-console (>=0.5); sys_platform == \\\"win32\\\" and python_version < \\\"3.6\\\"\",\n      \"nbconvert; extra == 'all'\",\n      \"ipywidgets; extra == 'all'\",\n      \"pygments; extra == 'all'\",\n      \"ipykernel; extra == 'all'\",\n      \"notebook; extra == 'all'\",\n      \"ipyparallel; extra == 'all'\",\n      \"requests; extra == 'all'\",\n      \"Sphinx (>=1.3); extra == 'all'\",\n      \"nbformat; extra == 'all'\",\n      \"nose (>=0.10.1); extra == 'all'\",\n      \"numpy; extra == 'all'\",\n      \"testpath; extra == 'all'\",\n      \"qtconsole; extra == 'all'\",\n      \"Sphinx (>=1.3); extra == 'doc'\",\n      \"ipykernel; extra == 'kernel'\",\n      \"nbconvert; extra == 'nbconvert'\",\n      \"nbformat; extra == 'nbformat'\",\n      \"notebook; extra == 'notebook'\",\n      \"ipywidgets; extra == 'notebook'\",\n      \"ipyparallel; extra == 'parallel'\",\n      \"qtconsole; extra == 'qtconsole'\",\n      \"nose (>=0.10.1); extra == 'test'\",\n      \"requests; extra == 'test'\",\n      \"testpath; extra == 'test'\",\n      \"pygments; extra == 'test'\",\n      \"nbformat; extra == 'test'\",\n      \"ipykernel; extra == 'test'\",\n      \"numpy; extra == 'test'\"\n    ],\n    \"requires_python\": \">=3.5\",\n    \"summary\": \"IPython: Productive Interactive Computing\",\n    \"version\": \"7.5.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"f40ea889fb7adf989760c5e7a38bd112\",\n        \"sha256\": \"1b4c76bf1e8dd9067a4f5ab4695d4c5ad81c30d7d06f7592f4c069c389e37f37\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-7.5.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f40ea889fb7adf989760c5e7a38bd112\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.5\",\n      \"size\": 770001,\n      \"upload_time\": \"2019-04-25T15:38:21\",\n      \"upload_time_iso_8601\": \"2019-04-25T15:38:21.726776Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/a9/2e/41dce4ed129057e05a555a7f9629aa2d5f81fdcd4d16568bc24b75a1d2c9/ipython-7.5.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"0e8c1d7c14f309f6cd2dfd4e48e75cb1\",\n        \"sha256\": \"cd2a17ac273fea8bf8953118a2d83bad94f592f0db3e83fff9129a1842e36dbe\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"ipython-7.5.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"0e8c1d7c14f309f6cd2dfd4e48e75cb1\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.5\",\n      \"size\": 5118610,\n      \"upload_time\": \"2019-04-25T15:38:33\",\n      \"upload_time_iso_8601\": \"2019-04-25T15:38:33.098872Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/75/74/9b0ef91c8e356c907bb12297000951acb804583b54eeaddc342c5bad4d96/ipython-7.5.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/ipython.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"16762b4f9bbc29f0aa70a7dbd69dee034a28cbdcd72c7c70f09b5bc6da762592\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"16762b4f9bbc29f0aa70a7dbd69dee034a28cbdcd72c7c70f09b5bc6da762592\"\n      },\n      \"filename\": \"ipython-4.1.0rc1-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"4d0a08f3fd8837502bf33e9497a5ab28fe63e2fa4201765f378cb139c7a60d5f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 736900,\n      \"upload-time\": \"2016-01-26T19:58:35.544443Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ac/02/04a5d372b4e64f9c97b2846646aec1ce4532885005aa4ba51eb20b80e17f/ipython-4.1.0rc1-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"ipython-4.1.0rc1.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"6244a8e3293088ee31c1854abe1a1e7a409cf3ac2fb7579aa9616bdfadd3d4dc\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4933377,\n      \"upload-time\": \"2016-01-26T19:58:53.938491Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/a0/de/f71f0c8b8a26ef28cc968fbf1859729ad3e68146cd2eb4a759e9c88da218/ipython-4.1.0rc1.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"ipython-4.1.0rc1.zip\",\n      \"hashes\": {\n        \"sha256\": \"efa3a5a676648cb18e2a2d3cd6353f3c83f0f704df8eb0eb6ae7d0dcbf187ea1\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 5100723,\n      \"upload-time\": \"2016-01-26T19:59:14.449245Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/71/f0/9d670266b840b8b921dc7106ecddd892f6fb893424883498e1ba3ec3a3a1/ipython-4.1.0rc1.zip\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"07bb0f56a1a5d60d319a01d196c2593ea972f2285e85e473131282ef64b83e59\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"07bb0f56a1a5d60d319a01d196c2593ea972f2285e85e473131282ef64b83e59\"\n      },\n      \"filename\": \"ipython-5.7.0-py2-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"20da5e0b1f79dccb37f033a885d798d7\",\n        \"sha256\": \"4608e3e0500fe8142659d149891400fc0b9fa250051814b569457ae4688943dc\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 760413,\n      \"upload-time\": \"2018-05-10T18:56:05.083695Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/52/19/aadde98d6bde1667d0bf431fb2d22451f880aaa373e0a241c7e7cb5815a0/ipython-5.7.0-py2-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"891e312c0f4969308ab703c5e4437dab51f4cac0b190e1e51045ec4a5cad2de4\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"891e312c0f4969308ab703c5e4437dab51f4cac0b190e1e51045ec4a5cad2de4\"\n      },\n      \"filename\": \"ipython-5.7.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"2844fa01618fe27ab99ad455d605b47d\",\n        \"sha256\": \"4292c026552a77b2edc0543941516eddd6fe1a4b681a76ac40b3f585d2fca76f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 760415,\n      \"upload-time\": \"2018-05-10T18:56:07.665559Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c7/b6/03e0b5b0972e6161d16c4cec8d41a20372bd0634f8cb4cc0c984b8a91db6/ipython-5.7.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"ipython-5.7.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"01f2808ebe78ff2f28dc39be3aa635ca\",\n        \"sha256\": \"4e7fb265e0264498bd0d62c6261936a658bf3d38beb8a7b10cd2c6327c62ac2a\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4977745,\n      \"upload-time\": \"2018-05-10T18:56:17.132984Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3c/fd/559fead731a29eaa55cc235c8029807b2520976a937c30e9ee603f3bb566/ipython-5.7.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"865b29decaca03261c0df6ce2a374ef5f3df4c3f9f7b3388c534a703d45a12ef\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"865b29decaca03261c0df6ce2a374ef5f3df4c3f9f7b3388c534a703d45a12ef\"\n      },\n      \"filename\": \"ipython-7.5.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"f40ea889fb7adf989760c5e7a38bd112\",\n        \"sha256\": \"1b4c76bf1e8dd9067a4f5ab4695d4c5ad81c30d7d06f7592f4c069c389e37f37\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.5\",\n      \"size\": 770001,\n      \"upload-time\": \"2019-04-25T15:38:21.726776Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/a9/2e/41dce4ed129057e05a555a7f9629aa2d5f81fdcd4d16568bc24b75a1d2c9/ipython-7.5.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"ipython-7.5.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"0e8c1d7c14f309f6cd2dfd4e48e75cb1\",\n        \"sha256\": \"cd2a17ac273fea8bf8953118a2d83bad94f592f0db3e83fff9129a1842e36dbe\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.5\",\n      \"size\": 5118610,\n      \"upload-time\": \"2019-04-25T15:38:33.098872Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/75/74/9b0ef91c8e356c907bb12297000951acb804583b54eeaddc342c5bad4d96/ipython-7.5.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"ipython\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"4.1.0rc1\",\n    \"5.7.0\",\n    \"7.5.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/isodate/0.7.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Gerhard Weis\",\n    \"author_email\": null,\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.12\",\n      \"Programming Language :: Python :: 3.13\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Internet\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst\",\n    \"docs_url\": null,\n    \"download_url\": null,\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": null,\n    \"keywords\": null,\n    \"license\": \"Copyright (c) 2021, Hugo van Kemenade and contributors Copyright (c) 2009-2018, Gerhard Weis and contributors Copyright (c) 2009, Gerhard Weis All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the <organization> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \\\"AS IS\\\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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. \",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": null,\n    \"maintainer_email\": null,\n    \"name\": \"isodate\",\n    \"package_url\": \"https://pypi.org/project/isodate/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/isodate/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/gweis/isodate/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/isodate/0.7.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": null,\n    \"summary\": \"An ISO 8601 date/time/duration parser and formatter\",\n    \"version\": \"0.7.0\",\n    \"yanked\": true,\n    \"yanked_reason\": \"fails for py2.7 but is not marked as py3 only.\"\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"1af9e3ee3f5669186356afd2dbe7ce81\",\n        \"sha256\": \"04505f97eb100b66dff1239859e6e04ab913714c453d6ab9591adbf418285847\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isodate-0.7.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1af9e3ee3f5669186356afd2dbe7ce81\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 22286,\n      \"upload_time\": \"2024-10-08T02:38:56\",\n      \"upload_time_iso_8601\": \"2024-10-08T02:38:56.092325Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8f/90/7fba16c0bbee2ea71c135bbf5a905d4f7873ec982ffbe7305b30c555eec1/isodate-0.7.0-py3-none-any.whl\",\n      \"yanked\": true,\n      \"yanked_reason\": \"fails for py2.7 but is not marked as py3 only.\"\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"5668b7b7120797f03330363000afc35a\",\n        \"sha256\": \"167c3615c0bd2e498c9bae7a1aba5863a17e52299aafd89f17a3a091187dca74\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isodate-0.7.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"5668b7b7120797f03330363000afc35a\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 29597,\n      \"upload_time\": \"2024-10-08T02:38:58\",\n      \"upload_time_iso_8601\": \"2024-10-08T02:38:58.140328Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/40/32ce777053517be3032bb2ab3bb216959071ee0c16c761879e75c34a323e/isodate-0.7.0.tar.gz\",\n      \"yanked\": true,\n      \"yanked_reason\": \"fails for py2.7 but is not marked as py3 only.\"\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/isodate.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"e7f80a7843aedda2ae25b1d351c9cbb50c16e45acf0596f7a6591e76d90049a4\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"e7f80a7843aedda2ae25b1d351c9cbb50c16e45acf0596f7a6591e76d90049a4\"\n      },\n      \"filename\": \"isodate-0.7.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"1af9e3ee3f5669186356afd2dbe7ce81\",\n        \"sha256\": \"04505f97eb100b66dff1239859e6e04ab913714c453d6ab9591adbf418285847\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 22286,\n      \"upload-time\": \"2024-10-08T02:38:56.092325Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8f/90/7fba16c0bbee2ea71c135bbf5a905d4f7873ec982ffbe7305b30c555eec1/isodate-0.7.0-py3-none-any.whl\",\n      \"yanked\": \"fails for py2.7 but is not marked as py3 only.\"\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"isodate-0.7.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"5668b7b7120797f03330363000afc35a\",\n        \"sha256\": \"167c3615c0bd2e498c9bae7a1aba5863a17e52299aafd89f17a3a091187dca74\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 29597,\n      \"upload-time\": \"2024-10-08T02:38:58.140328Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/40/32ce777053517be3032bb2ab3bb216959071ee0c16c761879e75c34a323e/isodate-0.7.0.tar.gz\",\n      \"yanked\": \"fails for py2.7 but is not marked as py3 only.\"\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"isodate\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.7.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/isort/4.3.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Timothy Crosley\",\n    \"author_email\": \"timothy.crosley@gmail.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 6 - Mature\",\n      \"Environment :: Console\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/timothycrosley/isort\",\n    \"keywords\": \"Refactor\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"isort\",\n    \"package_url\": \"https://pypi.org/project/isort/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/isort/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/timothycrosley/isort\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/isort/4.3.4/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"A Python utility / library to sort Python imports.\",\n    \"version\": \"4.3.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"42bccda292eca3c91eadf3eb781a224f\",\n        \"sha256\": \"383c39c10b5db83e8d150ac5b84d74bda96e3a1b06a30257f022dcbcd21f54b9\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"42bccda292eca3c91eadf3eb781a224f\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"2.7\",\n      \"requires_python\": null,\n      \"size\": 45393,\n      \"upload_time\": \"2018-02-12T15:06:38\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:38.441257Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/isort-4.3.4-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"6c3b582d7782633ec23917b00a97a2fe\",\n        \"sha256\": \"5668dce9fb48544c57ed626982e190c8ea99e3a612850453e9c3b193b9fa2edc\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"6c3b582d7782633ec23917b00a97a2fe\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"3.6\",\n      \"requires_python\": null,\n      \"size\": 45352,\n      \"upload_time\": \"2018-02-12T15:06:20\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:20.089641Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/2c/22eee714d7199ae0464beda6ad5fedec8fee6a2f7ffd1e8f1840928fe318/isort-4.3.4-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"9244631852cf8bd8559f7ab78bf4ec78\",\n        \"sha256\": \"234ad07e1e2780c27fa56364eefa734bee991b0d744337ef7e7ce3d5b1b59f39\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"9244631852cf8bd8559f7ab78bf4ec78\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 56070,\n      \"upload_time\": \"2018-02-12T15:06:16\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:16.498194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b1/de/a628d16fdba0d38cafb3d7e34d4830f2c9cb3881384ce5c08c44762e1846/isort-4.3.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/isort-metadata.json",
    "content": "{\n  \"name\": \"isort-metadata\",\n  \"files\": [\n    {\n      \"filename\": \"isort-metadata-4.3.4-py2-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/isort-metadata-4.3.4-py2-none-any.whl\",\n      \"core-metadata\": true,\n      \"hashes\": {\n        \"md5\": \"f0ad7704b6dc947073398ba290c3517f\",\n        \"sha256\": \"ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497\"\n      }\n    },\n    {\n      \"filename\": \"isort-metadata-4.3.4-py3-none-any.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/2c/22eee714d7199ae0464beda6ad5fedec8fee6a2f7ffd1e8f1840928fe318/isort-metadata-4.3.4-py3-none-any.whl\",\n      \"core-metadata\": true,\n      \"hashes\": {\n        \"md5\": \"fbaac4cd669ac21ea9e21ab1ea3180db\",\n        \"sha256\": \"1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af\"\n      }\n    },\n    {\n      \"filename\": \"isort-metadata-4.3.4.tar.gz\",\n      \"url\": \"https://files.pythonhosted.org/packages/b1/de/a628d16fdba0d38cafb3d7e34d4830f2c9cb3881384ce5c08c44762e1846/isort-metadata-4.3.4.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"fb554e9c8f9aa76e333a03d470a5cf52\",\n        \"sha256\": \"b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"api-version\": \"1.0\",\n    \"_last-serial\": 3575149\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/isort.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4a736288a055765f102d09f6b8bac7d5a2ba13a82cc9e46e80c3f65ef2497d3c\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4a736288a055765f102d09f6b8bac7d5a2ba13a82cc9e46e80c3f65ef2497d3c\"\n      },\n      \"filename\": \"isort-4.3.4-py2-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"42bccda292eca3c91eadf3eb781a224f\",\n        \"sha256\": \"383c39c10b5db83e8d150ac5b84d74bda96e3a1b06a30257f022dcbcd21f54b9\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 45393,\n      \"upload-time\": \"2018-02-12T15:06:38.441257Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/isort-4.3.4-py2-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bd3a28432bb89f731dc82c08c1ee1497c874e85eb8290371547db13d3adc5a34\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bd3a28432bb89f731dc82c08c1ee1497c874e85eb8290371547db13d3adc5a34\"\n      },\n      \"filename\": \"isort-4.3.4-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"6c3b582d7782633ec23917b00a97a2fe\",\n        \"sha256\": \"5668dce9fb48544c57ed626982e190c8ea99e3a612850453e9c3b193b9fa2edc\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 45352,\n      \"upload-time\": \"2018-02-12T15:06:20.089641Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/2c/22eee714d7199ae0464beda6ad5fedec8fee6a2f7ffd1e8f1840928fe318/isort-4.3.4-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"isort-4.3.4.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"9244631852cf8bd8559f7ab78bf4ec78\",\n        \"sha256\": \"234ad07e1e2780c27fa56364eefa734bee991b0d744337ef7e7ce3d5b1b59f39\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 56070,\n      \"upload-time\": \"2018-02-12T15:06:16.498194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b1/de/a628d16fdba0d38cafb3d7e34d4830f2c9cb3881384ce5c08c44762e1846/isort-4.3.4.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"isort\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"4.3.4\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/jupyter/1.0.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Jupyter Development Team\",\n    \"author_email\": \"jupyter@googlegroups.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"Intended Audience :: Science/Research\",\n      \"Intended Audience :: System Administrators\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"UNKNOWN\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://jupyter.org\",\n    \"keywords\": null,\n    \"license\": \"BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": null,\n    \"maintainer_email\": null,\n    \"name\": \"jupyter\",\n    \"package_url\": \"https://pypi.org/project/jupyter/\",\n    \"platform\": \"UNKNOWN\",\n    \"project_url\": \"https://pypi.org/project/jupyter/\",\n    \"project_urls\": {\n      \"Download\": \"UNKNOWN\",\n      \"Homepage\": \"http://jupyter.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/jupyter/1.0.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": null,\n    \"summary\": \"Jupyter metapackage. Install all the Jupyter components in one go.\",\n    \"version\": \"1.0.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"27f411f164e0878104d76d868127f76f\",\n        \"sha256\": \"1de1f2be45629dd6f7f9558e2385ddf6901849699ef1044c52d171a9b520a420\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"jupyter-1.0.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"27f411f164e0878104d76d868127f76f\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"3.4\",\n      \"requires_python\": null,\n      \"size\": 2736,\n      \"upload_time\": \"2015-08-12T00:42:58\",\n      \"upload_time_iso_8601\": \"2015-08-12T00:42:58.951595Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/83/df/0f5dd132200728a86190397e1ea87cd76244e42d39ec5e88efd25b2abd7e/jupyter-1.0.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"78acaec88533ea6b6e761e7d086a1d04\",\n        \"sha256\": \"3ef1e86ba0556ea5922b846416a41acfd2625830d996c7d06d80c90bed1dc193\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"jupyter-1.0.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"78acaec88533ea6b6e761e7d086a1d04\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 12916,\n      \"upload_time\": \"2015-08-12T00:43:08\",\n      \"upload_time_iso_8601\": \"2015-08-12T00:43:08.537857Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"7b7a957694a73ac0c19fe46c216c0ea0\",\n        \"sha256\": \"4a855b9717c3ea24fd8ca4fd91ab5995894aecc4d20e7f39c28786a2c1869fae\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"jupyter-1.0.0.zip\",\n      \"has_sig\": false,\n      \"md5_digest\": \"7b7a957694a73ac0c19fe46c216c0ea0\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 16690,\n      \"upload_time\": \"2015-08-12T00:43:12\",\n      \"upload_time_iso_8601\": \"2015-08-12T00:43:12.460314Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fc/21/a372b73e3a498b41b92ed915ada7de2ad5e16631546329c03e484c3bf4e9/jupyter-1.0.0.zip\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/jupyter.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"89666fe631d5e97add0c96b87457f91671b0ce522e4f55d88886507984a5f743\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"89666fe631d5e97add0c96b87457f91671b0ce522e4f55d88886507984a5f743\"\n      },\n      \"filename\": \"jupyter-1.0.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"27f411f164e0878104d76d868127f76f\",\n        \"sha256\": \"1de1f2be45629dd6f7f9558e2385ddf6901849699ef1044c52d171a9b520a420\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 2736,\n      \"upload-time\": \"2015-08-12T00:42:58.951595Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/83/df/0f5dd132200728a86190397e1ea87cd76244e42d39ec5e88efd25b2abd7e/jupyter-1.0.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"jupyter-1.0.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"78acaec88533ea6b6e761e7d086a1d04\",\n        \"sha256\": \"3ef1e86ba0556ea5922b846416a41acfd2625830d996c7d06d80c90bed1dc193\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 12916,\n      \"upload-time\": \"2015-08-12T00:43:08.537857Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"jupyter-1.0.0.zip\",\n      \"hashes\": {\n        \"md5\": \"7b7a957694a73ac0c19fe46c216c0ea0\",\n        \"sha256\": \"4a855b9717c3ea24fd8ca4fd91ab5995894aecc4d20e7f39c28786a2c1869fae\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 16690,\n      \"upload-time\": \"2015-08-12T00:43:12.460314Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fc/21/a372b73e3a498b41b92ed915ada7de2ad5e16631546329c03e484c3bf4e9/jupyter-1.0.0.zip\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"jupyter\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.0.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/invalid-version-package.json",
    "content": "{\n  \"name\": \"invalid-version-package\",\n  \"files\": [\n    {\n      \"filename\": \"invalid_version_package-.9-cp27-cp27m-win32.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/12/9b/efdbaa3c9694b6315a4410e0d494ad50c5ade22ce33f4b482bfaea3930fd/invalid_version_package-.9-cp27-cp27m-win32.whl\",\n      \"hashes\": {\n        \"md5\": \"76e2c2e8adea20377d9a7e6b6713c952\",\n        \"sha256\": \"8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b\"\n      }\n    },\n    {\n      \"filename\": \"invalid_version_package-3.13-cp27-cp27m-win32.whl\",\n      \"url\": \"https://files.pythonhosted.org/packages/b8/2e/9c2285870c9de070a1fa5ede702ab5fb329901b3cc4028c24f44eda27c5f/invalid_version_package-3.13-cp27-cp27m-win32.whl\",\n      \"hashes\": {\n        \"md5\": \"a83441aa7004e474bed6f6daeb61f27a\",\n        \"sha256\": \"d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"api-version\": \"1.0\",\n    \"_last-serial\": 0\n  }\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/isort-metadata/4.3.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Timothy Crosley\",\n    \"author_email\": \"timothy.crosley@gmail.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 6 - Mature\",\n      \"Environment :: Console\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \".. image:: https://raw.github.com/timothycrosley/isort/master/logo.png\\n    :alt: isort\\n\\n########\\n\\n.. image:: https://badge.fury.io/py/isort.svg\\n    :target: https://badge.fury.io/py/isort\\n    :alt: PyPI version\\n\\n.. image:: https://travis-ci.org/timothycrosley/isort.svg?branch=master\\n    :target: https://travis-ci.org/timothycrosley/isort\\n    :alt: Build Status\\n\\n\\n.. image:: https://coveralls.io/repos/timothycrosley/isort/badge.svg?branch=release%2F2.6.0&service=github\\n  :target: https://coveralls.io/github/timothycrosley/isort?branch=release%2F2.6.0\\n  :alt: Coverage\\n\\n.. image:: https://img.shields.io/github/license/mashape/apistatus.svg\\n    :target: https://pypi.python.org/pypi/hug/\\n    :alt: License\\n\\n.. image:: https://badges.gitter.im/Join%20Chat.svg\\n   :alt: Join the chat at https://gitter.im/timothycrosley/isort\\n   :target: https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\\n\\n\\nisort your python imports for you so you don't have to.\\n\\nisort is a Python utility / library to sort imports alphabetically, and automatically separated into sections.\\nIt provides a command line utility, Python library and `plugins for various editors <https://github.com/timothycrosley/isort/wiki/isort-Plugins>`_ to quickly sort all your imports.\\nIt currently cleanly supports Python 2.7 - 3.6 without any dependencies.\\n\\n.. image:: https://raw.github.com/timothycrosley/isort/develop/example.gif\\n   :alt: Example Usage\\n\\nBefore isort:\\n\\n.. code-block:: python\\n\\n    from my_lib import Object\\n\\n    print(\\\"Hey\\\")\\n\\n    import os\\n\\n    from my_lib import Object3\\n\\n    from my_lib import Object2\\n\\n    import sys\\n\\n    from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14\\n\\n    import sys\\n\\n    from __future__ import absolute_import\\n\\n    from third_party import lib3\\n\\n    print(\\\"yo\\\")\\n\\nAfter isort:\\n\\n.. code-block:: python\\n\\n    from __future__ import absolute_import\\n\\n    import os\\n    import sys\\n\\n    from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8,\\n                             lib9, lib10, lib11, lib12, lib13, lib14, lib15)\\n\\n    from my_lib import Object, Object2, Object3\\n\\n    print(\\\"Hey\\\")\\n    print(\\\"yo\\\")\\n\\nInstalling isort\\n================\\n\\nInstalling isort is as simple as:\\n\\n.. code-block:: bash\\n\\n    pip install isort\\n\\nor if you prefer\\n\\n.. code-block:: bash\\n\\n    easy_install isort\\n\\nUsing isort\\n===========\\n\\n**From the command line**:\\n\\n.. code-block:: bash\\n\\n    isort mypythonfile.py mypythonfile2.py\\n\\nor recursively:\\n\\n.. code-block:: bash\\n\\n    isort -rc .\\n\\n*which is equivalent to:*\\n\\n.. code-block:: bash\\n\\n    isort **/*.py\\n\\nor to see the proposed changes without applying them:\\n\\n.. code-block:: bash\\n\\n    isort mypythonfile.py --diff\\n\\nFinally, to atomically run isort against a project, only applying changes if they don't introduce syntax errors do:\\n\\n.. code-block:: bash\\n\\n    isort -rc --atomic .\\n\\n(Note: this is disabled by default as it keeps isort from being able to run against code written using a different version of Python)\\n\\n**From within Python**:\\n\\n.. code-block:: bash\\n\\n    from isort import SortImports\\n\\n    SortImports(\\\"pythonfile.py\\\")\\n\\nor:\\n\\n.. code-block:: bash\\n\\n    from isort import SortImports\\n\\n    new_contents = SortImports(file_contents=old_contents).output\\n\\n**From within Kate:**\\n\\n.. code-block:: bash\\n\\n    ctrl+[\\n\\nor:\\n\\n.. code-block:: bash\\n\\n    menu > Python > Sort Imports\\n\\nInstalling isort's Kate plugin\\n==============================\\n\\nFor KDE 4.13+ / Pate 2.0+:\\n\\n.. code-block:: bash\\n\\n    wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py\\n    wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_ui.rc --output-document ~/.kde/share/apps/kate/pate/isort_plugin_ui.rc\\n    wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/katepart_isort.desktop --output-document ~/.kde/share/kde4/services/katepart_isort.desktop\\n\\nFor all older versions:\\n\\n.. code-block:: bash\\n\\n    wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_old.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py\\n\\nYou will then need to restart kate and enable Python Plugins as well as the isort plugin itself.\\n\\nInstalling isort's for your preferred text editor\\n=================================================\\n\\nSeveral plugins have been written that enable to use isort from within a variety of text-editors.\\nYou can find a full list of them `on the isort wiki <https://github.com/timothycrosley/isort/wiki/isort-Plugins>`_.\\nAdditionally, I will enthusiastically accept pull requests that include plugins for other text editors\\nand add documentation for them as I am notified.\\n\\nHow does isort work?\\n====================\\n\\nisort parses specified files for global level import lines (imports outside of try / except blocks, functions, etc..)\\nand puts them all at the top of the file grouped together by the type of import:\\n\\n- Future\\n- Python Standard Library\\n- Third Party\\n- Current Python Project\\n- Explicitly Local (. before import, as in: ``from . import x``)\\n- Custom Separate Sections (Defined by forced_separate list in configuration file)\\n- Custom Sections (Defined by sections list in configuration file)\\n\\nInside of each section the imports are sorted alphabetically. isort automatically removes duplicate python imports,\\nand wraps long from imports to the specified line length (defaults to 80).\\n\\nWhen will isort not work?\\n=========================\\n\\nIf you ever have the situation where you need to have a try / except block in the middle of top-level imports or if\\nyour import order is directly linked to precedence.\\n\\nFor example: a common practice in Django settings files is importing * from various settings files to form\\na new settings file. In this case if any of the imports change order you are changing the settings definition itself.\\n\\nHowever, you can configure isort to skip over just these files - or even to force certain imports to the top.\\n\\nConfiguring isort\\n=================\\n\\nIf you find the default isort settings do not work well for your project, isort provides several ways to adjust\\nthe behavior.\\n\\nTo configure isort for a single user create a ``~/.isort.cfg`` file:\\n\\n.. code-block:: ini\\n\\n    [settings]\\n    line_length=120\\n    force_to_top=file1.py,file2.py\\n    skip=file3.py,file4.py\\n    known_future_library=future,pies\\n    known_standard_library=std,std2\\n    known_third_party=randomthirdparty\\n    known_first_party=mylib1,mylib2\\n    indent='    '\\n    multi_line_output=3\\n    length_sort=1\\n    forced_separate=django.contrib,django.utils\\n    default_section=FIRSTPARTY\\n    no_lines_before=LOCALFOLDER\\n\\nAdditionally, you can specify project level configuration simply by placing a ``.isort.cfg`` file at the root of your\\nproject. isort will look up to 25 directories up, from the file it is ran against, to find a project specific configuration.\\n\\nOr, if you prefer, you can add an isort section to your project's ``setup.cfg`` or ``tox.ini`` file with any desired settings.\\n\\nYou can then override any of these settings by using command line arguments, or by passing in override values to the\\nSortImports class.\\n\\nFinally, as of version 3.0 isort supports editorconfig files using the standard syntax defined here:\\nhttp://editorconfig.org/\\n\\nMeaning you place any standard isort configuration parameters within a .editorconfig file under the ``*.py`` section\\nand they will be honored.\\n\\nFor a full list of isort settings and their meanings `take a look at the isort wiki <https://github.com/timothycrosley/isort/wiki/isort-Settings>`_.\\n\\nMulti line output modes\\n=======================\\n\\nYou will notice above the \\\"multi_line_output\\\" setting. This setting defines how from imports wrap when they extend\\npast the line_length limit and has 6 possible settings:\\n\\n**0 - Grid**\\n\\n.. code-block:: python\\n\\n    from third_party import (lib1, lib2, lib3,\\n                             lib4, lib5, ...)\\n\\n**1 - Vertical**\\n\\n.. code-block:: python\\n\\n    from third_party import (lib1,\\n                             lib2,\\n                             lib3\\n                             lib4,\\n                             lib5,\\n                             ...)\\n\\n**2 - Hanging Indent**\\n\\n.. code-block:: python\\n\\n    from third_party import \\\\\\n        lib1, lib2, lib3, \\\\\\n        lib4, lib5, lib6\\n\\n**3 - Vertical Hanging Indent**\\n\\n.. code-block:: python\\n\\n    from third_party import (\\n        lib1,\\n        lib2,\\n        lib3,\\n        lib4,\\n    )\\n\\n**4 - Hanging Grid**\\n\\n.. code-block:: python\\n\\n    from third_party import (\\n        lib1, lib2, lib3, lib4,\\n        lib5, ...)\\n\\n**5 - Hanging Grid Grouped**\\n\\n.. code-block:: python\\n\\n    from third_party import (\\n        lib1, lib2, lib3, lib4,\\n        lib5, ...\\n    )\\n\\n**6 - NOQA**\\n\\n.. code-block:: python\\n\\n    from third_party import lib1, lib2, lib3, ...  # NOQA\\n\\nAlternatively, you can set ``force_single_line`` to ``True`` (``-sl`` on the command line) and every import will appear on its\\nown line:\\n\\n.. code-block:: python\\n\\n    from third_party import lib1\\n    from third_party import lib2\\n    from third_party import lib3\\n    ...\\n\\nNote: to change the how constant indents appear - simply change the indent property with the following accepted formats:\\n*   Number of spaces you would like. For example: 4 would cause standard 4 space indentation.\\n*   Tab\\n*   A verbatim string with quotes around it.\\n\\nFor example:\\n\\n.. code-block:: python\\n\\n    \\\"    \\\"\\n\\nis equivalent to 4.\\n\\nFor the import styles that use parentheses, you can control whether or not to\\ninclude a trailing comma after the last import with the ``include_trailing_comma``\\noption (defaults to ``False``).\\n\\nIntelligently Balanced Multi-line Imports\\n=========================================\\n\\nAs of isort 3.1.0 support for balanced multi-line imports has been added.\\nWith this enabled isort will dynamically change the import length to the one that produces the most balanced grid,\\nwhile staying below the maximum import length defined.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n    from __future__ import (absolute_import, division,\\n                            print_function, unicode_literals)\\n\\nWill be produced instead of:\\n\\n.. code-block:: python\\n\\n    from __future__ import (absolute_import, division, print_function,\\n                            unicode_literals)\\n\\nTo enable this set ``balanced_wrapping`` to ``True`` in your config or pass the ``-e`` option into the command line utility.\\n\\nCustom Sections and Ordering\\n============================\\n\\nYou can change the section order with ``sections`` option from the default of:\\n\\n.. code-block:: ini\\n\\n    FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\\n\\nto your preference:\\n\\n.. code-block:: ini\\n\\n    sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER\\n\\nYou also can define your own sections and their order.\\n\\nExample:\\n\\n.. code-block:: ini\\n\\n    known_django=django\\n    known_pandas=pandas,numpy\\n    sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER\\n\\nwould create two new sections with the specified known modules.\\n\\nThe ``no_lines_before`` option will prevent the listed sections from being split from the previous section by an empty line.\\n\\nExample:\\n\\n.. code-block:: ini\\n\\n   sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\\n   no_lines_before=LOCALFOLDER\\n\\nwould produce a section with both FIRSTPARTY and LOCALFOLDER modules combined.\\n\\nAuto-comment import sections\\n============================\\n\\nSome projects prefer to have import sections uniquely titled to aid in identifying the sections quickly\\nwhen visually scanning. isort can automate this as well. To do this simply set the ``import_heading_{section_name}``\\nsetting for each section you wish to have auto commented - to the desired comment.\\n\\nFor Example:\\n\\n.. code-block:: ini\\n\\n    import_heading_stdlib=Standard Library\\n    import_heading_firstparty=My Stuff\\n\\nWould lead to output looking like the following:\\n\\n.. code-block:: python\\n\\n    # Standard Library\\n    import os\\n    import sys\\n\\n    import django.settings\\n\\n    # My Stuff\\n    import myproject.test\\n\\nOrdering by import length\\n=========================\\n\\nisort also makes it easy to sort your imports by length, simply by setting the ``length_sort`` option to ``True``.\\nThis will result in the following output style:\\n\\n.. code-block:: python\\n\\n    from evn.util import (\\n        Pool,\\n        Dict,\\n        Options,\\n        Constant,\\n        DecayDict,\\n        UnexpectedCodePath,\\n    )\\n\\nSkip processing of imports (outside of configuration)\\n=====================================================\\n\\nTo make isort ignore a single import simply add a comment at the end of the import line containing the text ``isort:skip``:\\n\\n.. code-block:: python\\n\\n    import module  # isort:skip\\n\\nor:\\n\\n.. code-block:: python\\n\\n    from xyz import (abc,  # isort:skip\\n                     yo,\\n                     hey)\\n\\nTo make isort skip an entire file simply add ``isort:skip_file`` to the module's doc string:\\n\\n.. code-block:: python\\n\\n    \\\"\\\"\\\" my_module.py\\n        Best module ever\\n\\n       isort:skip_file\\n    \\\"\\\"\\\"\\n\\n    import b\\n    import a\\n\\nAdding an import to multiple files\\n==================================\\n\\nisort makes it easy to add an import statement across multiple files, while being assured it's correctly placed.\\n\\nFrom the command line:\\n\\n.. code-block:: bash\\n\\n    isort -a \\\"from __future__ import print_function\\\" *.py\\n\\nfrom within Kate:\\n\\n.. code-block::\\n\\n    ctrl+]\\n\\nor:\\n\\n.. code-block::\\n\\n    menu > Python > Add Import\\n\\nRemoving an import from multiple files\\n======================================\\n\\nisort also makes it easy to remove an import from multiple files, without having to be concerned with how it was originally\\nformatted.\\n\\nFrom the command line:\\n\\n.. code-block:: bash\\n\\n    isort -r \\\"os.system\\\" *.py\\n\\nfrom within Kate:\\n\\n.. code-block::\\n\\n    ctrl+shift+]\\n\\nor:\\n\\n.. code-block::\\n\\n    menu > Python > Remove Import\\n\\nUsing isort to verify code\\n==========================\\n\\nThe ``--check-only`` option\\n---------------------------\\n\\nisort can also be used to used to verify that code is correctly formatted by running it with ``-c``.\\nAny files that contain incorrectly sorted and/or formatted imports will be outputted to ``stderr``.\\n\\n.. code-block:: bash\\n\\n    isort **/*.py -c -vb\\n\\n    SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good!\\n    ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted.\\n\\nOne great place this can be used is with a pre-commit git hook, such as this one by @acdha:\\n\\nhttps://gist.github.com/acdha/8717683\\n\\nThis can help to ensure a certain level of code quality throughout a project.\\n\\n\\nGit hook\\n--------\\n\\nisort provides a hook function that can be integrated into your Git pre-commit script to check\\nPython code before committing.\\n\\nTo cause the commit to fail if there are isort errors (strict mode), include the following in\\n``.git/hooks/pre-commit``:\\n\\n.. code-block:: python\\n\\n    #!/usr/bin/env python\\n    import sys\\n    from isort.hooks import git_hook\\n\\n    sys.exit(git_hook(strict=True))\\n\\nIf you just want to display warnings, but allow the commit to happen anyway, call ``git_hook`` without\\nthe `strict` parameter.\\n\\nSetuptools integration\\n----------------------\\n\\nUpon installation, isort enables a ``setuptools`` command that checks Python files\\ndeclared by your project.\\n\\nRunning ``python setup.py isort`` on the command line will check the files\\nlisted in your ``py_modules`` and ``packages``.  If any warning is found,\\nthe command will exit with an error code:\\n\\n.. code-block:: bash\\n\\n    $ python setup.py isort\\n\\nAlso, to allow users to be able to use the command without having to install\\nisort themselves, add isort to the setup_requires of your ``setup()`` like so:\\n\\n.. code-block:: python\\n\\n    setup(\\n        name=\\\"project\\\",\\n        packages=[\\\"project\\\"],\\n\\n        setup_requires=[\\n            \\\"isort\\\"\\n        ]\\n    )\\n\\n\\nWhy isort?\\n==========\\n\\nisort simply stands for import sort. It was originally called \\\"sortImports\\\" however I got tired of typing the extra\\ncharacters and came to the realization camelCase is not pythonic.\\n\\nI wrote isort because in an organization I used to work in the manager came in one day and decided all code must\\nhave alphabetically sorted imports. The code base was huge - and he meant for us to do it by hand. However, being a\\nprogrammer - I'm too lazy to spend 8 hours mindlessly performing a function, but not too lazy to spend 16\\nhours automating it. I was given permission to open source sortImports and here we are :)\\n\\n--------------------------------------------\\n\\nThanks and I hope you find isort useful!\\n\\n~Timothy Crosley\\n\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"home_page\": \"https://github.com/timothycrosley/isort\",\n    \"keywords\": \"Refactor\",\n    \"license\": \"MIT\",\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"isort\",\n    \"package_url\": \"https://pypi.org/project/isort/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/isort/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/timothycrosley/isort\"\n    },\n    \"release_url\": \"https://pypi.org/project/isort/4.3.4/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"A Python utility / library to sort Python imports.\",\n    \"version\": \"4.3.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 11968646,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"f0ad7704b6dc947073398ba290c3517f\",\n        \"sha256\": \"ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f0ad7704b6dc947073398ba290c3517f\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"2.7\",\n      \"requires_python\": null,\n      \"size\": 45393,\n      \"upload_time\": \"2018-02-12T15:06:38\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:38.441257Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/41/d8/a945da414f2adc1d9e2f7d6e7445b27f2be42766879062a2e63616ad4199/isort-4.3.4-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"fbaac4cd669ac21ea9e21ab1ea3180db\",\n        \"sha256\": \"1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"fbaac4cd669ac21ea9e21ab1ea3180db\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"3.6\",\n      \"requires_python\": null,\n      \"size\": 45352,\n      \"upload_time\": \"2018-02-12T15:06:20\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:20.089641Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1f/2c/22eee714d7199ae0464beda6ad5fedec8fee6a2f7ffd1e8f1840928fe318/isort-4.3.4-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"fb554e9c8f9aa76e333a03d470a5cf52\",\n        \"sha256\": \"b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"isort-4.3.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"fb554e9c8f9aa76e333a03d470a5cf52\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 56070,\n      \"upload_time\": \"2018-02-12T15:06:16\",\n      \"upload_time_iso_8601\": \"2018-02-12T15:06:16.498194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b1/de/a628d16fdba0d38cafb3d7e34d4830f2c9cb3881384ce5c08c44762e1846/isort-4.3.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/six-unknown-version/1.11.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Benjamin Peterson\",\n    \"author_email\": \"benjamin@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pypi.python.org/pypi/six/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"six-unknown-version\",\n    \"package_url\": \"https://pypi.org/project/six/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/six/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pypi.python.org/pypi/six/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/six/1.11.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Python 2 and 3 compatibility utilities\",\n    \"version\": \"1.11.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"b126a063c665f4c66f23bfd8dc6ebff5\",\n        \"sha256\": \"1d9f63b87ee9f0aee49e9d9f8d7883319836bc43f17321b488bc38933827d2c0\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"six_unknown_version-1.11.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"25d3568604f921dd23532b88a0ce17e7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 29860,\n      \"upload_time\": \"2017-09-17T18:46:54\",\n      \"upload_time_iso_8601\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six_unknown_version-1.11.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/six-unknown-version.json",
    "content": "{\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"six_unknown_version-1.11.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"b126a063c665f4c66f23bfd8dc6ebff5\",\n        \"sha256\": \"1d9f63b87ee9f0aee49e9d9f8d7883319836bc43f17321b488bc38933827d2c0\"\n      },\n      \"requires-python\": null,\n      \"size\": 29860,\n      \"upload-time\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six_unknown_version-1.11.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"six_unknown_version-unknown.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"25d3568604f921dd23532b88a0ce17e7\",\n        \"sha256\": \"268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d\"\n      },\n      \"requires-python\": null,\n      \"size\": 29860,\n      \"upload-time\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six_unknown_version-unknown.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.1\"\n  },\n  \"name\": \"six-unknown-version\",\n  \"versions\": [\n    \"1.11.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/with-extra-dependency/0.12.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Benjamin Peterson\",\n    \"author_email\": \"benjamin@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pypi.python.org/pypi/six/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"with-extra-dependency\",\n    \"package_url\": \"https://pypi.org/project/six/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/six/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pypi.python.org/pypi/six/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/six/0.12.4/\",\n    \"requires_dist\": [\n      \"filecache; extra == 'filecache'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python 2 and 3 compatibility utilities\",\n    \"version\": \"0.12.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"b126a063c665f4c66f23bfd8dc6ebff5\",\n        \"sha256\": \"1d9f63b87ee9f0aee49e9d9f8d7883319836bc43f17321b488bc38933827d2c0\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"with_extra_dependency-0.12.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"25d3568604f921dd23532b88a0ce17e7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 29860,\n      \"upload_time\": \"2017-09-17T18:46:54\",\n      \"upload_time_iso_8601\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/with_extra_dependency-0.12.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/with-extra-dependency.json",
    "content": "{\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"e98cf244e0e09b017a43e1f03ac551c6b04755176e7c06ce52d6ceddc25cfad2\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"e98cf244e0e09b017a43e1f03ac551c6b04755176e7c06ce52d6ceddc25cfad2\"\n      },\n      \"filename\": \"with_extra_dependency-0.12.4-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"632fcf45cc28aed4a4dce1324d1bd1d1\",\n        \"sha256\": \"d88b34efa115392ee42c55d6f82cdf5e5e08221ef2e18a16ae696a80008c3499\"\n      },\n      \"requires-python\": null,\n      \"size\": 6718,\n      \"upload-time\": \"2017-02-01T23:26:20.148511Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/7e/7d701686013c0d7dae62e0977467232a6adc2e562c23878eb3cd4f97d02e/with_extra_dependency-0.12.4-py3-none-any.whl\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.1\"\n  },\n  \"name\": \"with-extra-dependency\",\n  \"versions\": [\n    \"0.12.4\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/with-transitive-extra-dependency/0.12.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Benjamin Peterson\",\n    \"author_email\": \"benjamin@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pypi.python.org/pypi/six/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"with-transitive-extra-dependency\",\n    \"package_url\": \"https://pypi.org/project/six/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/six/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pypi.python.org/pypi/six/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/six/0.12.4/\",\n    \"requires_dist\": [\n      \"with-extra-dependency[filecache] (>=0.12.4,<0.13.0)\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python 2 and 3 compatibility utilities\",\n    \"version\": \"0.12.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"b126a063c665f4c66f23bfd8dc6ebff5\",\n        \"sha256\": \"1d9f63b87ee9f0aee49e9d9f8d7883319836bc43f17321b488bc38933827d2c0\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"with_transitive_extra_dependency-0.12.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"25d3568604f921dd23532b88a0ce17e7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 29860,\n      \"upload_time\": \"2017-09-17T18:46:54\",\n      \"upload_time_iso_8601\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/with_transitive_extra_dependency-0.12.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/mocked/with-transitive-extra-dependency.json",
    "content": "{\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"2e0d683c7b4e771e915184e69ed28e4ce7c239ca89b9025f7e74d63b37c656b6\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"2e0d683c7b4e771e915184e69ed28e4ce7c239ca89b9025f7e74d63b37c656b6\"\n      },\n      \"filename\": \"with_transitive_extra_dependency-0.12.4-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"632fcf45cc28aed4a4dce1324d1bd1d1\",\n        \"sha256\": \"d88b34efa115392ee42c55d6f82cdf5e5e08221ef2e18a16ae696a80008c3499\"\n      },\n      \"requires-python\": null,\n      \"size\": 6718,\n      \"upload-time\": \"2017-02-01T23:26:20.148511Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/7e/7d701686013c0d7dae62e0977467232a6adc2e562c23878eb3cd4f97d02e/with_transitive_extra_dependency-0.12.4-py3-none-any.whl\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.1\"\n  },\n  \"name\": \"with-transitive-extra-dependency\",\n  \"versions\": [\n    \"0.12.4\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/more-itertools/4.1.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Erik Rose\",\n    \"author_email\": \"erikrose@grinchcentral.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.2\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Topic :: Software Development :: Libraries\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": \"https://pythonhosted.org/more-itertools/\",\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/erikrose/more-itertools\",\n    \"keywords\": \"itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"more-itertools\",\n    \"package_url\": \"https://pypi.org/project/more-itertools/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/more-itertools/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/erikrose/more-itertools\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/more-itertools/4.1.0/\",\n    \"requires_dist\": [\n      \"six (<2.0.0,>=1.0.0)\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"More routines for operating on iterables, beyond itertools\",\n    \"version\": \"4.1.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"703e1e0922de1f11823da60af1488b7a\",\n        \"sha256\": \"0f461c2cd4ec16611396f9ee57f40433de3d59e95475d84c0c829cde02f746cd\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"more_itertools-4.1.0-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"703e1e0922de1f11823da60af1488b7a\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2\",\n      \"requires_python\": null,\n      \"size\": 47987,\n      \"upload_time\": \"2018-01-21T15:34:19\",\n      \"upload_time_iso_8601\": \"2018-01-21T15:34:19.304356Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4a/88/c28e2a2da8f3dc3a391d9c97ad949f2ea0c05198222e7e6af176e5bf9b26/more_itertools-4.1.0-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"ae17a45d13e9dc319794c40fa739c38f\",\n        \"sha256\": \"580b6002d1f28feb5bcb8303278d59cf17dfbd19a63a5c2375112dae72c9bf98\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"more_itertools-4.1.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"ae17a45d13e9dc319794c40fa739c38f\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 47988,\n      \"upload_time\": \"2018-01-21T15:34:20\",\n      \"upload_time_iso_8601\": \"2018-01-21T15:34:20.567853Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7a/46/886917c6a4ce49dd3fff250c01c5abac5390d57992751384fe61befc4877/more_itertools-4.1.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"bf351a1050242ce3af7e475a4da1a26b\",\n        \"sha256\": \"bab2dc6f4be8f9a4a72177842c5283e2dff57c167439a03e3d8d901e854f0f2e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"more-itertools-4.1.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"bf351a1050242ce3af7e475a4da1a26b\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 51310,\n      \"upload_time\": \"2018-01-21T15:34:22\",\n      \"upload_time_iso_8601\": \"2018-01-21T15:34:22.533243Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/db/0b/f5660bf6299ec5b9f17bd36096fa8148a1c843fa77ddfddf9bebac9301f7/more-itertools-4.1.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/more-itertools.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"more-itertools-4.1.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"bf351a1050242ce3af7e475a4da1a26b\",\n        \"sha256\": \"bab2dc6f4be8f9a4a72177842c5283e2dff57c167439a03e3d8d901e854f0f2e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 51310,\n      \"upload-time\": \"2018-01-21T15:34:22.533243Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/db/0b/f5660bf6299ec5b9f17bd36096fa8148a1c843fa77ddfddf9bebac9301f7/more-itertools-4.1.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"59c3513ac4a78ad61b2a0eda88807985c4bb71cd36e6917f2f9c0ea121a7c029\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"59c3513ac4a78ad61b2a0eda88807985c4bb71cd36e6917f2f9c0ea121a7c029\"\n      },\n      \"filename\": \"more_itertools-4.1.0-py2-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"703e1e0922de1f11823da60af1488b7a\",\n        \"sha256\": \"0f461c2cd4ec16611396f9ee57f40433de3d59e95475d84c0c829cde02f746cd\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 47987,\n      \"upload-time\": \"2018-01-21T15:34:19.304356Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4a/88/c28e2a2da8f3dc3a391d9c97ad949f2ea0c05198222e7e6af176e5bf9b26/more_itertools-4.1.0-py2-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"59c3513ac4a78ad61b2a0eda88807985c4bb71cd36e6917f2f9c0ea121a7c029\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"59c3513ac4a78ad61b2a0eda88807985c4bb71cd36e6917f2f9c0ea121a7c029\"\n      },\n      \"filename\": \"more_itertools-4.1.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"ae17a45d13e9dc319794c40fa739c38f\",\n        \"sha256\": \"580b6002d1f28feb5bcb8303278d59cf17dfbd19a63a5c2375112dae72c9bf98\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 47988,\n      \"upload-time\": \"2018-01-21T15:34:20.567853Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7a/46/886917c6a4ce49dd3fff250c01c5abac5390d57992751384fe61befc4877/more_itertools-4.1.0-py3-none-any.whl\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"more-itertools\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"4.1.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pastel/0.1.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"https://github.com/sdispater/pastel/archive/0.1.0.tar.gz\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/sdispater/pastel\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"pastel\",\n    \"package_url\": \"https://pypi.org/project/pastel/\",\n    \"platform\": \"UNKNOWN\",\n    \"project_url\": \"https://pypi.org/project/pastel/\",\n    \"project_urls\": {\n      \"Download\": \"https://github.com/sdispater/pastel/archive/0.1.0.tar.gz\",\n      \"Homepage\": \"https://github.com/sdispater/pastel\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/pastel/0.1.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Bring colors to your terminal.\",\n    \"version\": \"0.1.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"cf7c53ab0a5d7e7c721425b24b486124\",\n        \"sha256\": \"754d192c088e256d52a3f825c3b9e14252d5adc70f53656453f6431e50a70b99\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pastel-0.1.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"cf7c53ab0a5d7e7c721425b24b486124\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 6718,\n      \"upload_time\": \"2017-02-01T23:26:20\",\n      \"upload_time_iso_8601\": \"2017-02-01T23:26:20.148511Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/7e/7d701686013c0d7dae62e0977467232a6adc2e562c23878eb3cd4f97d02e/pastel-0.1.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"43ea5f07660f630da18ae1827f5b4333\",\n        \"sha256\": \"22f14474c4120b37c54ac2173b49b0ac1de9283ca714be6eb3ea8b39296285a9\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pastel-0.1.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"43ea5f07660f630da18ae1827f5b4333\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 4490,\n      \"upload_time\": \"2017-02-01T23:26:21\",\n      \"upload_time_iso_8601\": \"2017-02-01T23:26:21.720111Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/bd/13/a68f2e448b471e8c49e9b596d569ae167a5135ac672b1dc5f24f62f9c15f/pastel-0.1.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pastel.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"a3d071a0b389c60bee1f22fa02aea80322ff957ba0704d4c385f48291af2125d\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"a3d071a0b389c60bee1f22fa02aea80322ff957ba0704d4c385f48291af2125d\"\n      },\n      \"filename\": \"pastel-0.1.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"cf7c53ab0a5d7e7c721425b24b486124\",\n        \"sha256\": \"754d192c088e256d52a3f825c3b9e14252d5adc70f53656453f6431e50a70b99\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 6718,\n      \"upload-time\": \"2017-02-01T23:26:20.148511Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/7e/7d701686013c0d7dae62e0977467232a6adc2e562c23878eb3cd4f97d02e/pastel-0.1.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"pastel-0.1.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"43ea5f07660f630da18ae1827f5b4333\",\n        \"sha256\": \"22f14474c4120b37c54ac2173b49b0ac1de9283ca714be6eb3ea8b39296285a9\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4490,\n      \"upload-time\": \"2017-02-01T23:26:21.720111Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/bd/13/a68f2e448b471e8c49e9b596d569ae167a5135ac672b1dc5f24f62f9c15f/pastel-0.1.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"pastel\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.1.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pluggy/0.6.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Holger Krekel\",\n    \"author_email\": \"holger@merlinux.eu\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: MacOS :: MacOS X\",\n      \"Operating System :: Microsoft :: Windows\",\n      \"Operating System :: POSIX\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Software Development :: Testing\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/pytest-dev/pluggy\",\n    \"keywords\": \"\",\n    \"license\": \"MIT license\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"pluggy\",\n    \"package_url\": \"https://pypi.org/project/pluggy/\",\n    \"platform\": \"unix\",\n    \"project_url\": \"https://pypi.org/project/pluggy/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/pytest-dev/pluggy\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/pluggy/0.6.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"plugin and hook calling mechanisms for python\",\n    \"version\": \"0.6.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"095eed084713c9b2a9a01520485e20fb\",\n        \"sha256\": \"f5f767d398f18aa177976bf9c4d0c05d96487a7d8f07062251585803aaf56246\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pluggy-0.6.0-py2-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"095eed084713c9b2a9a01520485e20fb\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 11953,\n      \"upload_time\": \"2018-04-15T17:55:28\",\n      \"upload_time_iso_8601\": \"2018-04-15T17:55:28.983278Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/82/05/43e3947125a2137cba4746135c75934ceed1863f27e050fc560052104a71/pluggy-0.6.0-py2-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"2b6dc266f54023dfb26726686ee6b227\",\n        \"sha256\": \"d34798b80853ab688de1a3ca5b99ba4de91c459c19c76a555dc939979ae67eb0\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pluggy-0.6.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"2b6dc266f54023dfb26726686ee6b227\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 13723,\n      \"upload_time\": \"2018-04-15T17:55:22\",\n      \"upload_time_iso_8601\": \"2018-04-15T17:55:22.927924Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ba/65/ded3bc40bbf8d887f262f150fbe1ae6637765b5c9534bd55690ed2c0b0f7/pluggy-0.6.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"ef8a88abcd501afd47cb22245fe4315a\",\n        \"sha256\": \"a982e208d054867661d27c6d2a86b17ba05fbb6b1bdc01f42660732dd107f865\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pluggy-0.6.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"ef8a88abcd501afd47cb22245fe4315a\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 19678,\n      \"upload_time\": \"2017-11-24T16:33:11\",\n      \"upload_time_iso_8601\": \"2017-11-24T16:33:11.250495Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/11/bf/cbeb8cdfaffa9f2ea154a30ae31a9d04a1209312e2919138b4171a1f8199/pluggy-0.6.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pluggy.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4d9f559e9a7cda8e28dcedbc0f37ef6335a7560e3386c8016358ed4c5f3785a6\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4d9f559e9a7cda8e28dcedbc0f37ef6335a7560e3386c8016358ed4c5f3785a6\"\n      },\n      \"filename\": \"pluggy-0.6.0-py2-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"095eed084713c9b2a9a01520485e20fb\",\n        \"sha256\": \"f5f767d398f18aa177976bf9c4d0c05d96487a7d8f07062251585803aaf56246\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 11953,\n      \"upload-time\": \"2018-04-15T17:55:28.983278Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/82/05/43e3947125a2137cba4746135c75934ceed1863f27e050fc560052104a71/pluggy-0.6.0-py2-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"77f2f50376e7e82c6302a126af0eaf929596851f0586ced7ac1d5d381c9b6f19\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"77f2f50376e7e82c6302a126af0eaf929596851f0586ced7ac1d5d381c9b6f19\"\n      },\n      \"filename\": \"pluggy-0.6.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"2b6dc266f54023dfb26726686ee6b227\",\n        \"sha256\": \"d34798b80853ab688de1a3ca5b99ba4de91c459c19c76a555dc939979ae67eb0\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 13723,\n      \"upload-time\": \"2018-04-15T17:55:22.927924Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ba/65/ded3bc40bbf8d887f262f150fbe1ae6637765b5c9534bd55690ed2c0b0f7/pluggy-0.6.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"pluggy-0.6.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"ef8a88abcd501afd47cb22245fe4315a\",\n        \"sha256\": \"a982e208d054867661d27c6d2a86b17ba05fbb6b1bdc01f42660732dd107f865\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 19678,\n      \"upload-time\": \"2017-11-24T16:33:11.250495Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/11/bf/cbeb8cdfaffa9f2ea154a30ae31a9d04a1209312e2919138b4171a1f8199/pluggy-0.6.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"pluggy\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.6.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/poetry-core/1.5.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Software Development :: Build Tools\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/python-poetry/poetry-core\",\n    \"keywords\": \"packaging,dependency,poetry\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"poetry-core\",\n    \"package_url\": \"https://pypi.org/project/poetry-core/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/poetry-core/\",\n    \"project_urls\": {\n      \"Bug Tracker\": \"https://github.com/python-poetry/poetry/issues\",\n      \"Homepage\": \"https://github.com/python-poetry/poetry-core\",\n      \"Repository\": \"https://github.com/python-poetry/poetry-core\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/poetry-core/1.5.0/\",\n    \"requires_dist\": [\n      \"importlib-metadata (>=1.7.0) ; python_version < \\\"3.8\\\"\"\n    ],\n    \"requires_python\": \">=3.7,<4.0\",\n    \"summary\": \"Poetry PEP 517 Build Backend\",\n    \"version\": \"1.5.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"be7589b4902793e66d7d979bd8581591\",\n        \"sha256\": \"e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"poetry_core-1.5.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"be7589b4902793e66d7d979bd8581591\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.7,<4.0\",\n      \"size\": 464992,\n      \"upload_time\": \"2023-01-28T10:52:52\",\n      \"upload_time_iso_8601\": \"2023-01-28T10:52:52.445537Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/99/6b0c5fe90e247b2b7b96a27cdf39ee59a02aab3c01d7243fc0c63cd7fb73/poetry_core-1.5.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"3f9b36a7a94cd235bfd5f05794828445\",\n        \"sha256\": \"0ae8d28caf5c12ec1714b16d2e7157ddd52397ea6bfdeba5a9432e449a0184da\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"poetry_core-1.5.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"3f9b36a7a94cd235bfd5f05794828445\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.7,<4.0\",\n      \"size\": 448812,\n      \"upload_time\": \"2023-01-28T10:52:53\",\n      \"upload_time_iso_8601\": \"2023-01-28T10:52:53.916268Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/57/bb/2435fef60bb01f6c0891d9482c7053b50e90639f0f74d7658e99bdd4da69/poetry_core-1.5.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/poetry-core/2.0.1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.12\",\n      \"Programming Language :: Python :: 3.13\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Software Development :: Build Tools\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": null,\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": null,\n    \"keywords\": \"packaging, dependency, poetry\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"Arun Babu Neelicattu\",\n    \"maintainer_email\": \"arun.neelicattu@gmail.com\",\n    \"name\": \"poetry-core\",\n    \"package_url\": \"https://pypi.org/project/poetry-core/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/poetry-core/\",\n    \"project_urls\": {\n      \"Bug Tracker\": \"https://github.com/python-poetry/poetry/issues\",\n      \"Homepage\": \"https://github.com/python-poetry/poetry-core\",\n      \"Repository\": \"https://github.com/python-poetry/poetry-core\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/poetry-core/2.0.1/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"<4.0,>=3.9\",\n    \"summary\": \"Poetry PEP 517 Build Backend\",\n    \"version\": \"2.0.1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"a52cf4beef0de009e0a9a36c9e6962f5\",\n        \"sha256\": \"a3c7009536522cda4eb0fb3805c9dc935b5537f8727dd01efb9c15e51a17552b\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"poetry_core-2.0.1-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"a52cf4beef0de009e0a9a36c9e6962f5\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \"<4.0,>=3.9\",\n      \"size\": 544761,\n      \"upload_time\": \"2025-01-11T18:34:58\",\n      \"upload_time_iso_8601\": \"2025-01-11T18:34:58.051401Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/99/9e/b2d13aecfd3a7ea05e6eeddff7c450b8ff5233e5d0da834fbfd81b19acaf/poetry_core-2.0.1-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"1b1bb959cd760ac509de9b38ae67fc3b\",\n        \"sha256\": \"d2acdaec3b93dc1ab43adaeb0e9a8a6a6b3701c4535b5baab4b718ab12c8993c\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"poetry_core-2.0.1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1b1bb959cd760ac509de9b38ae67fc3b\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \"<4.0,>=3.9\",\n      \"size\": 355487,\n      \"upload_time\": \"2025-01-11T18:35:01\",\n      \"upload_time_iso_8601\": \"2025-01-11T18:35:01.191422Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c4/f5/89d11008714e0a49cab9cba7cce89c66ea5a94f37cc6d283798cc1725fac/poetry_core-2.0.1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/poetry-core.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"7901e9c07999287d28be06df377c9e8529ec602c583dc7e2f22c1211d5f747e3\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"7901e9c07999287d28be06df377c9e8529ec602c583dc7e2f22c1211d5f747e3\"\n      },\n      \"filename\": \"poetry_core-1.5.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"be7589b4902793e66d7d979bd8581591\",\n        \"sha256\": \"e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7,<4.0\",\n      \"size\": 464992,\n      \"upload-time\": \"2023-01-28T10:52:52.445537Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/99/6b0c5fe90e247b2b7b96a27cdf39ee59a02aab3c01d7243fc0c63cd7fb73/poetry_core-1.5.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry_core-1.5.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"3f9b36a7a94cd235bfd5f05794828445\",\n        \"sha256\": \"0ae8d28caf5c12ec1714b16d2e7157ddd52397ea6bfdeba5a9432e449a0184da\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7,<4.0\",\n      \"size\": 448812,\n      \"upload-time\": \"2023-01-28T10:52:53.916268Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/57/bb/2435fef60bb01f6c0891d9482c7053b50e90639f0f74d7658e99bdd4da69/poetry_core-1.5.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"61e9048f1aa5ec95b9327f64f2d2618b80b4564f0b406a8a18980a36f5d9fc4a\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"61e9048f1aa5ec95b9327f64f2d2618b80b4564f0b406a8a18980a36f5d9fc4a\"\n      },\n      \"filename\": \"poetry_core-2.0.1-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"a52cf4beef0de009e0a9a36c9e6962f5\",\n        \"sha256\": \"a3c7009536522cda4eb0fb3805c9dc935b5537f8727dd01efb9c15e51a17552b\"\n      },\n      \"provenance\": \"https://pypi.org/integrity/poetry-core/2.0.1/poetry_core-2.0.1-py3-none-any.whl/provenance\",\n      \"requires-python\": \"<4.0,>=3.9\",\n      \"size\": 544761,\n      \"upload-time\": \"2025-01-11T18:34:58.051401Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/99/9e/b2d13aecfd3a7ea05e6eeddff7c450b8ff5233e5d0da834fbfd81b19acaf/poetry_core-2.0.1-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"poetry_core-2.0.1.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"1b1bb959cd760ac509de9b38ae67fc3b\",\n        \"sha256\": \"d2acdaec3b93dc1ab43adaeb0e9a8a6a6b3701c4535b5baab4b718ab12c8993c\"\n      },\n      \"provenance\": \"https://pypi.org/integrity/poetry-core/2.0.1/poetry_core-2.0.1.tar.gz/provenance\",\n      \"requires-python\": \"<4.0,>=3.9\",\n      \"size\": 355487,\n      \"upload-time\": \"2025-01-11T18:35:01.191422Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c4/f5/89d11008714e0a49cab9cba7cce89c66ea5a94f37cc6d283798cc1725fac/poetry_core-2.0.1.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"poetry-core\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.5.0\",\n    \"2.0.1\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/py/1.5.3.json",
    "content": "{\n  \"info\": {\n    \"author\": \"holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others\",\n    \"author_email\": \"pytest-dev@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 6 - Mature\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: MacOS :: MacOS X\",\n      \"Operating System :: Microsoft :: Windows\",\n      \"Operating System :: POSIX\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Software Development :: Testing\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://py.readthedocs.io/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT license\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"py\",\n    \"package_url\": \"https://pypi.org/project/py/\",\n    \"platform\": \"unix\",\n    \"project_url\": \"https://pypi.org/project/py/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://py.readthedocs.io/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/py/1.5.3/\",\n    \"requires_dist\": null,\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"library with cross-python path, ini-parsing, io, code, log facilities\",\n    \"version\": \"1.5.3\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"b316b380701661cb67732ecdaef30eeb\",\n        \"sha256\": \"ef4a94f47156178e42ef8f2b131db420e0f4b6aa0b3936b6dbde6ad6487476a5\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"py-1.5.3-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"b316b380701661cb67732ecdaef30eeb\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 84903,\n      \"upload_time\": \"2018-03-22T10:06:50\",\n      \"upload_time_iso_8601\": \"2018-03-22T10:06:50.318783Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/67/a5/f77982214dd4c8fd104b066f249adea2c49e25e8703d284382eb5e9ab35a/py-1.5.3-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"623e80cfc06df930414a9ce4bf0fd6c9\",\n        \"sha256\": \"2df2c513c3af11de15f58189ba5539ddc4768c6f33816dc5c03950c8bd6180fa\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"py-1.5.3.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"623e80cfc06df930414a9ce4bf0fd6c9\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 202335,\n      \"upload_time\": \"2018-03-22T10:06:52\",\n      \"upload_time_iso_8601\": \"2018-03-22T10:06:52.627078Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f7/84/b4c6e84672c4ceb94f727f3da8344037b62cee960d80e999b1cd9b832d83/py-1.5.3.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/py.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"6922e816d9a839c9735c50ad24e50bdc0e42c3727cf742f5121e3d6bf58d79e2\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"6922e816d9a839c9735c50ad24e50bdc0e42c3727cf742f5121e3d6bf58d79e2\"\n      },\n      \"filename\": \"py-1.5.3-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"b316b380701661cb67732ecdaef30eeb\",\n        \"sha256\": \"ef4a94f47156178e42ef8f2b131db420e0f4b6aa0b3936b6dbde6ad6487476a5\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 84903,\n      \"upload-time\": \"2018-03-22T10:06:50.318783Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/67/a5/f77982214dd4c8fd104b066f249adea2c49e25e8703d284382eb5e9ab35a/py-1.5.3-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"py-1.5.3.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"623e80cfc06df930414a9ce4bf0fd6c9\",\n        \"sha256\": \"2df2c513c3af11de15f58189ba5539ddc4768c6f33816dc5c03950c8bd6180fa\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 202335,\n      \"upload-time\": \"2018-03-22T10:06:52.627078Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f7/84/b4c6e84672c4ceb94f727f3da8344037b62cee960d80e999b1cd9b832d83/py-1.5.3.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"py\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.5.3\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pylev/1.3.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Daniel Lindsley\",\n    \"author_email\": \"daniel@toastdriven.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"UNKNOWN\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://github.com/toastdriven/pylev\",\n    \"keywords\": null,\n    \"license\": \"UNKNOWN\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": null,\n    \"maintainer_email\": null,\n    \"name\": \"pylev\",\n    \"package_url\": \"https://pypi.org/project/pylev/\",\n    \"platform\": \"UNKNOWN\",\n    \"project_url\": \"https://pypi.org/project/pylev/\",\n    \"project_urls\": {\n      \"Download\": \"UNKNOWN\",\n      \"Homepage\": \"http://github.com/toastdriven/pylev\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/pylev/1.3.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": null,\n    \"summary\": \"A pure Python Levenshtein implementation that's not freaking GPL'd.\",\n    \"version\": \"1.3.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"401c7dff1d242bf1e19f9c6202f0ba4e6fd18cc7ecb8bc85b17b2d16c806e228\",\n        \"md5\": \"6da14dfce5034873fc5c2d7a6e83dc29\",\n        \"sha256\": \"1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pylev-1.3.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"6da14dfce5034873fc5c2d7a6e83dc29\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"2.7\",\n      \"requires_python\": null,\n      \"size\": 4927,\n      \"upload_time\": \"2014-10-23T00:24:34\",\n      \"upload_time_iso_8601\": \"2014-10-23T00:24:34.125905Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/40/1c/7dff1d242bf1e19f9c6202f0ba4e6fd18cc7ecb8bc85b17b2d16c806e228/pylev-1.3.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"cc61dab2081d3d86dcf0b9f5dcfb11b256d76cd14aad7ccdd7c8dd5e7f7e41a0\",\n        \"md5\": \"3be579cfc32ce5140cc04001f898741b\",\n        \"sha256\": \"063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pylev-1.3.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"3be579cfc32ce5140cc04001f898741b\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 3193,\n      \"upload_time\": \"2014-10-23T00:24:19\",\n      \"upload_time_iso_8601\": \"2014-10-23T00:24:19.460779Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cc/61/dab2081d3d86dcf0b9f5dcfb11b256d76cd14aad7ccdd7c8dd5e7f7e41a0/pylev-1.3.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pylev.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4a5b8c2be7e6f09ffe9a9b8018091c79074f61601fbbb693d6a3bd93bb4d5242\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4a5b8c2be7e6f09ffe9a9b8018091c79074f61601fbbb693d6a3bd93bb4d5242\"\n      },\n      \"filename\": \"pylev-1.3.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4927,\n      \"upload-time\": \"2014-10-23T00:24:34.125905Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/40/1c/7dff1d242bf1e19f9c6202f0ba4e6fd18cc7ecb8bc85b17b2d16c806e228/pylev-1.3.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"pylev-1.3.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 3193,\n      \"upload-time\": \"2014-10-23T00:24:19.460779Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cc/61/dab2081d3d86dcf0b9f5dcfb11b256d76cd14aad7ccdd7c8dd5e7f7e41a0/pylev-1.3.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"pylev\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.3.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pytest/3.5.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others\",\n    \"author_email\": \"\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 6 - Mature\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: MacOS :: MacOS X\",\n      \"Operating System :: Microsoft :: Windows\",\n      \"Operating System :: POSIX\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Software Development :: Testing\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pytest.org\",\n    \"keywords\": \"test unittest\",\n    \"license\": \"MIT license\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"pytest\",\n    \"package_url\": \"https://pypi.org/project/pytest/\",\n    \"platform\": \"unix\",\n    \"project_url\": \"https://pypi.org/project/pytest/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pytest.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/pytest/3.5.0/\",\n    \"requires_dist\": [\n      \"py (>=1.5.0)\",\n      \"six (>=1.10.0)\",\n      \"setuptools\",\n      \"attrs (>=17.4.0)\",\n      \"more-itertools (>=4.0.0)\",\n      \"pluggy (<0.7,>=0.5)\",\n      \"funcsigs; python_version < \\\"3.0\\\"\",\n      \"colorama; sys_platform == \\\"win32\\\"\"\n    ],\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"pytest: simple powerful testing with Python\",\n    \"version\": \"3.5.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"4a8651dec151e76f283bf59e333286f9\",\n        \"sha256\": \"427b4582bda18e92ad1967e8b1e071e2c53e6cb7e3e5f090cb3ca443455be23f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pytest-3.5.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"4a8651dec151e76f283bf59e333286f9\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 194247,\n      \"upload_time\": \"2018-03-22T23:47:54\",\n      \"upload_time_iso_8601\": \"2018-03-22T23:47:54.595523Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ed/96/271c93f75212c06e2a7ec3e2fa8a9c90acee0a4838dc05bf379ea09aae31/pytest-3.5.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"ccd78dac54112045f561c4df86631f19\",\n        \"sha256\": \"677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pytest-3.5.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"ccd78dac54112045f561c4df86631f19\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 830816,\n      \"upload_time\": \"2018-03-22T23:47:56\",\n      \"upload_time_iso_8601\": \"2018-03-22T23:47:56.511852Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/56/6019153cdd743300c5688ab3b07702355283e53c83fbf922242c053ffb7b/pytest-3.5.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pytest/3.5.1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others\",\n    \"author_email\": \"\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 6 - Mature\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: MacOS :: MacOS X\",\n      \"Operating System :: Microsoft :: Windows\",\n      \"Operating System :: POSIX\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Software Development :: Testing\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pytest.org\",\n    \"keywords\": \"test unittest\",\n    \"license\": \"MIT license\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"pytest\",\n    \"package_url\": \"https://pypi.org/project/pytest/\",\n    \"platform\": \"unix\",\n    \"project_url\": \"https://pypi.org/project/pytest/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pytest.org\",\n      \"Source\": \"https://github.com/pytest-dev/pytest\",\n      \"Tracker\": \"https://github.com/pytest-dev/pytest/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/pytest/3.5.1/\",\n    \"requires_dist\": [\n      \"py (>=1.5.0)\",\n      \"six (>=1.10.0)\",\n      \"setuptools\",\n      \"attrs (>=17.4.0)\",\n      \"more-itertools (>=4.0.0)\",\n      \"pluggy (<0.7,>=0.5)\",\n      \"funcsigs; python_version < \\\"3.0\\\"\",\n      \"colorama; sys_platform == \\\"win32\\\"\"\n    ],\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"pytest: simple powerful testing with Python\",\n    \"version\": \"3.5.1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"1e81fba94885bef80170545d045924eb\",\n        \"sha256\": \"d327df3686046c5b374a9776d9e11606f7dba6fb3db5cf5d60ebc78a31e0768e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pytest-3.5.1-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1e81fba94885bef80170545d045924eb\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 192143,\n      \"upload_time\": \"2018-04-24T21:37:43\",\n      \"upload_time_iso_8601\": \"2018-04-24T21:37:43.104462Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/76/52/fc48d02492d9e6070cb672d9133382e83084f567f88eff1c27bd2c6c27a8/pytest-3.5.1-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"961104636090457187851ccb9ef0f677\",\n        \"sha256\": \"b8fe151f3e181801dd38583a1c03818fbc662a8fce96c9063a0af624613e78f8\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"pytest-3.5.1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"961104636090457187851ccb9ef0f677\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 830571,\n      \"upload_time\": \"2018-04-24T21:37:44\",\n      \"upload_time_iso_8601\": \"2018-04-24T21:37:44.492084Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b2/85/24954df0ea8156599563b753de54383a5d702081093b7953334e4701b8d8/pytest-3.5.1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pytest.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"3825a201ef8accd23ae8e728ac48c8f06fdf04cbdf2aa5f0729cbcab5d06a833\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"3825a201ef8accd23ae8e728ac48c8f06fdf04cbdf2aa5f0729cbcab5d06a833\"\n      },\n      \"filename\": \"pytest-3.5.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"4a8651dec151e76f283bf59e333286f9\",\n        \"sha256\": \"427b4582bda18e92ad1967e8b1e071e2c53e6cb7e3e5f090cb3ca443455be23f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 194247,\n      \"upload-time\": \"2018-03-22T23:47:54.595523Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ed/96/271c93f75212c06e2a7ec3e2fa8a9c90acee0a4838dc05bf379ea09aae31/pytest-3.5.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"pytest-3.5.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"ccd78dac54112045f561c4df86631f19\",\n        \"sha256\": \"677b1d6decd29c041fe64276f29f79fbe66e40c59e445eb251366b4a8ab8bf68\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 830816,\n      \"upload-time\": \"2018-03-22T23:47:56.511852Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2d/56/6019153cdd743300c5688ab3b07702355283e53c83fbf922242c053ffb7b/pytest-3.5.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"8a80e51c143666a2d213dc466ec5be4ab558c6d84457173f4ed079f4331ebb71\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"8a80e51c143666a2d213dc466ec5be4ab558c6d84457173f4ed079f4331ebb71\"\n      },\n      \"filename\": \"pytest-3.5.1-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"1e81fba94885bef80170545d045924eb\",\n        \"sha256\": \"d327df3686046c5b374a9776d9e11606f7dba6fb3db5cf5d60ebc78a31e0768e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 192143,\n      \"upload-time\": \"2018-04-24T21:37:43.104462Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/76/52/fc48d02492d9e6070cb672d9133382e83084f567f88eff1c27bd2c6c27a8/pytest-3.5.1-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"pytest-3.5.1.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"961104636090457187851ccb9ef0f677\",\n        \"sha256\": \"b8fe151f3e181801dd38583a1c03818fbc662a8fce96c9063a0af624613e78f8\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 830571,\n      \"upload-time\": \"2018-04-24T21:37:44.492084Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b2/85/24954df0ea8156599563b753de54383a5d702081093b7953334e4701b8d8/pytest-3.5.1.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"pytest\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"3.5.0\",\n    \"3.5.1\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/python-language-server/0.21.2.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Palantir Technologies, Inc.\",\n    \"author_email\": \"\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/palantir/python-language-server\",\n    \"keywords\": \"\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"python-language-server\",\n    \"package_url\": \"https://pypi.org/project/python-language-server/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/python-language-server/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/palantir/python-language-server\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/python-language-server/0.21.2/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Python Language Server for the Language Server Protocol\",\n    \"version\": \"0.21.2\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"677602ec38bc1c7b72de6128d90d846b\",\n        \"sha256\": \"91b564e092f3135b2bac70dbd23d283da5ad50269766a76648787b69fe702c7e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"python-language-server-0.21.2.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"677602ec38bc1c7b72de6128d90d846b\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 51676,\n      \"upload_time\": \"2018-09-06T18:11:28\",\n      \"upload_time_iso_8601\": \"2018-09-06T18:11:28.546667Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/python-language-server.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"python-language-server-0.21.2.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"677602ec38bc1c7b72de6128d90d846b\",\n        \"sha256\": \"91b564e092f3135b2bac70dbd23d283da5ad50269766a76648787b69fe702c7e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 51676,\n      \"upload-time\": \"2018-09-06T18:11:28.546667Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"python-language-server\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.21.2\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pyyaml/3.13.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kirill Simonov\",\n    \"author_email\": \"xi@resolvent.net\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: Text Processing :: Markup\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pyyaml.org/wiki/PyYAML\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"PyYAML\",\n    \"package_url\": \"https://pypi.org/project/PyYAML/\",\n    \"platform\": \"Any\",\n    \"project_url\": \"https://pypi.org/project/PyYAML/\",\n    \"project_urls\": {\n      \"Download\": \"http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\",\n      \"Homepage\": \"http://pyyaml.org/wiki/PyYAML\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/PyYAML/3.13/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"YAML parser and emitter for Python\",\n    \"version\": \"3.13\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"b82e9c2285870c9de070a1fa5ede702ab5fb329901b3cc4028c24f44eda27c5f\",\n        \"md5\": \"a83441aa7004e474bed6f6daeb61f27a\",\n        \"sha256\": \"d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp27-cp27m-win32.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"a83441aa7004e474bed6f6daeb61f27a\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp27\",\n      \"requires_python\": null,\n      \"size\": 191712,\n      \"upload_time\": \"2018-07-05T22:53:15\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:15.231061Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b8/2e/9c2285870c9de070a1fa5ede702ab5fb329901b3cc4028c24f44eda27c5f/PyYAML-3.13-cp27-cp27m-win32.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"df4d1ef8d60464a171112401e17a3a3e88fdb1d5b44af7606e8652b2f39ee9ce\",\n        \"md5\": \"dd05ba2d6cb042452a3849dea13b94f0\",\n        \"sha256\": \"e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp27-cp27m-win_amd64.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"dd05ba2d6cb042452a3849dea13b94f0\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp27\",\n      \"requires_python\": null,\n      \"size\": 209872,\n      \"upload_time\": \"2018-07-05T22:53:16\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:16.904443Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/df/4d/1ef8d60464a171112401e17a3a3e88fdb1d5b44af7606e8652b2f39ee9ce/PyYAML-3.13-cp27-cp27m-win_amd64.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"35f0cf0363b5c431c3a828284903aeacc6bdbba342fd4d7871dda9a3b0b00d15\",\n        \"md5\": \"49365caa070d53e30deceae118e4fea8\",\n        \"sha256\": \"558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp34-cp34m-win32.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"49365caa070d53e30deceae118e4fea8\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp34\",\n      \"requires_python\": null,\n      \"size\": 192898,\n      \"upload_time\": \"2018-07-05T22:53:19\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:19.190872Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/35/f0/cf0363b5c431c3a828284903aeacc6bdbba342fd4d7871dda9a3b0b00d15/PyYAML-3.13-cp34-cp34m-win32.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"8cbc8950092a86259dc511e02a4c3a517ed4b28a254e4da134e3c04e5264e5a3\",\n        \"md5\": \"0c486a54c19dd18b9e65a559886935c4\",\n        \"sha256\": \"d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp34-cp34m-win_amd64.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"0c486a54c19dd18b9e65a559886935c4\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp34\",\n      \"requires_python\": null,\n      \"size\": 206242,\n      \"upload_time\": \"2018-07-05T22:53:20\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:20.770605Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8c/bc/8950092a86259dc511e02a4c3a517ed4b28a254e4da134e3c04e5264e5a3/PyYAML-3.13-cp34-cp34m-win_amd64.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"29338bbcd3740d9e96cfb57427b8db7a12093402a3a83f2054887e027b2849de\",\n        \"md5\": \"53ce2b9f6b741fb2f070d12839b5789e\",\n        \"sha256\": \"a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp35-cp35m-win32.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"53ce2b9f6b741fb2f070d12839b5789e\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp35\",\n      \"requires_python\": null,\n      \"size\": 187499,\n      \"upload_time\": \"2018-07-05T22:53:22\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:22.576919Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/29/33/8bbcd3740d9e96cfb57427b8db7a12093402a3a83f2054887e027b2849de/PyYAML-3.13-cp35-cp35m-win32.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"add4d895fb7ac1b0828151b829a32cefc8a8b58b4499570520b91af20982b880\",\n        \"md5\": \"1b70e7ced4c82364bda4ac9094d6e259\",\n        \"sha256\": \"bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp35-cp35m-win_amd64.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1b70e7ced4c82364bda4ac9094d6e259\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp35\",\n      \"requires_python\": null,\n      \"size\": 205387,\n      \"upload_time\": \"2018-07-05T22:53:24\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:24.438646Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ad/d4/d895fb7ac1b0828151b829a32cefc8a8b58b4499570520b91af20982b880/PyYAML-3.13-cp35-cp35m-win_amd64.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"fb510c49c6caafe8d9a27ad9b0ca9f91adda5a5072b9efbbe7585fb97a4c71c4\",\n        \"md5\": \"8f62197b853b5b387ff588df05cee7a6\",\n        \"sha256\": \"40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp36-cp36m-win32.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8f62197b853b5b387ff588df05cee7a6\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp36\",\n      \"requires_python\": null,\n      \"size\": 188186,\n      \"upload_time\": \"2018-07-05T22:53:25\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:25.923669Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fb/51/0c49c6caafe8d9a27ad9b0ca9f91adda5a5072b9efbbe7585fb97a4c71c4/PyYAML-3.13-cp36-cp36m-win32.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"4fca5fad249c5032270540c24d2189b0ddf1396aac49b0bdc548162edcf14131\",\n        \"md5\": \"ff7280dd032d202b417871d39febadec\",\n        \"sha256\": \"3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp36-cp36m-win_amd64.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"ff7280dd032d202b417871d39febadec\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp36\",\n      \"requires_python\": null,\n      \"size\": 206277,\n      \"upload_time\": \"2018-07-05T22:53:27\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:27.386610Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/ca/5fad249c5032270540c24d2189b0ddf1396aac49b0bdc548162edcf14131/PyYAML-3.13-cp36-cp36m-win_amd64.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"5cedd6557f70daaaab6ee5cd2f8ccf7bedd63081e522e38679c03840e1acc114\",\n        \"md5\": \"03ac720a2dcb18f2f1a3d026d281d778\",\n        \"sha256\": \"e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp37-cp37m-win32.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"03ac720a2dcb18f2f1a3d026d281d778\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp37\",\n      \"requires_python\": null,\n      \"size\": 188313,\n      \"upload_time\": \"2018-07-05T22:53:28\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:28.995194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5c/ed/d6557f70daaaab6ee5cd2f8ccf7bedd63081e522e38679c03840e1acc114/PyYAML-3.13-cp37-cp37m-win32.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"bf96d02ef8e1f3073e07ffdc240444e5041f403f29c0775f9f1653f18221082f\",\n        \"md5\": \"02ab28701247a80e059daa6efe11e67d\",\n        \"sha256\": \"aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13-cp37-cp37m-win_amd64.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"02ab28701247a80e059daa6efe11e67d\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"cp37\",\n      \"requires_python\": null,\n      \"size\": 206614,\n      \"upload_time\": \"2018-07-05T22:53:30\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:53:30.864210Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/bf/96/d02ef8e1f3073e07ffdc240444e5041f403f29c0775f9f1653f18221082f/PyYAML-3.13-cp37-cp37m-win_amd64.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"9ea31d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769\",\n        \"md5\": \"b78b96636d68ac581c0e2f38158c224f\",\n        \"sha256\": \"3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"PyYAML-3.13.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"b78b96636d68ac581c0e2f38158c224f\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 270607,\n      \"upload_time\": \"2018-07-05T22:52:16\",\n      \"upload_time_iso_8601\": \"2018-07-05T22:52:16.800539Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/pyyaml.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"eb5621acdfd1a084efcc04d1a5292117422c616dc2ea004d8e760aadbd3f8388\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"eb5621acdfd1a084efcc04d1a5292117422c616dc2ea004d8e760aadbd3f8388\"\n      },\n      \"filename\": \"PyYAML-3.13-cp27-cp27m-win32.whl\",\n      \"hashes\": {\n        \"sha256\": \"d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 191712,\n      \"upload-time\": \"2018-07-05T22:53:15.231061Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b8/2e/9c2285870c9de070a1fa5ede702ab5fb329901b3cc4028c24f44eda27c5f/PyYAML-3.13-cp27-cp27m-win32.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"eb5621acdfd1a084efcc04d1a5292117422c616dc2ea004d8e760aadbd3f8388\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"eb5621acdfd1a084efcc04d1a5292117422c616dc2ea004d8e760aadbd3f8388\"\n      },\n      \"filename\": \"PyYAML-3.13-cp27-cp27m-win_amd64.whl\",\n      \"hashes\": {\n        \"sha256\": \"e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 209872,\n      \"upload-time\": \"2018-07-05T22:53:16.904443Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/df/4d/1ef8d60464a171112401e17a3a3e88fdb1d5b44af7606e8652b2f39ee9ce/PyYAML-3.13-cp27-cp27m-win_amd64.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp34-cp34m-win32.whl\",\n      \"hashes\": {\n        \"sha256\": \"558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 192898,\n      \"upload-time\": \"2018-07-05T22:53:19.190872Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/35/f0/cf0363b5c431c3a828284903aeacc6bdbba342fd4d7871dda9a3b0b00d15/PyYAML-3.13-cp34-cp34m-win32.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp34-cp34m-win_amd64.whl\",\n      \"hashes\": {\n        \"sha256\": \"d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 206242,\n      \"upload-time\": \"2018-07-05T22:53:20.770605Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/8c/bc/8950092a86259dc511e02a4c3a517ed4b28a254e4da134e3c04e5264e5a3/PyYAML-3.13-cp34-cp34m-win_amd64.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp35-cp35m-win32.whl\",\n      \"hashes\": {\n        \"sha256\": \"a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 187499,\n      \"upload-time\": \"2018-07-05T22:53:22.576919Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/29/33/8bbcd3740d9e96cfb57427b8db7a12093402a3a83f2054887e027b2849de/PyYAML-3.13-cp35-cp35m-win32.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp35-cp35m-win_amd64.whl\",\n      \"hashes\": {\n        \"sha256\": \"bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 205387,\n      \"upload-time\": \"2018-07-05T22:53:24.438646Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ad/d4/d895fb7ac1b0828151b829a32cefc8a8b58b4499570520b91af20982b880/PyYAML-3.13-cp35-cp35m-win_amd64.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp36-cp36m-win32.whl\",\n      \"hashes\": {\n        \"sha256\": \"40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 188186,\n      \"upload-time\": \"2018-07-05T22:53:25.923669Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fb/51/0c49c6caafe8d9a27ad9b0ca9f91adda5a5072b9efbbe7585fb97a4c71c4/PyYAML-3.13-cp36-cp36m-win32.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp36-cp36m-win_amd64.whl\",\n      \"hashes\": {\n        \"sha256\": \"3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 206277,\n      \"upload-time\": \"2018-07-05T22:53:27.386610Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/ca/5fad249c5032270540c24d2189b0ddf1396aac49b0bdc548162edcf14131/PyYAML-3.13-cp36-cp36m-win_amd64.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp37-cp37m-win32.whl\",\n      \"hashes\": {\n        \"sha256\": \"e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 188313,\n      \"upload-time\": \"2018-07-05T22:53:28.995194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5c/ed/d6557f70daaaab6ee5cd2f8ccf7bedd63081e522e38679c03840e1acc114/PyYAML-3.13-cp37-cp37m-win32.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bb6a78439e80d7904471d134b006d366645dfdf9241495f8a9626f778f9dc5bd\"\n      },\n      \"filename\": \"PyYAML-3.13-cp37-cp37m-win_amd64.whl\",\n      \"hashes\": {\n        \"sha256\": \"aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 206614,\n      \"upload-time\": \"2018-07-05T22:53:30.864210Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/bf/96/d02ef8e1f3073e07ffdc240444e5041f403f29c0775f9f1653f18221082f/PyYAML-3.13-cp37-cp37m-win_amd64.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"PyYAML-3.13.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 270607,\n      \"upload-time\": \"2018-07-05T22:52:16.800539Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"pyyaml\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"3.13.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.18.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.18.0/\",\n    \"requires_dist\": [\n      \"certifi (>=2017.4.17)\",\n      \"chardet (>=3.0.2,<3.1.0)\",\n      \"idna (>=2.5,<2.6)\",\n      \"urllib3 (<1.22,>=1.21.1)\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.18.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"e2f0c81405acbf53d0412b984eb3fc578cdd10e347374e1aec074638a500c186\",\n        \"md5\": \"6f34e2439fcb3dd1b6e3304903bb6be8\",\n        \"sha256\": \"5e88d64aa56ac0fda54e77fb9762ebc65879e171b746d5479a33c4082519d6c6\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"6f34e2439fcb3dd1b6e3304903bb6be8\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 563596,\n      \"upload_time\": \"2017-06-14T15:44:35\",\n      \"upload_time_iso_8601\": \"2017-06-14T15:44:35.080617Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e2/f0/c81405acbf53d0412b984eb3fc578cdd10e347374e1aec074638a500c186/requests-2.18.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"e097e2f972b6826c9cfe57b6934e3773d2783733bc2d345d810bafd309df3d15\",\n        \"md5\": \"b8b333ace1653652ddcce95284577f5c\",\n        \"sha256\": \"cd0189f962787284bff715fddaad478eb4d9c15aa167bd64e52ea0f661e7ea5c\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"b8b333ace1653652ddcce95284577f5c\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 124085,\n      \"upload_time\": \"2017-06-14T15:44:37\",\n      \"upload_time_iso_8601\": \"2017-06-14T15:44:37.484470Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e0/97/e2f972b6826c9cfe57b6934e3773d2783733bc2d345d810bafd309df3d15/requests-2.18.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.18.1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.18.1/\",\n    \"requires_dist\": [\n      \"certifi (>=2017.4.17)\",\n      \"chardet (>=3.0.2,<3.1.0)\",\n      \"idna (>=2.5,<2.6)\",\n      \"urllib3 (<1.22,>=1.21.1)\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.18.1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"5a58671011e3ff4a06e2969322267d78dcfda1bf4d1576551df1cce93cd7239d\",\n        \"md5\": \"a7fbdc82134a2610b3d0cdc7e59f0bde\",\n        \"sha256\": \"6afd3371c1f4c1970497cdcace5c5ecbbe58267bf05ca1abd93d99d170803ab7\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.1-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"a7fbdc82134a2610b3d0cdc7e59f0bde\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 88107,\n      \"upload_time\": \"2017-06-14T17:51:25\",\n      \"upload_time_iso_8601\": \"2017-06-14T17:51:25.096686Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5a/58/671011e3ff4a06e2969322267d78dcfda1bf4d1576551df1cce93cd7239d/requests-2.18.1-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"2cb52b6e8ef8dd18203b6399e9f28c7d54f6de7b7549853fe36d575bd31e29a7\",\n        \"md5\": \"40f723ed01dddeaf990d0609d073f021\",\n        \"sha256\": \"c6f3bdf4a4323ac7b45d01e04a6f6c20e32a052cd04de81e05103abc049ad9b9\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"40f723ed01dddeaf990d0609d073f021\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 124229,\n      \"upload_time\": \"2017-06-14T17:51:28\",\n      \"upload_time_iso_8601\": \"2017-06-14T17:51:28.960131Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2c/b5/2b6e8ef8dd18203b6399e9f28c7d54f6de7b7549853fe36d575bd31e29a7/requests-2.18.1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.18.2.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.18.2/\",\n    \"requires_dist\": [\n      \"certifi (>=2017.4.17)\",\n      \"chardet (>=3.0.2,<3.1.0)\",\n      \"idna (>=2.5,<2.6)\",\n      \"urllib3 (<1.23,>=1.21.1)\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.18.2\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"cffa31b222e4b44975de1b5ac3e1a725abdfeb00e0d761567ab426ee28a7fc73\",\n        \"md5\": \"08026e24839d8bf36d248abfb2b6b674\",\n        \"sha256\": \"414459f05392835d4d653b57b8e58f98aea9c6ff2782e37de0a1ee92891ce900\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.2-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"08026e24839d8bf36d248abfb2b6b674\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 88342,\n      \"upload_time\": \"2017-07-25T15:23:15\",\n      \"upload_time_iso_8601\": \"2017-07-25T15:23:15.338694Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cf/fa/31b222e4b44975de1b5ac3e1a725abdfeb00e0d761567ab426ee28a7fc73/requests-2.18.2-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"072e81fdfdfac91cf3cb2518fb149ac67caf0e081b485eab68e9aee63396f7e8\",\n        \"md5\": \"49bd9924d3be341871bc922cde6f372e\",\n        \"sha256\": \"5b26fcc5e72757a867e4d562333f841eddcef93548908a1bb1a9207260618da9\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.2.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"49bd9924d3be341871bc922cde6f372e\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 125381,\n      \"upload_time\": \"2017-07-25T15:23:18\",\n      \"upload_time_iso_8601\": \"2017-07-25T15:23:18.103843Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/07/2e/81fdfdfac91cf3cb2518fb149ac67caf0e081b485eab68e9aee63396f7e8/requests-2.18.2.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.18.3.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.18.3/\",\n    \"requires_dist\": [\n      \"certifi (>=2017.4.17)\",\n      \"chardet (>=3.0.2,<3.1.0)\",\n      \"idna (>=2.5,<2.6)\",\n      \"urllib3 (<1.23,>=1.21.1)\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.18.3\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"ba92c35ed010e8f96781f08dfa6d9a6a19445a175a9304aceedece77cd48b68f\",\n        \"md5\": \"d2d34c959a45f7da592a383485ad8b8c\",\n        \"sha256\": \"b62be4ec5999c24d10c98d248a136e7db20ca6616a2b65060cd9399417331e8a\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.3-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"d2d34c959a45f7da592a383485ad8b8c\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 88626,\n      \"upload_time\": \"2017-08-02T13:23:31\",\n      \"upload_time_iso_8601\": \"2017-08-02T13:23:31.998938Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ba/92/c35ed010e8f96781f08dfa6d9a6a19445a175a9304aceedece77cd48b68f/requests-2.18.3-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"c338d95ddb6cc8558930600be088e174a2152261a1e0708a18bf91b5b8c90b22\",\n        \"md5\": \"c8f60cf816a35c0c3fef0a40d0e407a6\",\n        \"sha256\": \"fb68a7baef4965c12d9cd67c0f5a46e6e28be3d8c7b6910c758fbcc99880b518\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.3.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"c8f60cf816a35c0c3fef0a40d0e407a6\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 126008,\n      \"upload_time\": \"2017-08-02T13:23:35\",\n      \"upload_time_iso_8601\": \"2017-08-02T13:23:35.599515Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c3/38/d95ddb6cc8558930600be088e174a2152261a1e0708a18bf91b5b8c90b22/requests-2.18.3.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.18.4.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2.6\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.18.4/\",\n    \"requires_dist\": [\n      \"certifi (>=2017.4.17)\",\n      \"chardet (>=3.0.2,<3.1.0)\",\n      \"idna (>=2.5,<2.7)\",\n      \"urllib3 (<1.23,>=1.21.1)\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.18.4\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"e770e65750c42f40b97b0ed738d0f859\",\n        \"sha256\": \"098be851f30be5bcb2c7537798d44314f576e53818ba9def25141ae4dce8b25d\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.4-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"e770e65750c42f40b97b0ed738d0f859\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 88704,\n      \"upload_time\": \"2017-08-15T13:23:43\",\n      \"upload_time_iso_8601\": \"2017-08-15T13:23:43.489631Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/49/df/50aa1999ab9bde74656c2919d9c0c085fd2b3775fd3eca826012bef76d8c/requests-2.18.4-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"942a6a383dc94da90cf58f5adcf028a4\",\n        \"sha256\": \"ec62f7e0e9d4814656b0172dbd592fea06127c6556ff5651eb5d2c8768671fd4\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.18.4.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"942a6a383dc94da90cf58f5adcf028a4\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 126224,\n      \"upload_time\": \"2017-08-15T13:23:46\",\n      \"upload_time_iso_8601\": \"2017-08-15T13:23:46.348325Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b0/e1/eab4fc3752e3d240468a8c0b284607899d2fbfb236a56b7377a329aa8d09/requests-2.18.4.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests/2.19.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Kenneth Reitz\",\n    \"author_email\": \"me@kennethreitz.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: Apache Software License\",\n      \"Natural Language :: English\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://python-requests.org\",\n    \"keywords\": \"\",\n    \"license\": \"Apache 2.0\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"requests\",\n    \"package_url\": \"https://pypi.org/project/requests/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/requests/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://python-requests.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/requests/2.19.0/\",\n    \"requires_dist\": [\n      \"chardet (<3.1.0,>=3.0.2)\",\n      \"idna (<2.8,>=2.5)\",\n      \"urllib3 (<1.24,>=1.21.1)\",\n      \"certifi (>=2017.4.17)\",\n      \"pyOpenSSL (>=0.14); extra == 'security'\",\n      \"cryptography (>=1.3.4); extra == 'security'\",\n      \"idna (>=2.0.0); extra == 'security'\",\n      \"PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\",\n      \"win-inet-pton; sys_platform == \\\"win32\\\" and (python_version == \\\"2.7\\\" or python_version == \\\"2.6\\\") and extra == 'socks'\"\n    ],\n    \"requires_python\": \">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"Python HTTP for Humans.\",\n    \"version\": \"2.19.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"cc15e1c318dbc20032ffbe5628837ca0de2d5b116ffd1b849c699634010f6a5d\",\n        \"md5\": \"1ae2b89f8ea3e4aea8b199987fb2aae9\",\n        \"sha256\": \"421cfc8d9dde7d6aff68196420afd86b88c65d77d8da9cf83f4ecad785d7b9d6\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.19.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1ae2b89f8ea3e4aea8b199987fb2aae9\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 91865,\n      \"upload_time\": \"2018-06-12T14:46:15\",\n      \"upload_time_iso_8601\": \"2018-06-12T14:46:15.289074Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cc/15/e1c318dbc20032ffbe5628837ca0de2d5b116ffd1b849c699634010f6a5d/requests-2.19.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"752782da3fa4ea7a8c3526c48eaafe427352ff9c931633b917c2251826a43697\",\n        \"md5\": \"8a7844c58d496e9e92481de459830229\",\n        \"sha256\": \"cc408268d0e21589bcc2b2c248e42932b8c4d112f499c12c92e99e2178a6134c\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"requests-2.19.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8a7844c58d496e9e92481de459830229\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 130875,\n      \"upload_time\": \"2018-06-12T14:46:17\",\n      \"upload_time_iso_8601\": \"2018-06-12T14:46:17.223245Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/75/27/82da3fa4ea7a8c3526c48eaafe427352ff9c931633b917c2251826a43697/requests-2.19.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/requests.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4beb28df7dccdb2c54530560738dda249189e114357a69d5e5e7f06a2a019197\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4beb28df7dccdb2c54530560738dda249189e114357a69d5e5e7f06a2a019197\"\n      },\n      \"filename\": \"requests-2.18.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"5e88d64aa56ac0fda54e77fb9762ebc65879e171b746d5479a33c4082519d6c6\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 563596,\n      \"upload-time\": \"2017-06-14T15:44:35.080617Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e2/f0/c81405acbf53d0412b984eb3fc578cdd10e347374e1aec074638a500c186/requests-2.18.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.18.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"cd0189f962787284bff715fddaad478eb4d9c15aa167bd64e52ea0f661e7ea5c\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 124085,\n      \"upload-time\": \"2017-06-14T15:44:37.484470Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e0/97/e2f972b6826c9cfe57b6934e3773d2783733bc2d345d810bafd309df3d15/requests-2.18.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4e4599f783a6950eb72a9d78fecb2f3438f07237e83708a587beda8a335b5f94\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4e4599f783a6950eb72a9d78fecb2f3438f07237e83708a587beda8a335b5f94\"\n      },\n      \"filename\": \"requests-2.18.1-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"6afd3371c1f4c1970497cdcace5c5ecbbe58267bf05ca1abd93d99d170803ab7\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 88107,\n      \"upload-time\": \"2017-06-14T17:51:25.096686Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5a/58/671011e3ff4a06e2969322267d78dcfda1bf4d1576551df1cce93cd7239d/requests-2.18.1-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.18.1.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"c6f3bdf4a4323ac7b45d01e04a6f6c20e32a052cd04de81e05103abc049ad9b9\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 124229,\n      \"upload-time\": \"2017-06-14T17:51:28.960131Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/2c/b5/2b6e8ef8dd18203b6399e9f28c7d54f6de7b7549853fe36d575bd31e29a7/requests-2.18.1.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"2f28a31e586b7d1d382e1b64b48ce26d5ff18781f9eca0fba7654b55d7a76549\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"2f28a31e586b7d1d382e1b64b48ce26d5ff18781f9eca0fba7654b55d7a76549\"\n      },\n      \"filename\": \"requests-2.18.2-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"414459f05392835d4d653b57b8e58f98aea9c6ff2782e37de0a1ee92891ce900\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 88342,\n      \"upload-time\": \"2017-07-25T15:23:15.338694Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cf/fa/31b222e4b44975de1b5ac3e1a725abdfeb00e0d761567ab426ee28a7fc73/requests-2.18.2-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.18.2.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"5b26fcc5e72757a867e4d562333f841eddcef93548908a1bb1a9207260618da9\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 125381,\n      \"upload-time\": \"2017-07-25T15:23:18.103843Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/07/2e/81fdfdfac91cf3cb2518fb149ac67caf0e081b485eab68e9aee63396f7e8/requests-2.18.2.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"b486edffe7b597477bf5645377f50ad2cd4db97413c3e74df3051d9b0db41ac4\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"b486edffe7b597477bf5645377f50ad2cd4db97413c3e74df3051d9b0db41ac4\"\n      },\n      \"filename\": \"requests-2.18.3-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"b62be4ec5999c24d10c98d248a136e7db20ca6616a2b65060cd9399417331e8a\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 88626,\n      \"upload-time\": \"2017-08-02T13:23:31.998938Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/ba/92/c35ed010e8f96781f08dfa6d9a6a19445a175a9304aceedece77cd48b68f/requests-2.18.3-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.18.3.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"fb68a7baef4965c12d9cd67c0f5a46e6e28be3d8c7b6910c758fbcc99880b518\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 126008,\n      \"upload-time\": \"2017-08-02T13:23:35.599515Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/c3/38/d95ddb6cc8558930600be088e174a2152261a1e0708a18bf91b5b8c90b22/requests-2.18.3.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"37a7c01ccfdf63746e53a70d20723cb095be3f8048582ce38673adc640d14dfd\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"37a7c01ccfdf63746e53a70d20723cb095be3f8048582ce38673adc640d14dfd\"\n      },\n      \"filename\": \"requests-2.18.4-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"e770e65750c42f40b97b0ed738d0f859\",\n        \"sha256\": \"098be851f30be5bcb2c7537798d44314f576e53818ba9def25141ae4dce8b25d\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 88704,\n      \"upload-time\": \"2017-08-15T13:23:43.489631Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/49/df/50aa1999ab9bde74656c2919d9c0c085fd2b3775fd3eca826012bef76d8c/requests-2.18.4-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.18.4.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"942a6a383dc94da90cf58f5adcf028a4\",\n        \"sha256\": \"ec62f7e0e9d4814656b0172dbd592fea06127c6556ff5651eb5d2c8768671fd4\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 126224,\n      \"upload-time\": \"2017-08-15T13:23:46.348325Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/b0/e1/eab4fc3752e3d240468a8c0b284607899d2fbfb236a56b7377a329aa8d09/requests-2.18.4.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"e022ab2bfbbfbf3b77f29b8bc3115eb5b202c651b9b928ec9c0b7ef9a16070f3\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"e022ab2bfbbfbf3b77f29b8bc3115eb5b202c651b9b928ec9c0b7ef9a16070f3\"\n      },\n      \"filename\": \"requests-2.19.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"421cfc8d9dde7d6aff68196420afd86b88c65d77d8da9cf83f4ecad785d7b9d6\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 91865,\n      \"upload-time\": \"2018-06-12T14:46:15.289074Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cc/15/e1c318dbc20032ffbe5628837ca0de2d5b116ffd1b849c699634010f6a5d/requests-2.19.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"requests-2.19.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"cc408268d0e21589bcc2b2c248e42932b8c4d112f499c12c92e99e2178a6134c\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 130875,\n      \"upload-time\": \"2018-06-12T14:46:17.223245Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/75/27/82da3fa4ea7a8c3526c48eaafe427352ff9c931633b917c2251826a43697/requests-2.19.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"requests\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"2.18.0\",\n    \"2.18.1\",\n    \"2.18.2\",\n    \"2.18.3\",\n    \"2.18.4\",\n    \"2.19.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/setuptools/39.2.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Python Packaging Authority\",\n    \"author_email\": \"distutils-sig@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: System :: Archiving :: Packaging\",\n      \"Topic :: System :: Systems Administration\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst; charset=UTF-8\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/pypa/setuptools\",\n    \"keywords\": \"CPAN PyPI distutils eggs package management\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"setuptools\",\n    \"package_url\": \"https://pypi.org/project/setuptools/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/setuptools/\",\n    \"project_urls\": {\n      \"Documentation\": \"https://setuptools.readthedocs.io/\",\n      \"Homepage\": \"https://github.com/pypa/setuptools\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/setuptools/39.2.0/\",\n    \"requires_dist\": [\n      \"certifi (==2016.9.26); extra == 'certs'\",\n      \"wincertstore (==0.2); (sys_platform=='win32') and extra == 'ssl'\"\n    ],\n    \"requires_python\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*\",\n    \"summary\": \"Easily download, build, install, upgrade, and uninstall Python packages\",\n    \"version\": \"39.2.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"7fe1820d941153923aac1d49d7fc37e17b6e73bfbd2904959fffbad77900cf92\",\n        \"md5\": \"8d066d2201311ed30be535b473e32fed\",\n        \"sha256\": \"8fca9275c89964f13da985c3656cb00ba029d7f3916b37990927ffdf264e7926\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"setuptools-39.2.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8d066d2201311ed30be535b473e32fed\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*\",\n      \"size\": 567556,\n      \"upload_time\": \"2018-05-19T19:19:22\",\n      \"upload_time_iso_8601\": \"2018-05-19T19:19:22.625819Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7f/e1/820d941153923aac1d49d7fc37e17b6e73bfbd2904959fffbad77900cf92/setuptools-39.2.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"1a04d6f1159feaccdfc508517dba1929eb93a2854de729fa68da9d5c6b48fa00\",\n        \"md5\": \"dd4e3fa83a21bf7bf9c51026dc8a4e59\",\n        \"sha256\": \"f7cddbb5f5c640311eb00eab6e849f7701fa70bf6a183fc8a2c33dd1d1672fb2\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"setuptools-39.2.0.zip\",\n      \"has_sig\": false,\n      \"md5_digest\": \"dd4e3fa83a21bf7bf9c51026dc8a4e59\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*\",\n      \"size\": 851112,\n      \"upload_time\": \"2018-05-19T19:19:24\",\n      \"upload_time_iso_8601\": \"2018-05-19T19:19:24.480740Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1a/04/d6f1159feaccdfc508517dba1929eb93a2854de729fa68da9d5c6b48fa00/setuptools-39.2.0.zip\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/setuptools/67.6.1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Python Packaging Authority\",\n    \"author_email\": \"distutils-sig@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Topic :: Software Development :: Libraries :: Python Modules\",\n      \"Topic :: System :: Archiving :: Packaging\",\n      \"Topic :: System :: Systems Administration\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/pypa/setuptools\",\n    \"keywords\": \"CPAN PyPI distutils eggs package management\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"setuptools\",\n    \"package_url\": \"https://pypi.org/project/setuptools/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/setuptools/\",\n    \"project_urls\": {\n      \"Changelog\": \"https://setuptools.pypa.io/en/stable/history.html\",\n      \"Documentation\": \"https://setuptools.pypa.io/\",\n      \"Homepage\": \"https://github.com/pypa/setuptools\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/setuptools/67.6.1/\",\n    \"requires_dist\": [\n      \"sphinx (>=3.5) ; extra == 'docs'\",\n      \"jaraco.packaging (>=9) ; extra == 'docs'\",\n      \"rst.linker (>=1.9) ; extra == 'docs'\",\n      \"furo ; extra == 'docs'\",\n      \"sphinx-lint ; extra == 'docs'\",\n      \"jaraco.tidelift (>=1.4) ; extra == 'docs'\",\n      \"pygments-github-lexers (==0.0.5) ; extra == 'docs'\",\n      \"sphinx-favicon ; extra == 'docs'\",\n      \"sphinx-inline-tabs ; extra == 'docs'\",\n      \"sphinx-reredirects ; extra == 'docs'\",\n      \"sphinxcontrib-towncrier ; extra == 'docs'\",\n      \"sphinx-notfound-page (==0.8.3) ; extra == 'docs'\",\n      \"sphinx-hoverxref (<2) ; extra == 'docs'\",\n      \"pytest (>=6) ; extra == 'testing'\",\n      \"pytest-checkdocs (>=2.4) ; extra == 'testing'\",\n      \"flake8 (<5) ; extra == 'testing'\",\n      \"pytest-enabler (>=1.3) ; extra == 'testing'\",\n      \"pytest-perf ; extra == 'testing'\",\n      \"flake8-2020 ; extra == 'testing'\",\n      \"virtualenv (>=13.0.0) ; extra == 'testing'\",\n      \"wheel ; extra == 'testing'\",\n      \"pip (>=19.1) ; extra == 'testing'\",\n      \"jaraco.envs (>=2.2) ; extra == 'testing'\",\n      \"pytest-xdist ; extra == 'testing'\",\n      \"jaraco.path (>=3.2.0) ; extra == 'testing'\",\n      \"build[virtualenv] ; extra == 'testing'\",\n      \"filelock (>=3.4.0) ; extra == 'testing'\",\n      \"pip-run (>=8.8) ; extra == 'testing'\",\n      \"ini2toml[lite] (>=0.9) ; extra == 'testing'\",\n      \"tomli-w (>=1.0.0) ; extra == 'testing'\",\n      \"pytest-timeout ; extra == 'testing'\",\n      \"pytest ; extra == 'testing-integration'\",\n      \"pytest-xdist ; extra == 'testing-integration'\",\n      \"pytest-enabler ; extra == 'testing-integration'\",\n      \"virtualenv (>=13.0.0) ; extra == 'testing-integration'\",\n      \"tomli ; extra == 'testing-integration'\",\n      \"wheel ; extra == 'testing-integration'\",\n      \"jaraco.path (>=3.2.0) ; extra == 'testing-integration'\",\n      \"jaraco.envs (>=2.2) ; extra == 'testing-integration'\",\n      \"build[virtualenv] ; extra == 'testing-integration'\",\n      \"filelock (>=3.4.0) ; extra == 'testing-integration'\",\n      \"pytest-black (>=0.3.7) ; (platform_python_implementation != \\\"PyPy\\\") and extra == 'testing'\",\n      \"pytest-cov ; (platform_python_implementation != \\\"PyPy\\\") and extra == 'testing'\",\n      \"pytest-mypy (>=0.9.1) ; (platform_python_implementation != \\\"PyPy\\\") and extra == 'testing'\",\n      \"pytest-flake8 ; (python_version < \\\"3.12\\\") and extra == 'testing'\"\n    ],\n    \"requires_python\": \">=3.7\",\n    \"summary\": \"Easily download, build, install, upgrade, and uninstall Python packages\",\n    \"version\": \"67.6.1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"3b5b846e000da033d54eeaaf7915126e\",\n        \"sha256\": \"e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"setuptools-67.6.1-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"3b5b846e000da033d54eeaaf7915126e\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 1089263,\n      \"upload_time\": \"2023-03-28T13:45:43\",\n      \"upload_time_iso_8601\": \"2023-03-28T13:45:43.525946Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/0b/fc/8781442def77b0aa22f63f266d4dadd486ebc0c5371d6290caf4320da4b7/setuptools-67.6.1-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"ee2562f783544d1f95022c906dd3cf98\",\n        \"sha256\": \"a737d365c957dd3fced9ddd246118e95dce7a62c3dc49f37e7fdd9e93475d785\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"setuptools-67.6.1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"ee2562f783544d1f95022c906dd3cf98\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 2486256,\n      \"upload_time\": \"2023-03-28T13:45:45\",\n      \"upload_time_iso_8601\": \"2023-03-28T13:45:45.967259Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cb/46/22ec35f286a77e6b94adf81b4f0d59f402ed981d4251df0ba7b992299146/setuptools-67.6.1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/setuptools.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"73f25249f8930d4b8f842a9e71ed57cf898d19b28beac684454b9c8a1c0cc2d8\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"73f25249f8930d4b8f842a9e71ed57cf898d19b28beac684454b9c8a1c0cc2d8\"\n      },\n      \"filename\": \"setuptools-39.2.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"8fca9275c89964f13da985c3656cb00ba029d7f3916b37990927ffdf264e7926\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*\",\n      \"size\": 567556,\n      \"upload-time\": \"2018-05-19T19:19:22.625819Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/7f/e1/820d941153923aac1d49d7fc37e17b6e73bfbd2904959fffbad77900cf92/setuptools-39.2.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"setuptools-39.2.0.zip\",\n      \"hashes\": {\n        \"sha256\": \"f7cddbb5f5c640311eb00eab6e849f7701fa70bf6a183fc8a2c33dd1d1672fb2\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7,!=3.0.*,!=3.1.*,!=3.2.*\",\n      \"size\": 851112,\n      \"upload-time\": \"2018-05-19T19:19:24.480740Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/1a/04/d6f1159feaccdfc508517dba1929eb93a2854de729fa68da9d5c6b48fa00/setuptools-39.2.0.zip\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4cf21fee4bb46056adb125243cf6d0f5e1efafab06fae67223b43631dbd81cb6\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4cf21fee4bb46056adb125243cf6d0f5e1efafab06fae67223b43631dbd81cb6\"\n      },\n      \"filename\": \"setuptools-67.6.1-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"3b5b846e000da033d54eeaaf7915126e\",\n        \"sha256\": \"e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 1089263,\n      \"upload-time\": \"2023-03-28T13:45:43.525946Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/0b/fc/8781442def77b0aa22f63f266d4dadd486ebc0c5371d6290caf4320da4b7/setuptools-67.6.1-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"setuptools-67.6.1.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"ee2562f783544d1f95022c906dd3cf98\",\n        \"sha256\": \"a737d365c957dd3fced9ddd246118e95dce7a62c3dc49f37e7fdd9e93475d785\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 2486256,\n      \"upload-time\": \"2023-03-28T13:45:45.967259Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/cb/46/22ec35f286a77e6b94adf81b4f0d59f402ed981d4251df0ba7b992299146/setuptools-67.6.1.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"setuptools\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"39.2.0\",\n    \"67.6.1\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/six/1.11.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Benjamin Peterson\",\n    \"author_email\": \"benjamin@python.org\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 3\",\n      \"Topic :: Software Development :: Libraries\",\n      \"Topic :: Utilities\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pypi.python.org/pypi/six/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"six\",\n    \"package_url\": \"https://pypi.org/project/six/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/six/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pypi.python.org/pypi/six/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/six/1.11.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Python 2 and 3 compatibility utilities\",\n    \"version\": \"1.11.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"9500094701f7201ddd065c60abcefef1\",\n        \"sha256\": \"534e9875e44a507adec601c29b3cbd2ca6dae7df92bf3dd20c7289b2f99f7466\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"six-1.11.0-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"9500094701f7201ddd065c60abcefef1\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": null,\n      \"size\": 10702,\n      \"upload_time\": \"2017-09-17T18:46:53\",\n      \"upload_time_iso_8601\": \"2017-09-17T18:46:53.702194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"25d3568604f921dd23532b88a0ce17e7\",\n        \"sha256\": \"268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"six-1.11.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"25d3568604f921dd23532b88a0ce17e7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 29860,\n      \"upload_time\": \"2017-09-17T18:46:54\",\n      \"upload_time_iso_8601\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/six.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"d8e61e1b2a777206b74022118a9dd3410b5bee46bd65366c521ae4c5246c2019\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"d8e61e1b2a777206b74022118a9dd3410b5bee46bd65366c521ae4c5246c2019\"\n      },\n      \"filename\": \"six-1.11.0-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"9500094701f7201ddd065c60abcefef1\",\n        \"sha256\": \"534e9875e44a507adec601c29b3cbd2ca6dae7df92bf3dd20c7289b2f99f7466\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 10702,\n      \"upload-time\": \"2017-09-17T18:46:53.702194Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"six-1.11.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"25d3568604f921dd23532b88a0ce17e7\",\n        \"sha256\": \"268a4ccb159c1a2d2c79336b02e75058387b0cdbb4cea2f07846a758f48a356d\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 29860,\n      \"upload-time\": \"2017-09-17T18:46:54.492027Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"six\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.11.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/sqlalchemy/1.2.12.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Mike Bayer\",\n    \"author_email\": \"mike_mp@zzzcomputing.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: Implementation :: CPython\",\n      \"Programming Language :: Python :: Implementation :: PyPy\",\n      \"Topic :: Database :: Front-Ends\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://www.sqlalchemy.org\",\n    \"keywords\": \"\",\n    \"license\": \"MIT License\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"SQLAlchemy\",\n    \"package_url\": \"https://pypi.org/project/SQLAlchemy/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/SQLAlchemy/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://www.sqlalchemy.org\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/SQLAlchemy/1.2.12/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"Database Abstraction Library\",\n    \"version\": \"1.2.12\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"4a2617b5254748828d09349fc4eff6bd\",\n        \"sha256\": \"b5a127599b3f27847fba6119de0fcb70832a8041b103701a708b7c7d044faa38\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"SQLAlchemy-1.2.12.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"4a2617b5254748828d09349fc4eff6bd\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 5634807,\n      \"upload_time\": \"2018-09-19T18:14:55\",\n      \"upload_time_iso_8601\": \"2018-09-19T18:14:55.299706Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/sqlalchemy.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"SQLAlchemy-1.2.12.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"4a2617b5254748828d09349fc4eff6bd\",\n        \"sha256\": \"b5a127599b3f27847fba6119de0fcb70832a8041b103701a708b7c7d044faa38\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 5634807,\n      \"upload-time\": \"2018-09-19T18:14:55.299706Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"sqlalchemy\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"1.2.12\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/toga/0.3.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Russell Keith-Magee\",\n    \"author_email\": \"russell@keith-magee.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 3 - Alpha\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.12\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Software Development\",\n      \"Topic :: Software Development :: User Interfaces\",\n      \"Topic :: Software Development :: Widget Sets\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst; charset=UTF-8\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://beeware.org/project/projects/libraries/toga/\",\n    \"keywords\": \"gui,widget,cross-platform,desktop,mobile,web,macOS,cocoa,iOS,android,windows,winforms,linux,gtk\",\n    \"license\": \"New BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"BeeWare Team\",\n    \"maintainer_email\": \"team@beeware.org\",\n    \"name\": \"toga\",\n    \"package_url\": \"https://pypi.org/project/toga/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/toga/\",\n    \"project_urls\": {\n      \"Documentation\": \"http://toga.readthedocs.io/en/latest/\",\n      \"Funding\": \"https://beeware.org/contributing/membership/\",\n      \"Homepage\": \"https://beeware.org/project/projects/libraries/toga/\",\n      \"Source\": \"https://github.com/beeware/toga\",\n      \"Tracker\": \"https://github.com/beeware/toga/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/toga/0.3.0/\",\n    \"requires_dist\": [\n      \"toga-cocoa (==0.3.0) ; sys_platform==\\\"darwin\\\"\",\n      \"toga-gtk (==0.3.0) ; sys_platform==\\\"linux\\\"\",\n      \"toga-winforms (==0.3.0) ; sys_platform==\\\"win32\\\"\"\n    ],\n    \"requires_python\": \">=3.7\",\n    \"summary\": \"A Python native, OS native GUI toolkit.\",\n    \"version\": \"0.3.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"401df3f98e3811ef657aa2acabe039fcbbcae7fc3463f3e18a2458e099465a49\",\n        \"md5\": \"4c9f80d34e8d3f6a3cb301749fbca4ca\",\n        \"sha256\": \"846f1598d5ce91e44aefaac8abc9ba8c5a7c3bbd8c69d7004b92ef64228500ad\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"4c9f80d34e8d3f6a3cb301749fbca4ca\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 1945,\n      \"upload_time\": \"2023-01-30T03:29:31\",\n      \"upload_time_iso_8601\": \"2023-01-30T03:29:31.034500Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/40/1d/f3f98e3811ef657aa2acabe039fcbbcae7fc3463f3e18a2458e099465a49/toga-0.3.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"f3cf44b2d505e13b2ec0c7cef2677ac5d8bffb1fb22c4c1d3671179f8a52a40b\",\n        \"md5\": \"f6d6ddeffa5c08a875d436b2dc835e1d\",\n        \"sha256\": \"0d1f04d6b5773b8682e5eb1260ec9f07b43f950b89916f9406b2db33cde27240\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"f6d6ddeffa5c08a875d436b2dc835e1d\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 3236,\n      \"upload_time\": \"2023-01-30T03:29:34\",\n      \"upload_time_iso_8601\": \"2023-01-30T03:29:34.595566Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f3/cf/44b2d505e13b2ec0c7cef2677ac5d8bffb1fb22c4c1d3671179f8a52a40b/toga-0.3.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/toga/0.3.0dev1.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Russell Keith-Magee\",\n    \"author_email\": \"russell@keith-magee.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 3 - Alpha\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Topic :: Software Development\",\n      \"Topic :: Software Development :: User Interfaces\",\n      \"Topic :: Software Development :: Widget Sets\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pybee.org/toga\",\n    \"keywords\": \"\",\n    \"license\": \"New BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"toga\",\n    \"package_url\": \"https://pypi.org/project/toga/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/toga/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pybee.org/toga\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/toga/0.3.0.dev1/\",\n    \"requires_dist\": [\n      \"toga-cocoa; sys_platform==\\\"darwin\\\"\",\n      \"toga-gtk; sys_platform==\\\"linux\\\"\",\n      \"toga-winforms; sys_platform==\\\"win32\\\"\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"A Python native, OS native GUI toolkit.\",\n    \"version\": \"0.3.0.dev1\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"ebefea806c706d3dc90d4bc8c412c0ad3515fd018074f5fdd4bd020bdd4c0c80\",\n        \"md5\": \"7b219b2249b825f28051aae0230f2818\",\n        \"sha256\": \"8553bf332d8fbf39b500745ed9c4044a846fbba68e31de70e6fe83fdffcb0a9e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0.dev1-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"7b219b2249b825f28051aae0230f2818\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 4789,\n      \"upload_time\": \"2018-01-14T04:10:57\",\n      \"upload_time_iso_8601\": \"2018-01-14T04:10:57.825822Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/eb/ef/ea806c706d3dc90d4bc8c412c0ad3515fd018074f5fdd4bd020bdd4c0c80/toga-0.3.0.dev1-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"4f71c55c15950f7275e761fe53fb0dc83fe4f5fba6199d7b8fb05d741dd33566\",\n        \"md5\": \"21b8fff4d110ddfb8c3eb939d7b35a1e\",\n        \"sha256\": \"4e5c77056792168a4e84c84bb7214dfb614b79f289dcbe1525be614483496439\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0.dev1.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"21b8fff4d110ddfb8c3eb939d7b35a1e\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 40187,\n      \"upload_time\": \"2018-01-14T04:11:03\",\n      \"upload_time_iso_8601\": \"2018-01-14T04:11:03.212494Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/71/c55c15950f7275e761fe53fb0dc83fe4f5fba6199d7b8fb05d741dd33566/toga-0.3.0.dev1.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/toga/0.3.0dev2.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Russell Keith-Magee\",\n    \"author_email\": \"russell@keith-magee.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 3 - Alpha\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Topic :: Software Development\",\n      \"Topic :: Software Development :: User Interfaces\",\n      \"Topic :: Software Development :: Widget Sets\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": null,\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://pybee.org/toga\",\n    \"keywords\": \"\",\n    \"license\": \"New BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"toga\",\n    \"package_url\": \"https://pypi.org/project/toga/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/toga/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://pybee.org/toga\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/toga/0.3.0.dev2/\",\n    \"requires_dist\": [\n      \"toga-cocoa; sys_platform==\\\"darwin\\\"\",\n      \"toga-gtk; sys_platform==\\\"linux\\\"\",\n      \"toga-winforms; sys_platform==\\\"win32\\\"\"\n    ],\n    \"requires_python\": \"\",\n    \"summary\": \"A Python native, OS native GUI toolkit.\",\n    \"version\": \"0.3.0.dev2\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"3a68d1f6feb2ded26b9f6c36cd2a826e895e0fa6bba5fe489ec30b9f3bc1dbea\",\n        \"md5\": \"1aa1d5f48b81475569ea80ea04db8852\",\n        \"sha256\": \"6e0a2f800a351bbe8639802954d8d283a52b8cdde378541610ff2bfb3b24ad2f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0.dev2-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"1aa1d5f48b81475569ea80ea04db8852\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": null,\n      \"size\": 4788,\n      \"upload_time\": \"2018-01-14T04:52:03\",\n      \"upload_time_iso_8601\": \"2018-01-14T04:52:03.658804Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3a/68/d1f6feb2ded26b9f6c36cd2a826e895e0fa6bba5fe489ec30b9f3bc1dbea/toga-0.3.0.dev2-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"e36a3264b3d48733cac7546fee02fbc516621574252f7d86546255532b095415\",\n        \"md5\": \"a6558d0c5ba3cd763084e564001e9d24\",\n        \"sha256\": \"630d2f932bf7aba3a143d3a332190a46a0a3895f509099a20f033caadf131b76\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.3.0.dev2.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"a6558d0c5ba3cd763084e564001e9d24\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 40203,\n      \"upload_time\": \"2018-01-14T04:52:09\",\n      \"upload_time_iso_8601\": \"2018-01-14T04:52:09.347038Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e3/6a/3264b3d48733cac7546fee02fbc516621574252f7d86546255532b095415/toga-0.3.0.dev2.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/toga/0.4.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Russell Keith-Magee\",\n    \"author_email\": \"russell@keith-magee.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 4 - Beta\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: BSD License\",\n      \"Operating System :: OS Independent\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.12\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: Software Development\",\n      \"Topic :: Software Development :: User Interfaces\",\n      \"Topic :: Software Development :: Widget Sets\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst; charset=UTF-8\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://beeware.org/project/projects/libraries/toga/\",\n    \"keywords\": \"gui,widget,cross-platform,desktop,mobile,web,macOS,cocoa,iOS,android,windows,winforms,linux,freeBSD,gtk\",\n    \"license\": \"New BSD\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"BeeWare Team\",\n    \"maintainer_email\": \"team@beeware.org\",\n    \"name\": \"toga\",\n    \"package_url\": \"https://pypi.org/project/toga/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/toga/\",\n    \"project_urls\": {\n      \"Documentation\": \"http://toga.readthedocs.io/en/latest/\",\n      \"Funding\": \"https://beeware.org/contributing/membership/\",\n      \"Homepage\": \"https://beeware.org/project/projects/libraries/toga/\",\n      \"Source\": \"https://github.com/beeware/toga\",\n      \"Tracker\": \"https://github.com/beeware/toga/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/toga/0.4.0/\",\n    \"requires_dist\": [\n      \"toga-gtk ==0.4.0 ; \\\"freebsd\\\" in sys_platform\",\n      \"toga-cocoa ==0.4.0 ; sys_platform==\\\"darwin\\\"\",\n      \"toga-gtk ==0.4.0 ; sys_platform==\\\"linux\\\"\",\n      \"toga-winforms ==0.4.0 ; sys_platform==\\\"win32\\\"\"\n    ],\n    \"requires_python\": \">=3.8\",\n    \"summary\": \"A Python native, OS native GUI toolkit.\",\n    \"version\": \"0.4.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"365342de9ed8de0d256b785076ac7cf82eaa088f50df8ed2a8ab1011313e8f04\",\n        \"md5\": \"8a4f02f7c8a0353f3205801eadf3e3c9\",\n        \"sha256\": \"cd2e47bb19ad7dfe0447f1379d4a7e8d9849a25ab8db62d41358185dcb4e4636\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.4.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"8a4f02f7c8a0353f3205801eadf3e3c9\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.8\",\n      \"size\": 2270,\n      \"upload_time\": \"2023-11-03T04:09:37\",\n      \"upload_time_iso_8601\": \"2023-11-03T04:09:37.311582Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/36/53/42de9ed8de0d256b785076ac7cf82eaa088f50df8ed2a8ab1011313e8f04/toga-0.4.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"blake2b_256\": \"55607dc01b43f991228fd73679e40dc000e4c28a76b8b27b4d155e5bf716ff06\",\n        \"md5\": \"bd31edb40d9176b268250a6808b24d56\",\n        \"sha256\": \"5ced4a0c85399a52e7c2397e7326f787a04da748bc1e9fc37037bde8e1cf4d54\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"toga-0.4.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"bd31edb40d9176b268250a6808b24d56\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.8\",\n      \"size\": 3815,\n      \"upload_time\": \"2023-11-03T04:09:40\",\n      \"upload_time_iso_8601\": \"2023-11-03T04:09:40.090279Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/55/60/7dc01b43f991228fd73679e40dc000e4c28a76b8b27b4d155e5bf716ff06/toga-0.4.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/toga.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"01822b37a113268b35ff7f655cccc286926c96bff01a35b4884a116188e2fec3\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"01822b37a113268b35ff7f655cccc286926c96bff01a35b4884a116188e2fec3\"\n      },\n      \"filename\": \"toga-0.3.0.dev1-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"8553bf332d8fbf39b500745ed9c4044a846fbba68e31de70e6fe83fdffcb0a9e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4789,\n      \"upload-time\": \"2018-01-14T04:10:57.825822Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/eb/ef/ea806c706d3dc90d4bc8c412c0ad3515fd018074f5fdd4bd020bdd4c0c80/toga-0.3.0.dev1-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"toga-0.3.0.dev1.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"4e5c77056792168a4e84c84bb7214dfb614b79f289dcbe1525be614483496439\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 40187,\n      \"upload-time\": \"2018-01-14T04:11:03.212494Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/4f/71/c55c15950f7275e761fe53fb0dc83fe4f5fba6199d7b8fb05d741dd33566/toga-0.3.0.dev1.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"0d198a207170e3b5f2a7d20a8418efe297b77bb2ed098ece52c0366b2fc95de1\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"0d198a207170e3b5f2a7d20a8418efe297b77bb2ed098ece52c0366b2fc95de1\"\n      },\n      \"filename\": \"toga-0.3.0.dev2-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"6e0a2f800a351bbe8639802954d8d283a52b8cdde378541610ff2bfb3b24ad2f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 4788,\n      \"upload-time\": \"2018-01-14T04:52:03.658804Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3a/68/d1f6feb2ded26b9f6c36cd2a826e895e0fa6bba5fe489ec30b9f3bc1dbea/toga-0.3.0.dev2-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"toga-0.3.0.dev2.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"630d2f932bf7aba3a143d3a332190a46a0a3895f509099a20f033caadf131b76\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 40203,\n      \"upload-time\": \"2018-01-14T04:52:09.347038Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/e3/6a/3264b3d48733cac7546fee02fbc516621574252f7d86546255532b095415/toga-0.3.0.dev2.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bec0f52a3d8b11f7b4fb7c2a944b889b0021628a7de577a3eedbd683f812707a\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bec0f52a3d8b11f7b4fb7c2a944b889b0021628a7de577a3eedbd683f812707a\"\n      },\n      \"filename\": \"toga-0.3.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"846f1598d5ce91e44aefaac8abc9ba8c5a7c3bbd8c69d7004b92ef64228500ad\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 1945,\n      \"upload-time\": \"2023-01-30T03:29:31.034500Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/40/1d/f3f98e3811ef657aa2acabe039fcbbcae7fc3463f3e18a2458e099465a49/toga-0.3.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"toga-0.3.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"0d1f04d6b5773b8682e5eb1260ec9f07b43f950b89916f9406b2db33cde27240\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 3236,\n      \"upload-time\": \"2023-01-30T03:29:34.595566Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f3/cf/44b2d505e13b2ec0c7cef2677ac5d8bffb1fb22c4c1d3671179f8a52a40b/toga-0.3.0.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"fe89f5e92f7709b1db6d8d865ac7a0a80e09154c26a83e692af163b78a332392\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"fe89f5e92f7709b1db6d8d865ac7a0a80e09154c26a83e692af163b78a332392\"\n      },\n      \"filename\": \"toga-0.4.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"sha256\": \"cd2e47bb19ad7dfe0447f1379d4a7e8d9849a25ab8db62d41358185dcb4e4636\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.8\",\n      \"size\": 2270,\n      \"upload-time\": \"2023-11-03T04:09:37.311582Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/36/53/42de9ed8de0d256b785076ac7cf82eaa088f50df8ed2a8ab1011313e8f04/toga-0.4.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"toga-0.4.0.tar.gz\",\n      \"hashes\": {\n        \"sha256\": \"5ced4a0c85399a52e7c2397e7326f787a04da748bc1e9fc37037bde8e1cf4d54\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.8\",\n      \"size\": 3815,\n      \"upload-time\": \"2023-11-03T04:09:40.090279Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/55/60/7dc01b43f991228fd73679e40dc000e4c28a76b8b27b4d155e5bf716ff06/toga-0.4.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"toga\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.3.0\",\n    \"0.3.0dev1\",\n    \"0.3.0dev2\",\n    \"0.4.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/tomlkit/0.5.2.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/sdispater/tomlkit\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"Sébastien Eustace\",\n    \"maintainer_email\": \"sebastien@eustace.io\",\n    \"name\": \"tomlkit\",\n    \"package_url\": \"https://pypi.org/project/tomlkit/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/tomlkit/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/sdispater/tomlkit\",\n      \"Repository\": \"https://github.com/sdispater/tomlkit\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/tomlkit/0.5.2/\",\n    \"requires_dist\": [\n      \"enum34 (>=1.1,<2.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\"\",\n      \"functools32 (>=3.2.3,<4.0.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\"\",\n      \"typing (>=3.6,<4.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\" or python_version >= \\\"3.4\\\" and python_version < \\\"3.5\\\"\"\n    ],\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"Style preserving TOML library\",\n    \"version\": \"0.5.2\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"4045c5f6848fbc93c38df2296a441f07\",\n        \"sha256\": \"dea8ff39e9e2170f1b2f465520482eec71e7909cfff53dcb076b585d50f8ccc8\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"tomlkit-0.5.2-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"4045c5f6848fbc93c38df2296a441f07\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 116499,\n      \"upload_time\": \"2018-11-09T17:09:28\",\n      \"upload_time_iso_8601\": \"2018-11-09T17:09:28.212157Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/ca/8b60a94c01ee655ffb81d11c11396cb6fff89459317aa1fe3e98ee80f055/tomlkit-0.5.2-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"7c31987ef6fba2cd64715cae27fade64\",\n        \"sha256\": \"4a226ccf11ee5a2e76bfc185747b54ee7718706aeb3aabb981327249dbe2b1d4\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"tomlkit-0.5.2.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"7c31987ef6fba2cd64715cae27fade64\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 29813,\n      \"upload_time\": \"2018-11-09T17:09:29\",\n      \"upload_time_iso_8601\": \"2018-11-09T17:09:29.709061Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f6/8c/c27d292cf7c0f04f0e1b5c75ab95dc328542ccbe9a809a1eada66c897bd2/tomlkit-0.5.2.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/tomlkit/0.5.3.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Sébastien Eustace\",\n    \"author_email\": \"sebastien@eustace.io\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 2\",\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/markdown\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/sdispater/tomlkit\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"Sébastien Eustace\",\n    \"maintainer_email\": \"sebastien@eustace.io\",\n    \"name\": \"tomlkit\",\n    \"package_url\": \"https://pypi.org/project/tomlkit/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/tomlkit/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/sdispater/tomlkit\",\n      \"Repository\": \"https://github.com/sdispater/tomlkit\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/tomlkit/0.5.3/\",\n    \"requires_dist\": [\n      \"enum34 (>=1.1,<2.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\"\",\n      \"functools32 (>=3.2.3,<4.0.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\"\",\n      \"typing (>=3.6,<4.0); python_version >= \\\"2.7\\\" and python_version < \\\"2.8\\\" or python_version >= \\\"3.4\\\" and python_version < \\\"3.5\\\"\"\n    ],\n    \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n    \"summary\": \"Style preserving TOML library\",\n    \"version\": \"0.5.3\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"3a90c70a5067d5727110838094ab8674\",\n        \"sha256\": \"35f06da5835e85f149a4701d43e730adcc09f1b362e5fc2300d77bdd26280908\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"tomlkit-0.5.3-py2.py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"3a90c70a5067d5727110838094ab8674\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py2.py3\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 116796,\n      \"upload_time\": \"2018-11-19T20:05:37\",\n      \"upload_time_iso_8601\": \"2018-11-19T20:05:37.276181Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/71/c6/06c014b92cc48270765d6a9418d82239b158d8a9b69e031b0e2c6598740b/tomlkit-0.5.3-py2.py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"cdbdc302a184d1f1e38d5e0810e3b212\",\n        \"sha256\": \"e2f785651609492c771d9887ccb2369d891d16595d2d97972e2cbe5e8fb3439f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"tomlkit-0.5.3.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"cdbdc302a184d1f1e38d5e0810e3b212\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 29864,\n      \"upload_time\": \"2018-11-19T20:05:39\",\n      \"upload_time_iso_8601\": \"2018-11-19T20:05:39.200001Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f7/f7/bbd9213bfe76cb7821c897f9ed74877fd74993b4ca2fe9513eb5a31030f9/tomlkit-0.5.3.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/tomlkit.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"4997b3d6f4b23bbd12d75af591167d982af7a862e473b2fbce20c8172ec93d93\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"4997b3d6f4b23bbd12d75af591167d982af7a862e473b2fbce20c8172ec93d93\"\n      },\n      \"filename\": \"tomlkit-0.5.2-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"4045c5f6848fbc93c38df2296a441f07\",\n        \"sha256\": \"dea8ff39e9e2170f1b2f465520482eec71e7909cfff53dcb076b585d50f8ccc8\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 116499,\n      \"upload-time\": \"2018-11-09T17:09:28.212157Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/9b/ca/8b60a94c01ee655ffb81d11c11396cb6fff89459317aa1fe3e98ee80f055/tomlkit-0.5.2-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"tomlkit-0.5.2.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"7c31987ef6fba2cd64715cae27fade64\",\n        \"sha256\": \"4a226ccf11ee5a2e76bfc185747b54ee7718706aeb3aabb981327249dbe2b1d4\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 29813,\n      \"upload-time\": \"2018-11-09T17:09:29.709061Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f6/8c/c27d292cf7c0f04f0e1b5c75ab95dc328542ccbe9a809a1eada66c897bd2/tomlkit-0.5.2.tar.gz\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": {\n        \"sha256\": \"729d3b14964e2455c0082529dc24dce1c6c84dc74f76d14b5ce7a0f044b12bbe\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"729d3b14964e2455c0082529dc24dce1c6c84dc74f76d14b5ce7a0f044b12bbe\"\n      },\n      \"filename\": \"tomlkit-0.5.3-py2.py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"3a90c70a5067d5727110838094ab8674\",\n        \"sha256\": \"35f06da5835e85f149a4701d43e730adcc09f1b362e5fc2300d77bdd26280908\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 116796,\n      \"upload-time\": \"2018-11-19T20:05:37.276181Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/71/c6/06c014b92cc48270765d6a9418d82239b158d8a9b69e031b0e2c6598740b/tomlkit-0.5.3-py2.py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"tomlkit-0.5.3.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"cdbdc302a184d1f1e38d5e0810e3b212\",\n        \"sha256\": \"e2f785651609492c771d9887ccb2369d891d16595d2d97972e2cbe5e8fb3439f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\",\n      \"size\": 29864,\n      \"upload-time\": \"2018-11-19T20:05:39.200001Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/f7/f7/bbd9213bfe76cb7821c897f9ed74877fd74993b4ca2fe9513eb5a31030f9/tomlkit-0.5.3.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"tomlkit\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.5.2\",\n    \"0.5.3\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/twisted/18.9.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Twisted Matrix Laboratories\",\n    \"author_email\": \"twisted-python@twistedmatrix.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Programming Language :: Python :: 2.7\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3.4\",\n      \"Programming Language :: Python :: 3.5\",\n      \"Programming Language :: Python :: 3.6\",\n      \"Programming Language :: Python :: 3.7\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"http://twistedmatrix.com/\",\n    \"keywords\": \"\",\n    \"license\": \"MIT\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"Glyph Lefkowitz\",\n    \"maintainer_email\": \"glyph@twistedmatrix.com\",\n    \"name\": \"Twisted\",\n    \"package_url\": \"https://pypi.org/project/Twisted/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/Twisted/\",\n    \"project_urls\": {\n      \"Homepage\": \"http://twistedmatrix.com/\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/Twisted/18.9.0/\",\n    \"requires_dist\": null,\n    \"requires_python\": \"\",\n    \"summary\": \"An asynchronous networking framework written in Python\",\n    \"version\": \"18.9.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"35ff4705ea90a76bf972ff3b229546ca\",\n        \"sha256\": \"4335327da58be11dd6e482ec6b85eb055bcc953a9570cd59e7840a2ce9419a8e\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"Twisted-18.9.0.tar.bz2\",\n      \"has_sig\": false,\n      \"md5_digest\": \"35ff4705ea90a76bf972ff3b229546ca\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": null,\n      \"size\": 3088398,\n      \"upload_time\": \"2018-10-15T09:11:22\",\n      \"upload_time_iso_8601\": \"2018-10-15T09:11:22.298247Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/twisted.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"Twisted-18.9.0.tar.bz2\",\n      \"hashes\": {\n        \"md5\": \"35ff4705ea90a76bf972ff3b229546ca\",\n        \"sha256\": \"4335327da58be11dd6e482ec6b85eb055bcc953a9570cd59e7840a2ce9419a8e\"\n      },\n      \"provenance\": null,\n      \"requires-python\": null,\n      \"size\": 3088398,\n      \"upload-time\": \"2018-10-15T09:11:22.298247Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"twisted\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"18.9.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/wheel/0.40.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"\",\n    \"author_email\": \"Daniel Holth <dholth@fastmail.fm>\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python\",\n      \"Programming Language :: Python :: 3 :: Only\",\n      \"Programming Language :: Python :: 3.10\",\n      \"Programming Language :: Python :: 3.11\",\n      \"Programming Language :: Python :: 3.7\",\n      \"Programming Language :: Python :: 3.8\",\n      \"Programming Language :: Python :: 3.9\",\n      \"Topic :: System :: Archiving :: Packaging\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"text/x-rst\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"\",\n    \"keywords\": \"wheel,packaging\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"Alex Grönholm <alex.gronholm@nextday.fi>\",\n    \"name\": \"wheel\",\n    \"package_url\": \"https://pypi.org/project/wheel/\",\n    \"platform\": null,\n    \"project_url\": \"https://pypi.org/project/wheel/\",\n    \"project_urls\": {\n      \"Changelog\": \"https://wheel.readthedocs.io/en/stable/news.html\",\n      \"Documentation\": \"https://wheel.readthedocs.io/\",\n      \"Issue Tracker\": \"https://github.com/pypa/wheel/issues\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/wheel/0.40.0/\",\n    \"requires_dist\": [\n      \"pytest >= 6.0.0 ; extra == \\\"test\\\"\"\n    ],\n    \"requires_python\": \">=3.7\",\n    \"summary\": \"A built-package format for Python\",\n    \"version\": \"0.40.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"517d39f133bd7b1ff17caf09784b7543\",\n        \"sha256\": \"d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"wheel-0.40.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"517d39f133bd7b1ff17caf09784b7543\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 64545,\n      \"upload_time\": \"2023-03-14T15:10:00\",\n      \"upload_time_iso_8601\": \"2023-03-14T15:10:00.828550Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/61/86/cc8d1ff2ca31a312a25a708c891cf9facbad4eae493b3872638db6785eb5/wheel-0.40.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"5f175a8d693f74878964d4fd29729ab7\",\n        \"sha256\": \"5cb7e75751aa82e1b7db3fd52f5a9d59e7b06905630bed135793295931528740\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"wheel-0.40.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"5f175a8d693f74878964d4fd29729ab7\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.7\",\n      \"size\": 96226,\n      \"upload_time\": \"2023-03-14T15:10:02\",\n      \"upload_time_iso_8601\": \"2023-03-14T15:10:02.873691Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fc/ef/0335f7217dd1e8096a9e8383e1d472aa14717878ffe07c4772e68b6e8735/wheel-0.40.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/wheel.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"bf7a632f73a2d57bb54d06bbb2a46c162e6dfbe7565b0746e588ce6f080e49f7\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"bf7a632f73a2d57bb54d06bbb2a46c162e6dfbe7565b0746e588ce6f080e49f7\"\n      },\n      \"filename\": \"wheel-0.40.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"517d39f133bd7b1ff17caf09784b7543\",\n        \"sha256\": \"d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 64545,\n      \"upload-time\": \"2023-03-14T15:10:00.828550Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/61/86/cc8d1ff2ca31a312a25a708c891cf9facbad4eae493b3872638db6785eb5/wheel-0.40.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"wheel-0.40.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"5f175a8d693f74878964d4fd29729ab7\",\n        \"sha256\": \"5cb7e75751aa82e1b7db3fd52f5a9d59e7b06905630bed135793295931528740\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.7\",\n      \"size\": 96226,\n      \"upload-time\": \"2023-03-14T15:10:02.873691Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/fc/ef/0335f7217dd1e8096a9e8383e1d472aa14717878ffe07c4772e68b6e8735/wheel-0.40.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"wheel\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"0.40.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/zipp/3.5.0.json",
    "content": "{\n  \"info\": {\n    \"author\": \"Jason R. Coombs\",\n    \"author_email\": \"jaraco@jaraco.com\",\n    \"bugtrack_url\": null,\n    \"classifiers\": [\n      \"Development Status :: 5 - Production/Stable\",\n      \"Intended Audience :: Developers\",\n      \"License :: OSI Approved :: MIT License\",\n      \"Programming Language :: Python :: 3\",\n      \"Programming Language :: Python :: 3 :: Only\"\n    ],\n    \"description\": \"\",\n    \"description_content_type\": \"\",\n    \"docs_url\": null,\n    \"download_url\": \"\",\n    \"downloads\": {\n      \"last_day\": -1,\n      \"last_month\": -1,\n      \"last_week\": -1\n    },\n    \"dynamic\": null,\n    \"home_page\": \"https://github.com/jaraco/zipp\",\n    \"keywords\": \"\",\n    \"license\": \"\",\n    \"license_expression\": null,\n    \"license_files\": null,\n    \"maintainer\": \"\",\n    \"maintainer_email\": \"\",\n    \"name\": \"zipp\",\n    \"package_url\": \"https://pypi.org/project/zipp/\",\n    \"platform\": \"\",\n    \"project_url\": \"https://pypi.org/project/zipp/\",\n    \"project_urls\": {\n      \"Homepage\": \"https://github.com/jaraco/zipp\"\n    },\n    \"provides_extra\": null,\n    \"release_url\": \"https://pypi.org/project/zipp/3.5.0/\",\n    \"requires_dist\": [\n      \"sphinx ; extra == 'docs'\",\n      \"jaraco.packaging (>=8.2) ; extra == 'docs'\",\n      \"rst.linker (>=1.9) ; extra == 'docs'\",\n      \"pytest (>=4.6) ; extra == 'testing'\",\n      \"pytest-checkdocs (>=2.4) ; extra == 'testing'\",\n      \"pytest-flake8 ; extra == 'testing'\",\n      \"pytest-cov ; extra == 'testing'\",\n      \"pytest-enabler (>=1.0.1) ; extra == 'testing'\",\n      \"jaraco.itertools ; extra == 'testing'\",\n      \"func-timeout ; extra == 'testing'\",\n      \"pytest-black (>=0.3.7) ; (platform_python_implementation != \\\"PyPy\\\" and python_version < \\\"3.10\\\") and extra == 'testing'\",\n      \"pytest-mypy ; (platform_python_implementation != \\\"PyPy\\\" and python_version < \\\"3.10\\\") and extra == 'testing'\"\n    ],\n    \"requires_python\": \">=3.6\",\n    \"summary\": \"Backport of pathlib-compatible object wrapper for zip files\",\n    \"version\": \"3.5.0\",\n    \"yanked\": false,\n    \"yanked_reason\": null\n  },\n  \"last_serial\": 0,\n  \"urls\": [\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"da62cbd850ba32ba93817aab0f03a855\",\n        \"sha256\": \"ec508cd5a3ed3d126293cafb34611469f2aef7342f575c3b6e072b995dc9da1f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"zipp-3.5.0-py3-none-any.whl\",\n      \"has_sig\": false,\n      \"md5_digest\": \"da62cbd850ba32ba93817aab0f03a855\",\n      \"packagetype\": \"bdist_wheel\",\n      \"python_version\": \"py3\",\n      \"requires_python\": \">=3.6\",\n      \"size\": 5700,\n      \"upload_time\": \"2021-07-02T23:51:45\",\n      \"upload_time_iso_8601\": \"2021-07-02T23:51:45.759726Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/92/d9/89f433969fb8dc5b9cbdd4b4deb587720ec1aeb59a020cf15002b9593eef/zipp-3.5.0-py3-none-any.whl\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    },\n    {\n      \"comment_text\": \"\",\n      \"digests\": {\n        \"md5\": \"16bf2a24fae340052e8565c264d21092\",\n        \"sha256\": \"239d50954a15aa4b283023f18dc451ba811fb4d263f4dd6855642e4d1c80cc9f\"\n      },\n      \"downloads\": -1,\n      \"filename\": \"zipp-3.5.0.tar.gz\",\n      \"has_sig\": false,\n      \"md5_digest\": \"16bf2a24fae340052e8565c264d21092\",\n      \"packagetype\": \"sdist\",\n      \"python_version\": \"source\",\n      \"requires_python\": \">=3.6\",\n      \"size\": 13270,\n      \"upload_time\": \"2021-07-02T23:51:47\",\n      \"upload_time_iso_8601\": \"2021-07-02T23:51:47.004396Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3a/9f/1d4b62cbe8d222539a84089eeab603d8e45ee1f897803a0ae0860400d6e7/zipp-3.5.0.tar.gz\",\n      \"yanked\": false,\n      \"yanked_reason\": null\n    }\n  ],\n  \"vulnerabilities\": []\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/json/zipp.json",
    "content": "{\n  \"alternate-locations\": [],\n  \"files\": [\n    {\n      \"core-metadata\": {\n        \"sha256\": \"03c98c88c78ef3602e6fd904a321359fa0e374bee5d9e5bf3b1fbdecc3c8a92d\"\n      },\n      \"data-dist-info-metadata\": {\n        \"sha256\": \"03c98c88c78ef3602e6fd904a321359fa0e374bee5d9e5bf3b1fbdecc3c8a92d\"\n      },\n      \"filename\": \"zipp-3.5.0-py3-none-any.whl\",\n      \"hashes\": {\n        \"md5\": \"da62cbd850ba32ba93817aab0f03a855\",\n        \"sha256\": \"ec508cd5a3ed3d126293cafb34611469f2aef7342f575c3b6e072b995dc9da1f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6\",\n      \"size\": 5700,\n      \"upload-time\": \"2021-07-02T23:51:45.759726Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/92/d9/89f433969fb8dc5b9cbdd4b4deb587720ec1aeb59a020cf15002b9593eef/zipp-3.5.0-py3-none-any.whl\",\n      \"yanked\": false\n    },\n    {\n      \"core-metadata\": false,\n      \"data-dist-info-metadata\": false,\n      \"filename\": \"zipp-3.5.0.tar.gz\",\n      \"hashes\": {\n        \"md5\": \"16bf2a24fae340052e8565c264d21092\",\n        \"sha256\": \"239d50954a15aa4b283023f18dc451ba811fb4d263f4dd6855642e4d1c80cc9f\"\n      },\n      \"provenance\": null,\n      \"requires-python\": \">=3.6\",\n      \"size\": 13270,\n      \"upload-time\": \"2021-07-02T23:51:47.004396Z\",\n      \"url\": \"https://files.pythonhosted.org/packages/3a/9f/1d4b62cbe8d222539a84089eeab603d8e45ee1f897803a0ae0860400d6e7/zipp-3.5.0.tar.gz\",\n      \"yanked\": false\n    }\n  ],\n  \"meta\": {\n    \"_last-serial\": 0,\n    \"api-version\": \"1.4\"\n  },\n  \"name\": \"zipp\",\n  \"project-status\": {\n    \"status\": \"active\"\n  },\n  \"versions\": [\n    \"3.5.0\"\n  ]\n}\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp27-cp27m-win32.whl.metadata",
    "content": "Metadata-Version: 2.1\r\nName: PyYAML\r\nVersion: 3.13\r\nSummary: YAML parser and emitter for Python\r\nHome-page: http://pyyaml.org/wiki/PyYAML\r\nAuthor: Kirill Simonov\r\nAuthor-email: xi@resolvent.net\r\nLicense: MIT\r\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\r\nPlatform: Any\r\nClassifier: Development Status :: 5 - Production/Stable\r\nClassifier: Intended Audience :: Developers\r\nClassifier: License :: OSI Approved :: MIT License\r\nClassifier: Operating System :: OS Independent\r\nClassifier: Programming Language :: Python\r\nClassifier: Programming Language :: Python :: 2\r\nClassifier: Programming Language :: Python :: 2.7\r\nClassifier: Programming Language :: Python :: 3\r\nClassifier: Programming Language :: Python :: 3.4\r\nClassifier: Programming Language :: Python :: 3.5\r\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\r\nClassifier: Topic :: Text Processing :: Markup\r\n\r\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp27-cp27m-win_amd64.whl.metadata",
    "content": "Metadata-Version: 2.1\r\nName: PyYAML\r\nVersion: 3.13\r\nSummary: YAML parser and emitter for Python\r\nHome-page: http://pyyaml.org/wiki/PyYAML\r\nAuthor: Kirill Simonov\r\nAuthor-email: xi@resolvent.net\r\nLicense: MIT\r\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\r\nPlatform: Any\r\nClassifier: Development Status :: 5 - Production/Stable\r\nClassifier: Intended Audience :: Developers\r\nClassifier: License :: OSI Approved :: MIT License\r\nClassifier: Operating System :: OS Independent\r\nClassifier: Programming Language :: Python\r\nClassifier: Programming Language :: Python :: 2\r\nClassifier: Programming Language :: Python :: 2.7\r\nClassifier: Programming Language :: Python :: 3\r\nClassifier: Programming Language :: Python :: 3.4\r\nClassifier: Programming Language :: Python :: 3.5\r\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\r\nClassifier: Topic :: Text Processing :: Markup\r\n\r\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp34-cp34m-win32.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp34-cp34m-win_amd64.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp35-cp35m-win32.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp35-cp35m-win_amd64.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp36-cp36m-win32.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp36-cp36m-win_amd64.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp37-cp37m-win32.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/PyYAML-3.13-cp37-cp37m-win_amd64.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: PyYAML\nVersion: 3.13\nSummary: YAML parser and emitter for Python\nHome-page: http://pyyaml.org/wiki/PyYAML\nAuthor: Kirill Simonov\nAuthor-email: xi@resolvent.net\nLicense: MIT\nDownload-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.13.tar.gz\nPlatform: Any\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Text Processing :: Markup\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/attrs-17.4.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: attrs\nVersion: 17.4.0\nSummary: Classes Without Boilerplate\nHome-page: http://www.attrs.org/\nAuthor: Hynek Schlawack\nAuthor-email: hs@ox.cx\nLicense: MIT\nDescription-Content-Type: UNKNOWN\nKeywords: class,attribute,boilerplate\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nProvides-Extra: dev\nRequires-Dist: coverage; extra == 'dev'\nRequires-Dist: hypothesis; extra == 'dev'\nRequires-Dist: pympler; extra == 'dev'\nRequires-Dist: pytest; extra == 'dev'\nRequires-Dist: six; extra == 'dev'\nRequires-Dist: zope.interface; extra == 'dev'\nRequires-Dist: sphinx; extra == 'dev'\nRequires-Dist: zope.interface; extra == 'dev'\nProvides-Extra: docs\nRequires-Dist: sphinx; extra == 'docs'\nRequires-Dist: zope.interface; extra == 'docs'\nProvides-Extra: tests\nRequires-Dist: coverage; extra == 'tests'\nRequires-Dist: hypothesis; extra == 'tests'\nRequires-Dist: pympler; extra == 'tests'\nRequires-Dist: pytest; extra == 'tests'\nRequires-Dist: six; extra == 'tests'\nRequires-Dist: zope.interface; extra == 'tests'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/black-19.10b0-py36-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: black\nVersion: 19.10b0\nSummary: The uncompromising code formatter.\nHome-page: https://github.com/psf/black\nAuthor: Łukasz Langa\nAuthor-email: lukasz@langa.pl\nLicense: MIT\nKeywords: automation formatter yapf autopep8 pyfmt gofmt rustfmt\nPlatform: UNKNOWN\nClassifier: Development Status :: 4 - Beta\nClassifier: Environment :: Console\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Software Development :: Quality Assurance\nRequires-Python: >=3.6\nDescription-Content-Type: text/markdown\nRequires-Dist: click (>=6.5)\nRequires-Dist: attrs (>=18.1.0)\nRequires-Dist: appdirs\nRequires-Dist: toml (>=0.9.4)\nRequires-Dist: typed-ast (>=1.4.0)\nRequires-Dist: regex\nRequires-Dist: pathspec (<1,>=0.6)\nProvides-Extra: d\nRequires-Dist: aiohttp (>=3.3.2) ; extra == 'd'\nRequires-Dist: aiohttp-cors ; extra == 'd'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/black-21.11b0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: black\nVersion: 21.11b0\nSummary: The uncompromising code formatter.\nHome-page: https://github.com/psf/black\nAuthor: Łukasz Langa\nAuthor-email: lukasz@langa.pl\nLicense: MIT\nProject-URL: Changelog, https://github.com/psf/black/blob/main/CHANGES.md\nKeywords: automation formatter yapf autopep8 pyfmt gofmt rustfmt\nPlatform: UNKNOWN\nClassifier: Development Status :: 4 - Beta\nClassifier: Environment :: Console\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Software Development :: Quality Assurance\nRequires-Python: >=3.6.2\nDescription-Content-Type: text/markdown\nLicense-File: LICENSE\nLicense-File: AUTHORS.md\nRequires-Dist: click (>=7.1.2)\nRequires-Dist: platformdirs (>=2)\nRequires-Dist: tomli (<2.0.0,>=0.2.6)\nRequires-Dist: regex (>=2020.1.8)\nRequires-Dist: pathspec (<1,>=0.9.0)\nRequires-Dist: typing-extensions (>=3.10.0.0)\nRequires-Dist: mypy-extensions (>=0.4.3)\nRequires-Dist: dataclasses (>=0.6) ; python_version < \"3.7\"\nRequires-Dist: typed-ast (>=1.4.2) ; python_version < \"3.8\" and implementation_name == \"cpython\"\nRequires-Dist: typing-extensions (!=3.10.0.1) ; python_version >= \"3.10\"\nProvides-Extra: colorama\nRequires-Dist: colorama (>=0.4.3) ; extra == 'colorama'\nProvides-Extra: d\nRequires-Dist: aiohttp (>=3.7.4) ; extra == 'd'\nProvides-Extra: jupyter\nRequires-Dist: ipython (>=7.8.0) ; extra == 'jupyter'\nRequires-Dist: tokenize-rt (>=3.2.0) ; extra == 'jupyter'\nProvides-Extra: python2\nRequires-Dist: typed-ast (>=1.4.3) ; extra == 'python2'\nProvides-Extra: uvloop\nRequires-Dist: uvloop (>=0.15.2) ; extra == 'uvloop'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/cleo-1.0.0a5-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: cleo\nVersion: 1.0.0a5\nSummary: Cleo allows you to create beautiful and testable command-line interfaces.\nHome-page: https://github.com/python-poetry/cleo\nLicense: MIT\nKeywords: cli,commands\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=3.7,<4.0\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nRequires-Dist: crashtest (>=0.3.1,<0.4.0)\nRequires-Dist: pylev (>=1.3.0,<2.0.0)\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/clikit-0.2.4-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: clikit\nVersion: 0.2.4\nSummary: CliKit is a group of utilities to build beautiful and testable command line interfaces.\nHome-page: https://github.com/sdispater/clikit\nLicense: MIT\nKeywords: packaging,dependency,poetry\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nRequires-Dist: enum34 (>=1.1,<2.0); python_version >= \"2.7\" and python_version < \"2.8\"\nRequires-Dist: pastel (>=0.1.0,<0.2.0)\nRequires-Dist: pylev (>=1.3,<2.0)\nRequires-Dist: typing (>=3.6,<4.0); python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\"\nProject-URL: Repository, https://github.com/sdispater/clikit\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/colorama-0.3.9-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: colorama\nVersion: 0.3.9\nSummary: Cross-platform colored terminal text.\nHome-page: https://github.com/tartley/colorama\nAuthor: Arnon Yaari\nAuthor-email: tartley@tartley.com\nLicense: BSD\nKeywords: color colour terminal text ansi windows crossplatform xplatform\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Environment :: Console\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.5\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.1\nClassifier: Programming Language :: Python :: 3.2\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Terminals\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/discord.py-2.0.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: discord.py\nVersion: 2.0.0\nSummary: A Python wrapper for the Discord API\nHome-page: https://github.com/Rapptz/discord.py\nAuthor: Rapptz\nLicense: MIT\nProject-URL: Documentation, https://discordpy.readthedocs.io/en/latest/\nProject-URL: Issue tracker, https://github.com/Rapptz/discord.py/issues\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Topic :: Internet\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: Utilities\nClassifier: Typing :: Typed\nRequires-Python: >=3.8.0\nDescription-Content-Type: text/x-rst\nLicense-File: LICENSE\nRequires-Dist: aiohttp (<4,>=3.7.4)\nProvides-Extra: docs\nRequires-Dist: sphinx (==4.4.0) ; extra == 'docs'\nRequires-Dist: sphinxcontrib-trio (==1.1.2) ; extra == 'docs'\nRequires-Dist: sphinxcontrib-websupport ; extra == 'docs'\nRequires-Dist: typing-extensions (<5,>=4.3) ; extra == 'docs'\nProvides-Extra: speed\nRequires-Dist: orjson (>=3.5.4) ; extra == 'speed'\nRequires-Dist: aiodns (>=1.1) ; extra == 'speed'\nRequires-Dist: Brotli ; extra == 'speed'\nRequires-Dist: cchardet ; extra == 'speed'\nProvides-Extra: test\nRequires-Dist: coverage[toml] ; extra == 'test'\nRequires-Dist: pytest ; extra == 'test'\nRequires-Dist: pytest-asyncio ; extra == 'test'\nRequires-Dist: pytest-cov ; extra == 'test'\nRequires-Dist: pytest-mock ; extra == 'test'\nRequires-Dist: typing-extensions (<5,>=4.3) ; extra == 'test'\nProvides-Extra: voice\nRequires-Dist: PyNaCl (<1.6,>=1.3.0) ; extra == 'voice'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/filecache-0.81-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: filecache\nVersion: 0.81\nSummary: Persistent caching decorator\nHome-page: https://github.com/ubershmekel/filecache\nAuthor: ubershmekel\nAuthor-email: ubershmekel@gmail.com\nLicense: UNKNOWN\nPlatform: UNKNOWN\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 3\nClassifier: Topic :: Utilities\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/funcsigs-1.0.2-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: funcsigs\nVersion: 1.0.2\nSummary: Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+\nHome-page: http://funcsigs.readthedocs.org\nAuthor: Testing Cabal\nAuthor-email: testing-in-python@lists.idyll.org\nLicense: ASL\nPlatform: UNKNOWN\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nRequires-Dist: ordereddict; python_version<\"2.7\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/futures-3.2.0-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: futures\nVersion: 3.2.0\nSummary: Backport of the concurrent.futures package from Python 3\nHome-page: https://github.com/agronholm/pythonfutures\nAuthor: Alex Grönholm\nAuthor-email: alex.gronholm@nextday.fi\nLicense: PSF\nPlatform: UNKNOWN\nClassifier: License :: OSI Approved :: Python Software Foundation License\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 2 :: Only\nRequires-Python: >=2.6, <3\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/importlib_metadata-1.7.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: importlib-metadata\nVersion: 1.7.0\nSummary: Read metadata from Python packages\nHome-page: http://importlib-metadata.readthedocs.io/\nAuthor: Barry Warsaw\nAuthor-email: barry@python.org\nLicense: Apache Software License\nPlatform: UNKNOWN\nClassifier: Development Status :: 3 - Alpha\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 2\nRequires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7\nRequires-Dist: zipp (>=0.5)\nRequires-Dist: pathlib2 ; python_version < \"3\"\nRequires-Dist: contextlib2 ; python_version < \"3\"\nRequires-Dist: configparser (>=3.5) ; python_version < \"3\"\nProvides-Extra: docs\nRequires-Dist: sphinx ; extra == 'docs'\nRequires-Dist: rst.linker ; extra == 'docs'\nProvides-Extra: testing\nRequires-Dist: packaging ; extra == 'testing'\nRequires-Dist: pep517 ; extra == 'testing'\nRequires-Dist: importlib-resources (>=1.3) ; (python_version < \"3.9\") and extra == 'testing'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/ipython-4.1.0rc1-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: ipython\nVersion: 4.1.0rc1\nSummary: IPython: Productive Interactive Computing\nHome-page: http://ipython.org\nAuthor: The IPython Development Team\nAuthor-email: ipython-dev@scipy.org\nLicense: BSD\nDownload-URL: https://github.com/ipython/ipython/downloads\nKeywords: Interactive,Interpreter,Shell,Parallel,Distributed,Web-based computing,Qt console,Embedding\nPlatform: Linux\nPlatform: Mac OSX\nPlatform: Windows XP/Vista/7/8\nClassifier: Framework :: IPython\nClassifier: Intended Audience :: Developers\nClassifier: Intended Audience :: Science/Research\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Topic :: System :: Shells\nRequires-Dist: pickleshare\nRequires-Dist: setuptools (>=18.5decorator)\nRequires-Dist: simplegeneric (>0.8)\nRequires-Dist: traitlets\nRequires-Dist: pexpect; sys_platform != \"win32\"\nRequires-Dist: appnope; sys_platform == \"darwin\"\nRequires-Dist: gnureadline; sys_platform == \"darwin\" and platform_python_implementation == \"CPython\"\nProvides-Extra: all\nRequires-Dist: Sphinx (>=1.3); extra == 'all'\nRequires-Dist: ipykernel; extra == 'all'\nRequires-Dist: ipyparallel; extra == 'all'\nRequires-Dist: ipywidgets; extra == 'all'\nRequires-Dist: nbconvert; extra == 'all'\nRequires-Dist: nbformat; extra == 'all'\nRequires-Dist: nose (>=0.10.1); extra == 'all'\nRequires-Dist: notebook; extra == 'all'\nRequires-Dist: qtconsole; extra == 'all'\nRequires-Dist: requests; extra == 'all'\nRequires-Dist: testpath; extra == 'all'\nProvides-Extra: doc\nRequires-Dist: Sphinx (>=1.3); extra == 'doc'\nProvides-Extra: kernel\nRequires-Dist: ipykernel; extra == 'kernel'\nProvides-Extra: nbconvert\nRequires-Dist: nbconvert; extra == 'nbconvert'\nProvides-Extra: nbformat\nRequires-Dist: nbformat; extra == 'nbformat'\nProvides-Extra: notebook\nRequires-Dist: ipywidgets; extra == 'notebook'\nRequires-Dist: notebook; extra == 'notebook'\nProvides-Extra: parallel\nRequires-Dist: ipyparallel; extra == 'parallel'\nProvides-Extra: qtconsole\nRequires-Dist: qtconsole; extra == 'qtconsole'\nProvides-Extra: terminal\nProvides-Extra: terminal\nRequires-Dist: pyreadline (>=2); sys_platform == \"win32\" and extra == 'terminal'\nProvides-Extra: test\nRequires-Dist: nose (>=0.10.1); extra == 'test'\nRequires-Dist: requests; extra == 'test'\nRequires-Dist: testpath; extra == 'test'\nProvides-Extra: test\nRequires-Dist: mock; python_version == \"2.7\" and extra == 'test'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/ipython-5.7.0-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: ipython\nVersion: 5.7.0\nSummary: IPython: Productive Interactive Computing\nHome-page: https://ipython.org\nAuthor: The IPython Development Team\nAuthor-email: ipython-dev@python.org\nLicense: BSD\nDescription-Content-Type: UNKNOWN\nKeywords: Interactive,Interpreter,Shell,Embedding\nPlatform: Linux\nPlatform: Mac OSX\nPlatform: Windows\nClassifier: Framework :: IPython\nClassifier: Intended Audience :: Developers\nClassifier: Intended Audience :: Science/Research\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Topic :: System :: Shells\nRequires-Dist: setuptools (>=18.5)\nRequires-Dist: decorator\nRequires-Dist: pickleshare\nRequires-Dist: simplegeneric (>0.8)\nRequires-Dist: traitlets (>=4.2)\nRequires-Dist: prompt-toolkit (<2.0.0,>=1.0.4)\nRequires-Dist: pygments\nRequires-Dist: backports.shutil-get-terminal-size; python_version == \"2.7\"\nRequires-Dist: pathlib2; python_version == \"2.7\" or python_version == \"3.3\"\nRequires-Dist: pexpect; sys_platform != \"win32\"\nRequires-Dist: appnope; sys_platform == \"darwin\"\nRequires-Dist: colorama; sys_platform == \"win32\"\nRequires-Dist: win-unicode-console (>=0.5); sys_platform == \"win32\" and python_version < \"3.6\"\nProvides-Extra: all\nRequires-Dist: nbformat; extra == 'all'\nRequires-Dist: ipykernel; extra == 'all'\nRequires-Dist: pygments; extra == 'all'\nRequires-Dist: testpath; extra == 'all'\nRequires-Dist: notebook; extra == 'all'\nRequires-Dist: nbconvert; extra == 'all'\nRequires-Dist: ipyparallel; extra == 'all'\nRequires-Dist: qtconsole; extra == 'all'\nRequires-Dist: Sphinx (>=1.3); extra == 'all'\nRequires-Dist: requests; extra == 'all'\nRequires-Dist: nose (>=0.10.1); extra == 'all'\nRequires-Dist: ipywidgets; extra == 'all'\nProvides-Extra: doc\nRequires-Dist: Sphinx (>=1.3); extra == 'doc'\nProvides-Extra: kernel\nRequires-Dist: ipykernel; extra == 'kernel'\nProvides-Extra: nbconvert\nRequires-Dist: nbconvert; extra == 'nbconvert'\nProvides-Extra: nbformat\nRequires-Dist: nbformat; extra == 'nbformat'\nProvides-Extra: notebook\nRequires-Dist: notebook; extra == 'notebook'\nRequires-Dist: ipywidgets; extra == 'notebook'\nProvides-Extra: parallel\nRequires-Dist: ipyparallel; extra == 'parallel'\nProvides-Extra: qtconsole\nRequires-Dist: qtconsole; extra == 'qtconsole'\nProvides-Extra: terminal\nProvides-Extra: test\nRequires-Dist: nose (>=0.10.1); extra == 'test'\nRequires-Dist: requests; extra == 'test'\nRequires-Dist: testpath; extra == 'test'\nRequires-Dist: pygments; extra == 'test'\nRequires-Dist: nbformat; extra == 'test'\nRequires-Dist: ipykernel; extra == 'test'\nProvides-Extra: test\nRequires-Dist: mock; python_version == \"2.7\" and extra == 'test'\nProvides-Extra: test\nRequires-Dist: numpy; python_version >= \"3.4\" and extra == 'test'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/ipython-5.7.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: ipython\nVersion: 5.7.0\nSummary: IPython: Productive Interactive Computing\nHome-page: https://ipython.org\nAuthor: The IPython Development Team\nAuthor-email: ipython-dev@python.org\nLicense: BSD\nDescription-Content-Type: UNKNOWN\nKeywords: Interactive,Interpreter,Shell,Embedding\nPlatform: Linux\nPlatform: Mac OSX\nPlatform: Windows\nClassifier: Framework :: IPython\nClassifier: Intended Audience :: Developers\nClassifier: Intended Audience :: Science/Research\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Topic :: System :: Shells\nRequires-Dist: setuptools (>=18.5)\nRequires-Dist: decorator\nRequires-Dist: pickleshare\nRequires-Dist: simplegeneric (>0.8)\nRequires-Dist: traitlets (>=4.2)\nRequires-Dist: prompt-toolkit (<2.0.0,>=1.0.4)\nRequires-Dist: pygments\nRequires-Dist: backports.shutil-get-terminal-size; python_version == \"2.7\"\nRequires-Dist: pathlib2; python_version == \"2.7\" or python_version == \"3.3\"\nRequires-Dist: pexpect; sys_platform != \"win32\"\nRequires-Dist: appnope; sys_platform == \"darwin\"\nRequires-Dist: colorama; sys_platform == \"win32\"\nRequires-Dist: win-unicode-console (>=0.5); sys_platform == \"win32\" and python_version < \"3.6\"\nProvides-Extra: all\nRequires-Dist: nose (>=0.10.1); extra == 'all'\nRequires-Dist: ipywidgets; extra == 'all'\nRequires-Dist: nbformat; extra == 'all'\nRequires-Dist: pygments; extra == 'all'\nRequires-Dist: ipyparallel; extra == 'all'\nRequires-Dist: requests; extra == 'all'\nRequires-Dist: ipykernel; extra == 'all'\nRequires-Dist: qtconsole; extra == 'all'\nRequires-Dist: testpath; extra == 'all'\nRequires-Dist: nbconvert; extra == 'all'\nRequires-Dist: Sphinx (>=1.3); extra == 'all'\nRequires-Dist: notebook; extra == 'all'\nProvides-Extra: doc\nRequires-Dist: Sphinx (>=1.3); extra == 'doc'\nProvides-Extra: kernel\nRequires-Dist: ipykernel; extra == 'kernel'\nProvides-Extra: nbconvert\nRequires-Dist: nbconvert; extra == 'nbconvert'\nProvides-Extra: nbformat\nRequires-Dist: nbformat; extra == 'nbformat'\nProvides-Extra: notebook\nRequires-Dist: notebook; extra == 'notebook'\nRequires-Dist: ipywidgets; extra == 'notebook'\nProvides-Extra: parallel\nRequires-Dist: ipyparallel; extra == 'parallel'\nProvides-Extra: qtconsole\nRequires-Dist: qtconsole; extra == 'qtconsole'\nProvides-Extra: terminal\nProvides-Extra: test\nRequires-Dist: nose (>=0.10.1); extra == 'test'\nRequires-Dist: requests; extra == 'test'\nRequires-Dist: testpath; extra == 'test'\nRequires-Dist: pygments; extra == 'test'\nRequires-Dist: nbformat; extra == 'test'\nRequires-Dist: ipykernel; extra == 'test'\nProvides-Extra: test\nRequires-Dist: mock; python_version == \"2.7\" and extra == 'test'\nProvides-Extra: test\nRequires-Dist: numpy; python_version >= \"3.4\" and extra == 'test'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/ipython-7.5.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: ipython\nVersion: 7.5.0\nSummary: IPython: Productive Interactive Computing\nHome-page: https://ipython.org\nAuthor: The IPython Development Team\nAuthor-email: ipython-dev@python.org\nLicense: BSD\nProject-URL: Documentation, https://ipython.readthedocs.io/\nProject-URL: Funding, https://numfocus.org/\nProject-URL: Source, https://github.com/ipython/ipython\nProject-URL: Tracker, https://github.com/ipython/ipython/issues\nKeywords: Interactive,Interpreter,Shell,Embedding\nPlatform: Linux\nPlatform: Mac OSX\nPlatform: Windows\nClassifier: Framework :: IPython\nClassifier: Intended Audience :: Developers\nClassifier: Intended Audience :: Science/Research\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: System :: Shells\nRequires-Python: >=3.5\nProvides-Extra: parallel\nProvides-Extra: nbconvert\nProvides-Extra: terminal\nProvides-Extra: kernel\nProvides-Extra: notebook\nProvides-Extra: doc\nProvides-Extra: nbformat\nProvides-Extra: test\nProvides-Extra: all\nProvides-Extra: qtconsole\nRequires-Dist: setuptools (>=18.5)\nRequires-Dist: jedi (>=0.10)\nRequires-Dist: decorator\nRequires-Dist: pickleshare\nRequires-Dist: traitlets (>=4.2)\nRequires-Dist: prompt-toolkit (<2.1.0,>=2.0.0)\nRequires-Dist: pygments\nRequires-Dist: backcall\nRequires-Dist: typing; python_version == \"3.4\"\nRequires-Dist: pexpect; sys_platform != \"win32\"\nRequires-Dist: appnope; sys_platform == \"darwin\"\nRequires-Dist: colorama; sys_platform == \"win32\"\nRequires-Dist: win-unicode-console (>=0.5); sys_platform == \"win32\" and python_version < \"3.6\"\nProvides-Extra: all\nRequires-Dist: nbconvert; extra == 'all'\nRequires-Dist: ipywidgets; extra == 'all'\nRequires-Dist: pygments; extra == 'all'\nRequires-Dist: ipykernel; extra == 'all'\nRequires-Dist: notebook; extra == 'all'\nRequires-Dist: ipyparallel; extra == 'all'\nRequires-Dist: requests; extra == 'all'\nRequires-Dist: Sphinx (>=1.3); extra == 'all'\nRequires-Dist: nbformat; extra == 'all'\nRequires-Dist: nose (>=0.10.1); extra == 'all'\nRequires-Dist: numpy; extra == 'all'\nRequires-Dist: testpath; extra == 'all'\nRequires-Dist: qtconsole; extra == 'all'\nProvides-Extra: doc\nRequires-Dist: Sphinx (>=1.3); extra == 'doc'\nProvides-Extra: kernel\nRequires-Dist: ipykernel; extra == 'kernel'\nProvides-Extra: nbconvert\nRequires-Dist: nbconvert; extra == 'nbconvert'\nProvides-Extra: nbformat\nRequires-Dist: nbformat; extra == 'nbformat'\nProvides-Extra: notebook\nRequires-Dist: notebook; extra == 'notebook'\nRequires-Dist: ipywidgets; extra == 'notebook'\nProvides-Extra: parallel\nRequires-Dist: ipyparallel; extra == 'parallel'\nProvides-Extra: qtconsole\nRequires-Dist: qtconsole; extra == 'qtconsole'\nProvides-Extra: terminal\nProvides-Extra: test\nRequires-Dist: nose (>=0.10.1); extra == 'test'\nRequires-Dist: requests; extra == 'test'\nRequires-Dist: testpath; extra == 'test'\nRequires-Dist: pygments; extra == 'test'\nRequires-Dist: nbformat; extra == 'test'\nRequires-Dist: ipykernel; extra == 'test'\nRequires-Dist: numpy; extra == 'test'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/isodate-0.7.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: isodate\nVersion: 0.7.0\nSummary: An ISO 8601 date/time/duration parser and formatter\nAuthor: Gerhard Weis\nLicense: Copyright (c) 2021, Hugo van Kemenade and contributors\n        Copyright (c) 2009-2018, Gerhard Weis and contributors\n        Copyright (c) 2009, Gerhard Weis\n        All rights reserved.\n        \n        Redistribution and use in source and binary forms, with or without\n        modification, are permitted provided that the following conditions are met:\n            * Redistributions of source code must retain the above copyright\n              notice, this list of conditions and the following disclaimer.\n            * Redistributions in binary form must reproduce the above copyright\n              notice, this list of conditions and the following disclaimer in the\n              documentation and/or other materials provided with the distribution.\n            * Neither the name of the <organization> nor the\n              names of its contributors may be used to endorse or promote products\n              derived from this software without specific prior written permission.\n        \n        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n        ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n        WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n        DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\n        DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n        (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n        LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n        ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n        (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n        SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        \nProject-URL: Homepage, https://github.com/gweis/isodate/\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Programming Language :: Python :: 3.12\nClassifier: Programming Language :: Python :: 3.13\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Internet\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nDescription-Content-Type: text/x-rst\nLicense-File: LICENSE\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/isort-4.3.4-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: isort\nVersion: 4.3.4\nSummary: A Python utility / library to sort Python imports.\nHome-page: https://github.com/timothycrosley/isort\nAuthor: Timothy Crosley\nAuthor-email: timothy.crosley@gmail.com\nLicense: MIT\nDescription-Content-Type: UNKNOWN\nKeywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean\nPlatform: UNKNOWN\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: Environment :: Console\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: futures\nRequires-Dist: futures; python_version==\"2.7\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/isort-4.3.4-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: isort\nVersion: 4.3.4\nSummary: A Python utility / library to sort Python imports.\nHome-page: https://github.com/timothycrosley/isort\nAuthor: Timothy Crosley\nAuthor-email: timothy.crosley@gmail.com\nLicense: MIT\nKeywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean\nPlatform: UNKNOWN\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: Environment :: Console\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: futures; python_version==\"2.7\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/isort-metadata-4.3.4-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: isort-metadata\nVersion: 4.3.4\nSummary: A Python utility / library to sort Python imports.\nHome-page: https://github.com/timothycrosley/isort\nAuthor: Timothy Crosley\nAuthor-email: timothy.crosley@gmail.com\nLicense: MIT\nKeywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean\nPlatform: UNKNOWN\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: Environment :: Console\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: futures; python_version==\"2.7\"\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/isort-metadata-4.3.4-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: isort-metadata\nVersion: 4.3.4\nSummary: A Python utility / library to sort Python imports.\nHome-page: https://github.com/timothycrosley/isort\nAuthor: Timothy Crosley\nAuthor-email: timothy.crosley@gmail.com\nLicense: MIT\nKeywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean\nPlatform: UNKNOWN\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: Environment :: Console\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: futures; python_version==\"2.7\"\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/jupyter-1.0.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: jupyter\nVersion: 1.0.0\nSummary: Jupyter metapackage. Install all the Jupyter components in one go.\nHome-page: http://jupyter.org\nAuthor: Jupyter Development Team\nAuthor-email: jupyter@googlegroups.org\nLicense: BSD\nPlatform: UNKNOWN\nClassifier: Intended Audience :: Developers\nClassifier: Intended Audience :: System Administrators\nClassifier: Intended Audience :: Science/Research\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nRequires-Dist: notebook\nRequires-Dist: qtconsole\nRequires-Dist: jupyter-console\nRequires-Dist: nbconvert\nRequires-Dist: ipykernel\nRequires-Dist: ipywidgets\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/mocked/with_extra_dependency-0.12.4-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: with-extra-dependency\nVersion: 0.12.4\nSummary: Mock Package\nHome-page: https://python-poetry.org/\nLicense: MIT\nKeywords: packaging,dependency,poetry\nAuthor: Poetry Team\nAuthor-email: noreply@python-poetry.org\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Topic :: Software Development :: Build Tools\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nRequires-Dist: filecache; extra == 'filecache'\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/mocked/with_transitive_extra_dependency-0.12.4-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: with-transitive-extra-dependency\nVersion: 0.12.4\nSummary: Mock Package\nHome-page: https://python-poetry.org/\nLicense: MIT\nKeywords: packaging,dependency,poetry\nAuthor: Poetry Team\nAuthor-email: noreply@python-poetry.org\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Topic :: Software Development :: Build Tools\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nRequires-Dist: with-extra-dependency[filecache] (>=0.12.4,<0.13.0)\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/more_itertools-4.1.0-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: more-itertools\nVersion: 4.1.0\nSummary: More routines for operating on iterables, beyond itertools\nHome-page: https://github.com/erikrose/more-itertools\nAuthor: Erik Rose\nAuthor-email: erikrose@grinchcentral.com\nLicense: MIT\nDescription-Content-Type: UNKNOWN\nKeywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.2\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries\nRequires-Dist: six (<2.0.0,>=1.0.0)\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/more_itertools-4.1.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: more-itertools\nVersion: 4.1.0\nSummary: More routines for operating on iterables, beyond itertools\nHome-page: https://github.com/erikrose/more-itertools\nAuthor: Erik Rose\nAuthor-email: erikrose@grinchcentral.com\nLicense: MIT\nDescription-Content-Type: UNKNOWN\nKeywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.2\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Topic :: Software Development :: Libraries\nRequires-Dist: six (<2.0.0,>=1.0.0)\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pastel-0.1.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: pastel\nVersion: 0.1.0\nSummary: Bring colors to your terminal.\nHome-page: https://github.com/sdispater/pastel\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nLicense: MIT\nDownload-URL: https://github.com/sdispater/pastel/archive/0.1.0.tar.gz\nPlatform: UNKNOWN\nClassifier: Intended Audience :: Developers\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pluggy-0.6.0-py2-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: pluggy\nVersion: 0.6.0\nSummary: plugin and hook calling mechanisms for python\nHome-page: https://github.com/pytest-dev/pluggy\nAuthor: Holger Krekel\nAuthor-email: holger@merlinux.eu\nLicense: MIT license\nPlatform: unix\nPlatform: linux\nPlatform: osx\nPlatform: win32\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: POSIX\nClassifier: Operating System :: Microsoft :: Windows\nClassifier: Operating System :: MacOS :: MacOS X\nClassifier: Topic :: Software Development :: Testing\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pluggy-0.6.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: pluggy\nVersion: 0.6.0\nSummary: plugin and hook calling mechanisms for python\nHome-page: https://github.com/pytest-dev/pluggy\nAuthor: Holger Krekel\nAuthor-email: holger@merlinux.eu\nLicense: MIT license\nDescription-Content-Type: UNKNOWN\nPlatform: unix\nPlatform: linux\nPlatform: osx\nPlatform: win32\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: POSIX\nClassifier: Operating System :: Microsoft :: Windows\nClassifier: Operating System :: MacOS :: MacOS X\nClassifier: Topic :: Software Development :: Testing\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/poetry_core-1.5.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: poetry-core\nVersion: 1.5.0\nSummary: Poetry PEP 517 Build Backend\nHome-page: https://github.com/python-poetry/poetry-core\nLicense: MIT\nKeywords: packaging,dependency,poetry\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=3.7,<4.0\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Topic :: Software Development :: Build Tools\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nRequires-Dist: importlib-metadata (>=1.7.0) ; python_version < \"3.8\"\nProject-URL: Bug Tracker, https://github.com/python-poetry/poetry/issues\nProject-URL: Repository, https://github.com/python-poetry/poetry-core\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/poetry_core-2.0.1-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.3\nName: poetry-core\nVersion: 2.0.1\nSummary: Poetry PEP 517 Build Backend\nLicense: MIT\nKeywords: packaging,dependency,poetry\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nMaintainer: Arun Babu Neelicattu\nMaintainer-email: arun.neelicattu@gmail.com\nRequires-Python: >=3.9, <4.0\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Programming Language :: Python :: 3.12\nClassifier: Programming Language :: Python :: 3.13\nClassifier: Topic :: Software Development :: Build Tools\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nProject-URL: Bug Tracker, https://github.com/python-poetry/poetry/issues\nProject-URL: Homepage, https://github.com/python-poetry/poetry-core\nProject-URL: Repository, https://github.com/python-poetry/poetry-core\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/py-1.5.3-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: py\nVersion: 1.5.3\nSummary: library with cross-python path, ini-parsing, io, code, log facilities\nHome-page: http://py.readthedocs.io/\nAuthor: holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others\nAuthor-email: pytest-dev@python.org\nLicense: MIT license\nPlatform: unix\nPlatform: linux\nPlatform: osx\nPlatform: cygwin\nPlatform: win32\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: POSIX\nClassifier: Operating System :: Microsoft :: Windows\nClassifier: Operating System :: MacOS :: MacOS X\nClassifier: Topic :: Software Development :: Testing\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pylev-1.3.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: pylev\nVersion: 1.3.0\nSummary: A pure Python Levenshtein implementation that's not freaking GPL'd.\nHome-page: http://github.com/toastdriven/pylev\nAuthor: Daniel Lindsley\nAuthor-email: daniel@toastdriven.com\nLicense: UNKNOWN\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pytest-3.5.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: pytest\nVersion: 3.5.0\nSummary: pytest: simple powerful testing with Python\nHome-page: http://pytest.org\nAuthor: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others\nLicense: MIT license\nKeywords: test unittest\nPlatform: unix\nPlatform: linux\nPlatform: osx\nPlatform: cygwin\nPlatform: win32\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: POSIX\nClassifier: Operating System :: Microsoft :: Windows\nClassifier: Operating System :: MacOS :: MacOS X\nClassifier: Topic :: Software Development :: Testing\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: py (>=1.5.0)\nRequires-Dist: six (>=1.10.0)\nRequires-Dist: setuptools\nRequires-Dist: attrs (>=17.4.0)\nRequires-Dist: more-itertools (>=4.0.0)\nRequires-Dist: pluggy (<0.7,>=0.5)\nRequires-Dist: funcsigs; python_version < \"3.0\"\nRequires-Dist: colorama; sys_platform == \"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/pytest-3.5.1-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: pytest\nVersion: 3.5.1\nSummary: pytest: simple powerful testing with Python\nHome-page: http://pytest.org\nAuthor: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others\nLicense: MIT license\nProject-URL: Source, https://github.com/pytest-dev/pytest\nProject-URL: Tracker, https://github.com/pytest-dev/pytest/issues\nKeywords: test unittest\nPlatform: unix\nPlatform: linux\nPlatform: osx\nPlatform: cygwin\nPlatform: win32\nClassifier: Development Status :: 6 - Mature\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: POSIX\nClassifier: Operating System :: Microsoft :: Windows\nClassifier: Operating System :: MacOS :: MacOS X\nClassifier: Topic :: Software Development :: Testing\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: py (>=1.5.0)\nRequires-Dist: six (>=1.10.0)\nRequires-Dist: setuptools\nRequires-Dist: attrs (>=17.4.0)\nRequires-Dist: more-itertools (>=4.0.0)\nRequires-Dist: pluggy (<0.7,>=0.5)\nRequires-Dist: funcsigs; python_version < \"3.0\"\nRequires-Dist: colorama; sys_platform == \"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.18.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.18.0\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Dist: certifi (>=2017.4.17)\nRequires-Dist: chardet (>=3.0.2,<3.1.0)\nRequires-Dist: idna (>=2.5,<2.6)\nRequires-Dist: urllib3 (<1.22,>=1.21.1)\nProvides-Extra: security\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.18.1-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.18.1\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Dist: certifi (>=2017.4.17)\nRequires-Dist: chardet (>=3.0.2,<3.1.0)\nRequires-Dist: idna (>=2.5,<2.6)\nRequires-Dist: urllib3 (<1.22,>=1.21.1)\nProvides-Extra: security\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.18.2-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.18.2\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Dist: certifi (>=2017.4.17)\nRequires-Dist: chardet (>=3.0.2,<3.1.0)\nRequires-Dist: idna (>=2.5,<2.6)\nRequires-Dist: urllib3 (<1.23,>=1.21.1)\nProvides-Extra: security\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.18.3-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.18.3\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Dist: certifi (>=2017.4.17)\nRequires-Dist: chardet (>=3.0.2,<3.1.0)\nRequires-Dist: idna (>=2.5,<2.6)\nRequires-Dist: urllib3 (<1.23,>=1.21.1)\nProvides-Extra: security\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.18.4-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.18.4\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2.6\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Dist: certifi (>=2017.4.17)\nRequires-Dist: chardet (>=3.0.2,<3.1.0)\nRequires-Dist: idna (>=2.5,<2.7)\nRequires-Dist: urllib3 (<1.23,>=1.21.1)\nProvides-Extra: security\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/requests-2.19.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: requests\nVersion: 2.19.0\nSummary: Python HTTP for Humans.\nHome-page: http://python-requests.org\nAuthor: Kenneth Reitz\nAuthor-email: me@kennethreitz.org\nLicense: Apache 2.0\nDescription-Content-Type: text/x-rst\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Natural Language :: English\nClassifier: License :: OSI Approved :: Apache Software License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: Implementation :: CPython\nClassifier: Programming Language :: Python :: Implementation :: PyPy\nRequires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nRequires-Dist: chardet (<3.1.0,>=3.0.2)\nRequires-Dist: idna (<2.8,>=2.5)\nRequires-Dist: urllib3 (<1.24,>=1.21.1)\nRequires-Dist: certifi (>=2017.4.17)\nProvides-Extra: security\nRequires-Dist: pyOpenSSL (>=0.14); extra == 'security'\nRequires-Dist: cryptography (>=1.3.4); extra == 'security'\nRequires-Dist: idna (>=2.0.0); extra == 'security'\nProvides-Extra: socks\nRequires-Dist: PySocks (!=1.5.7,>=1.5.6); extra == 'socks'\nProvides-Extra: socks\nRequires-Dist: win-inet-pton; sys_platform == \"win32\" and (python_version == \"2.7\" or python_version == \"2.6\") and extra == 'socks'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/setuptools-39.2.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: setuptools\nVersion: 39.2.0\nSummary: Easily download, build, install, upgrade, and uninstall Python packages\nHome-page: https://github.com/pypa/setuptools\nAuthor: Python Packaging Authority\nAuthor-email: distutils-sig@python.org\nLicense: UNKNOWN\nProject-URL: Documentation, https://setuptools.readthedocs.io/\nKeywords: CPAN PyPI distutils eggs package management\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: System :: Archiving :: Packaging\nClassifier: Topic :: System :: Systems Administration\nClassifier: Topic :: Utilities\nRequires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*\nDescription-Content-Type: text/x-rst; charset=UTF-8\nProvides-Extra: ssl\nProvides-Extra: certs\nProvides-Extra: certs\nRequires-Dist: certifi (==2016.9.26); extra == 'certs'\nProvides-Extra: ssl\nRequires-Dist: wincertstore (==0.2); (sys_platform=='win32') and extra == 'ssl'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/setuptools-67.6.1-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: setuptools\nVersion: 67.6.1\nSummary: Easily download, build, install, upgrade, and uninstall Python packages\nHome-page: https://github.com/pypa/setuptools\nAuthor: Python Packaging Authority\nAuthor-email: distutils-sig@python.org\nProject-URL: Documentation, https://setuptools.pypa.io/\nProject-URL: Changelog, https://setuptools.pypa.io/en/stable/history.html\nKeywords: CPAN PyPI distutils eggs package management\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development :: Libraries :: Python Modules\nClassifier: Topic :: System :: Archiving :: Packaging\nClassifier: Topic :: System :: Systems Administration\nClassifier: Topic :: Utilities\nRequires-Python: >=3.7\nLicense-File: LICENSE\nProvides-Extra: certs\nProvides-Extra: docs\nRequires-Dist: sphinx (>=3.5) ; extra == 'docs'\nRequires-Dist: jaraco.packaging (>=9) ; extra == 'docs'\nRequires-Dist: rst.linker (>=1.9) ; extra == 'docs'\nRequires-Dist: furo ; extra == 'docs'\nRequires-Dist: sphinx-lint ; extra == 'docs'\nRequires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'\nRequires-Dist: pygments-github-lexers (==0.0.5) ; extra == 'docs'\nRequires-Dist: sphinx-favicon ; extra == 'docs'\nRequires-Dist: sphinx-inline-tabs ; extra == 'docs'\nRequires-Dist: sphinx-reredirects ; extra == 'docs'\nRequires-Dist: sphinxcontrib-towncrier ; extra == 'docs'\nRequires-Dist: sphinx-notfound-page (==0.8.3) ; extra == 'docs'\nRequires-Dist: sphinx-hoverxref (<2) ; extra == 'docs'\nProvides-Extra: ssl\nProvides-Extra: testing\nRequires-Dist: pytest (>=6) ; extra == 'testing'\nRequires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'\nRequires-Dist: flake8 (<5) ; extra == 'testing'\nRequires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'\nRequires-Dist: pytest-perf ; extra == 'testing'\nRequires-Dist: flake8-2020 ; extra == 'testing'\nRequires-Dist: virtualenv (>=13.0.0) ; extra == 'testing'\nRequires-Dist: wheel ; extra == 'testing'\nRequires-Dist: pip (>=19.1) ; extra == 'testing'\nRequires-Dist: jaraco.envs (>=2.2) ; extra == 'testing'\nRequires-Dist: pytest-xdist ; extra == 'testing'\nRequires-Dist: jaraco.path (>=3.2.0) ; extra == 'testing'\nRequires-Dist: build[virtualenv] ; extra == 'testing'\nRequires-Dist: filelock (>=3.4.0) ; extra == 'testing'\nRequires-Dist: pip-run (>=8.8) ; extra == 'testing'\nRequires-Dist: ini2toml[lite] (>=0.9) ; extra == 'testing'\nRequires-Dist: tomli-w (>=1.0.0) ; extra == 'testing'\nRequires-Dist: pytest-timeout ; extra == 'testing'\nProvides-Extra: testing-integration\nRequires-Dist: pytest ; extra == 'testing-integration'\nRequires-Dist: pytest-xdist ; extra == 'testing-integration'\nRequires-Dist: pytest-enabler ; extra == 'testing-integration'\nRequires-Dist: virtualenv (>=13.0.0) ; extra == 'testing-integration'\nRequires-Dist: tomli ; extra == 'testing-integration'\nRequires-Dist: wheel ; extra == 'testing-integration'\nRequires-Dist: jaraco.path (>=3.2.0) ; extra == 'testing-integration'\nRequires-Dist: jaraco.envs (>=2.2) ; extra == 'testing-integration'\nRequires-Dist: build[virtualenv] ; extra == 'testing-integration'\nRequires-Dist: filelock (>=3.4.0) ; extra == 'testing-integration'\nRequires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != \"PyPy\") and extra == 'testing'\nRequires-Dist: pytest-cov ; (platform_python_implementation != \"PyPy\") and extra == 'testing'\nRequires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != \"PyPy\") and extra == 'testing'\nRequires-Dist: pytest-flake8 ; (python_version < \"3.12\") and extra == 'testing'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/six-1.11.0-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: six\nVersion: 1.11.0\nSummary: Python 2 and 3 compatibility utilities\nHome-page: http://pypi.python.org/pypi/six/\nAuthor: Benjamin Peterson\nAuthor-email: benjamin@python.org\nLicense: MIT\nPlatform: UNKNOWN\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 3\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Topic :: Software Development :: Libraries\nClassifier: Topic :: Utilities\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/toga-0.3.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: toga\nVersion: 0.3.0\nSummary: A Python native, OS native GUI toolkit.\nHome-page: https://beeware.org/project/projects/libraries/toga/\nAuthor: Russell Keith-Magee\nAuthor-email: russell@keith-magee.com\nMaintainer: BeeWare Team\nMaintainer-email: team@beeware.org\nLicense: New BSD\nProject-URL: Funding, https://beeware.org/contributing/membership/\nProject-URL: Documentation, http://toga.readthedocs.io/en/latest/\nProject-URL: Tracker, https://github.com/beeware/toga/issues\nProject-URL: Source, https://github.com/beeware/toga\nKeywords: gui,widget,cross-platform,desktop,mobile,web,macOS,cocoa,iOS,android,windows,winforms,linux,gtk\nClassifier: Development Status :: 3 - Alpha\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Programming Language :: Python :: 3.12\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development\nClassifier: Topic :: Software Development :: User Interfaces\nClassifier: Topic :: Software Development :: Widget Sets\nRequires-Python: >=3.7\nDescription-Content-Type: text/x-rst; charset=UTF-8\nRequires-Dist: toga-cocoa (==0.3.0) ; sys_platform==\"darwin\"\nRequires-Dist: toga-gtk (==0.3.0) ; sys_platform==\"linux\"\nRequires-Dist: toga-winforms (==0.3.0) ; sys_platform==\"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/toga-0.3.0.dev1-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: toga\nVersion: 0.3.0.dev1\nSummary: A Python native, OS native GUI toolkit.\nHome-page: http://pybee.org/toga\nAuthor: Russell Keith-Magee\nAuthor-email: russell@keith-magee.com\nLicense: New BSD\nDescription-Content-Type: UNKNOWN\nPlatform: UNKNOWN\nClassifier: Development Status :: 3 - Alpha\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development\nClassifier: Topic :: Software Development :: User Interfaces\nClassifier: Topic :: Software Development :: Widget Sets\nRequires-Dist: toga-cocoa; sys_platform==\"darwin\"\nRequires-Dist: toga-gtk; sys_platform==\"linux\"\nRequires-Dist: toga-winforms; sys_platform==\"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/toga-0.3.0.dev2-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.0\nName: toga\nVersion: 0.3.0.dev2\nSummary: A Python native, OS native GUI toolkit.\nHome-page: http://pybee.org/toga\nAuthor: Russell Keith-Magee\nAuthor-email: russell@keith-magee.com\nLicense: New BSD\nDescription-Content-Type: UNKNOWN\nPlatform: UNKNOWN\nClassifier: Development Status :: 3 - Alpha\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development\nClassifier: Topic :: Software Development :: User Interfaces\nClassifier: Topic :: Software Development :: Widget Sets\nRequires-Dist: toga-cocoa; sys_platform==\"darwin\"\nRequires-Dist: toga-gtk; sys_platform==\"linux\"\nRequires-Dist: toga-winforms; sys_platform==\"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/toga-0.4.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: toga\nVersion: 0.4.0\nSummary: A Python native, OS native GUI toolkit.\nHome-page: https://beeware.org/project/projects/libraries/toga/\nAuthor: Russell Keith-Magee\nAuthor-email: russell@keith-magee.com\nMaintainer: BeeWare Team\nMaintainer-email: team@beeware.org\nLicense: New BSD\nProject-URL: Funding, https://beeware.org/contributing/membership/\nProject-URL: Documentation, http://toga.readthedocs.io/en/latest/\nProject-URL: Tracker, https://github.com/beeware/toga/issues\nProject-URL: Source, https://github.com/beeware/toga\nKeywords: gui,widget,cross-platform,desktop,mobile,web,macOS,cocoa,iOS,android,windows,winforms,linux,freeBSD,gtk\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: BSD License\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Programming Language :: Python :: 3.12\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Topic :: Software Development\nClassifier: Topic :: Software Development :: User Interfaces\nClassifier: Topic :: Software Development :: Widget Sets\nRequires-Python: >=3.8\nDescription-Content-Type: text/x-rst; charset=UTF-8\nRequires-Dist: toga-gtk ==0.4.0 ; \"freebsd\" in sys_platform\nRequires-Dist: toga-cocoa ==0.4.0 ; sys_platform==\"darwin\"\nRequires-Dist: toga-gtk ==0.4.0 ; sys_platform==\"linux\"\nRequires-Dist: toga-winforms ==0.4.0 ; sys_platform==\"win32\"\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/tomlkit-0.5.2-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: tomlkit\nVersion: 0.5.2\nSummary: Style preserving TOML library\nHome-page: https://github.com/sdispater/tomlkit\nLicense: MIT\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nRequires-Dist: enum34 (>=1.1,<2.0); python_version >= \"2.7\" and python_version < \"2.8\"\nRequires-Dist: functools32 (>=3.2.3,<4.0.0); python_version >= \"2.7\" and python_version < \"2.8\"\nRequires-Dist: typing (>=3.6,<4.0); python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\"\nProject-URL: Repository, https://github.com/sdispater/tomlkit\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/tomlkit-0.5.3-py2.py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: tomlkit\nVersion: 0.5.3\nSummary: Style preserving TOML library\nHome-page: https://github.com/sdispater/tomlkit\nLicense: MIT\nAuthor: Sébastien Eustace\nAuthor-email: sebastien@eustace.io\nRequires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 2\nClassifier: Programming Language :: Python :: 2.7\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.4\nClassifier: Programming Language :: Python :: 3.5\nClassifier: Programming Language :: Python :: 3.6\nClassifier: Programming Language :: Python :: 3.7\nRequires-Dist: enum34 (>=1.1,<2.0); python_version >= \"2.7\" and python_version < \"2.8\"\nRequires-Dist: functools32 (>=3.2.3,<4.0.0); python_version >= \"2.7\" and python_version < \"2.8\"\nRequires-Dist: typing (>=3.6,<4.0); python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\"\nProject-URL: Repository, https://github.com/sdispater/tomlkit\nDescription-Content-Type: text/markdown\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/wheel-0.40.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: wheel\nVersion: 0.40.0\nSummary: A built-package format for Python\nKeywords: wheel,packaging\nAuthor-email: Daniel Holth <dholth@fastmail.fm>\nMaintainer-email: Alex Grönholm <alex.gronholm@nextday.fi>\nRequires-Python: >=3.7\nDescription-Content-Type: text/x-rst\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: Topic :: System :: Archiving :: Packaging\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python\nClassifier: Programming Language :: Python :: 3 :: Only\nClassifier: Programming Language :: Python :: 3.7\nClassifier: Programming Language :: Python :: 3.8\nClassifier: Programming Language :: Python :: 3.9\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nRequires-Dist: pytest >= 6.0.0 ; extra == \"test\"\nProject-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html\nProject-URL: Documentation, https://wheel.readthedocs.io/\nProject-URL: Issue Tracker, https://github.com/pypa/wheel/issues\nProvides-Extra: test\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/metadata/zipp-3.5.0-py3-none-any.whl.metadata",
    "content": "Metadata-Version: 2.1\nName: zipp\nVersion: 3.5.0\nSummary: Backport of pathlib-compatible object wrapper for zip files\nHome-page: https://github.com/jaraco/zipp\nAuthor: Jason R. Coombs\nAuthor-email: jaraco@jaraco.com\nLicense: UNKNOWN\nPlatform: UNKNOWN\nClassifier: Development Status :: 5 - Production/Stable\nClassifier: Intended Audience :: Developers\nClassifier: License :: OSI Approved :: MIT License\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3 :: Only\nRequires-Python: >=3.6\nLicense-File: LICENSE\nProvides-Extra: docs\nRequires-Dist: sphinx ; extra == 'docs'\nRequires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'\nRequires-Dist: rst.linker (>=1.9) ; extra == 'docs'\nProvides-Extra: testing\nRequires-Dist: pytest (>=4.6) ; extra == 'testing'\nRequires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'\nRequires-Dist: pytest-flake8 ; extra == 'testing'\nRequires-Dist: pytest-cov ; extra == 'testing'\nRequires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'\nRequires-Dist: jaraco.itertools ; extra == 'testing'\nRequires-Dist: func-timeout ; extra == 'testing'\nRequires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == 'testing'\nRequires-Dist: pytest-mypy ; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == 'testing'\n\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/search/search-disallowed.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta\n      http-equiv=\"Content-Security-Policy\"\n      content=\"default-src 'self'; img-src 'self' data:; media-src 'self' data:; object-src 'none'; style-src 'self' 'sha256-o4vzfmmUENEg4chMjjRP9EuW9ucGnGIGVdbl8d0SHQQ='; script-src 'self' 'sha256-a9bHdQGvRzDwDVzx8m+Rzw+0FHZad8L0zjtBwkxOIz4=';\"\n    />\n    <link\n      href=\"/_fs-ch-1T1wmsGaOgGaSxcX/assets/inter-var.woff2\"\n      rel=\"preload\"\n      as=\"font\"\n      type=\"font/woff2\"\n      crossorigin\n    />\n    <link href=\"/_fs-ch-1T1wmsGaOgGaSxcX/assets/styles.css\" rel=\"stylesheet\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1\"\n    />\n    <style>\n      #loading-error {\n        font-size: 16px;\n        font-family: 'Inter', sans-serif;\n        margin-top: 10px;\n        margin-left: 10px;\n        display: none;\n      }\n    </style>\n  </head>\n  <body>\n    <noscript>\n      <div class=\"noscript-container\">\n        <div class=\"noscript-content\">\n          <img\n            src=\"/_fs-ch-1T1wmsGaOgGaSxcX/assets/errorIcon.svg\"\n            alt=\"Error Icon\"\n            class=\"error-icon\"\n          />\n          <span class=\"noscript-span\"\n            >JavaScript is disabled in your browser.</span\n          >\n          Please enable JavaScript to proceed.\n        </div>\n      </div>\n    </noscript>\n    <div id=\"loading-error\">\n      A required part of this site couldn’t load. This may be due to a browser\n      extension, network issues, or browser settings. Please check your\n      connection, disable any ad blockers, or try using a different browser.\n    </div>\n    <script>\n      function loadScript(src) {\n        return new Promise((resolve, reject) => {\n          const script = document.createElement('script');\n          script.onload = resolve;\n          script.onerror = (event) => {\n            console.error('Script load error event:', event);\n            document.getElementById('loading-error').style.display = 'block';\n            reject(\n              new Error(\n                `Failed to load script: ${src}, Please contact the service administrator.`\n              )\n            );\n          };\n          script.src = src;\n          document.body.appendChild(script);\n        });\n      }\n\n      loadScript('/_fs-ch-1T1wmsGaOgGaSxcX/errors.js')\n        .then(() => {\n          const script = document.createElement('script');\n          script.src = '/_fs-ch-1T1wmsGaOgGaSxcX/script.js?reload=true';\n          script.onerror = (event) => {\n            console.error('Script load error event:', event);\n            const errorMsg = new Error(\n              `Failed to load script: ${script.src}. Please contact the service administrator.`\n            );\n            console.error(errorMsg);\n            handleScriptError();\n          };\n          document.body.appendChild(script);\n        })\n        .catch((error) => {\n          console.error(error);\n        });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.org/search/search.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\"><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <meta name=\"defaultLanguage\" content=\"en\">\n    <meta name=\"availableLanguages\" content=\"en\">\n\n\n\n    <title>Search results · PyPI</title>\n    <meta name=\"description\" content=\"The Python Package Index (PyPI) is a repository of software for the Python programming language.\">\n\n    <link rel=\"stylesheet\" href=\"search_files/warehouse.css\">\n    <link rel=\"stylesheet\" href=\"search_files/fontawesome.css\">\n    <link rel=\"stylesheet\" href=\"search_files/regular.css\">\n    <link rel=\"stylesheet\" href=\"search_files/solid.css\">\n    <link rel=\"stylesheet\" href=\"search_files/brands.css\">\n    <link rel=\"stylesheet\" href=\"search_files/css.css\">\n    <noscript>\n      <link rel=\"stylesheet\" href=\"/static/css/noscript.69d08c82.css\">\n    </noscript>\n\n\n\n    <link rel=\"icon\" href=\"https://pypi.org/static/images/favicon.6a76275d.ico\" type=\"image/x-icon\">\n\n    <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS: 40 latest updates\" href=\"https://pypi.org/rss/updates.xml\">\n    <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS: 40 newest packages\" href=\"https://pypi.org/rss/packages.xml\">\n\n\n    <meta property=\"og:url\" content=\"https://pypi.org/search/?q=sqlalchemy\">\n    <meta property=\"og:site_name\" content=\"PyPI\">\n    <meta property=\"og:type\" content=\"website\">\n    <meta property=\"og:image\" content=\"https://pypi.org/static/images/twitter.c0030826.jpg\">\n    <meta property=\"og:title\" content=\"Search results\">\n    <meta property=\"og:description\" content=\"The Python Package Index (PyPI) is a repository of software for the Python programming language.\">\n\n    <link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"PyPI\" href=\"https://pypi.org/opensearch.xml\">\n\n\n    <script src=\"search_files/raven.js\" integrity=\"sha384-D6LXy67EIC102DTuqypxwQsTHgiatlbvg7q/1YAWFb6lRyZ1lIZ6bGDsX7jxHNKA\" crossorigin=\"anonymous\">\n    </script>\n\n    <script async=\"\" data-ga-id=\"UA-55961911-1\" data-sentry-frontend-dsn=\"https://3a67b35c9dc248a191d761410b095861@sentry.io/1231155\" src=\"search_files/warehouse.js\">\n    </script>\n\n\n    <script async=\"\" src=\"search_files/js\"></script>\n    <script defer=\"defer\" src=\"search_files/insights.js\"></script>\n  </head>\n\n  <body data-controller=\"viewport-toggle\" style=\"padding-top: 0px;\">\n\n\n    <!-- Accessibility: this link should always be the first piece of content inside the body-->\n    <a href=\"#content\" class=\"skip-to-content\">Skip to main content</a>\n\n    <button type=\"button\" class=\"button button--primary button--switch-to-mobile hidden\" data-target=\"viewport-toggle.switchToMobile\" data-action=\"viewport-toggle#switchToMobile\">\n      Switch to mobile version\n    </button>\n\n    <div id=\"sticky-notifications\" class=\"stick-to-top js-stick-to-top\">\n      <!-- Add browser warning. Will show for ie9 and below -->\n      <!--[if IE]>\n      <div class=\"notification-bar notification-bar--warning\" role=\"status\">\n        <span class=\"notification-bar__icon\">\n          <i class=\"fa fa-exclamation-triangle\" aria-hidden=\"true\"></i>\n          <span class=\"sr-only\">Warning</span>\n        </span>\n        <span class=\"notification-bar__message\">You are using an unsupported browser, upgrade to a newer version.</span>\n      </div>\n      <![endif]-->\n\n      <noscript>\n      <div class=\"notification-bar notification-bar--warning\" role=\"status\">\n\n        <span class=\"notification-bar__icon\">\n          <i class=\"fa fa-exclamation-triangle\" aria-hidden=\"true\"></i>\n          <span class=\"sr-only\">Warning</span>\n        </span>\n        <span class=\"notification-bar__message\">Some features may not work without JavaScript. Please try enabling it if you encounter problems.</span>\n      </div>\n      </noscript>\n    </div>\n\n\n      <div data-html-include=\"/_includes/flash-messages/\">\n\n\n\n</div>\n\n\n\n\n\n      <div data-html-include=\"/_includes/session-notifications/\">\n\n</div>\n\n\n    <header class=\"site-header \">\n      <div class=\"site-container\">\n        <div class=\"split-layout\">\n\n          <div class=\"split-layout\">\n            <div>\n              <a class=\"site-header__logo\" href=\"https://pypi.org/\">\n                <img alt=\"PyPI\" src=\"search_files/logo-small.svg\">\n              </a>\n            </div>\n\n            <form class=\"search-form search-form--primary\" action=\"/search/\" role=\"search\">\n              <label for=\"search\" class=\"sr-only\">Search PyPI</label>\n              <input id=\"search\" class=\"search-form__search\" type=\"text\" name=\"q\" placeholder=\"Search projects\" value=\"sqlalchemy\" autocomplete=\"off\" autocapitalize=\"off\" spellcheck=\"false\">\n              <input type=\"hidden\" name=\"o\" value=\"\">\n              <button type=\"submit\" class=\"search-form__button\">\n                <i class=\"fa fa-search\" aria-hidden=\"true\"></i>\n                <span class=\"sr-only\">Search</span>\n              </button>\n            </form>\n          </div>\n\n\n          <div data-html-include=\"/_includes/current-user-indicator/\">\n\n    <div id=\"user-indicator\" class=\"horizontal-menu horizontal-menu--light horizontal-menu--tall\">\n  <nav class=\"horizontal-menu horizontal-menu--light horizontal-menu--tall hide-on-tablet\" aria-label=\"Main navigation\">\n    <ul>\n      <li class=\"horizontal-menu__item\"><a href=\"https://pypi.org/help/\" class=\"horizontal-menu__link\">Help</a></li>\n      <li class=\"horizontal-menu__item\"><a href=\"https://donate.pypi.org/\" class=\"horizontal-menu__link\">Donate</a></li>\n      <li class=\"horizontal-menu__item\"><a href=\"https://pypi.org/account/login/\" class=\"horizontal-menu__link\">Log in</a></li>\n      <li class=\"horizontal-menu__item\"><a href=\"https://pypi.org/account/register/\" class=\"horizontal-menu__link\">Register</a></li>\n    </ul>\n  </nav>\n  <nav class=\"dropdown dropdown--on-menu hidden show-on-tablet\" aria-label=\"Main navigation\">\n    <button type=\"button\" class=\"horizontal-menu__link dropdown__trigger\" aria-haspopup=\"true\" aria-expanded=\"false\" aria-label=\"View menu\" data-dropdown-bound=\"true\">\n      Menu\n      <span class=\"dropdown__trigger-caret\">\n        <i class=\"fa fa-caret-down\" aria-hidden=\"true\"></i>\n      </span>\n    </button>\n    <ul class=\"dropdown__content\" aria-hidden=\"true\" aria-label=\"Main menu\">\n      <li><a class=\"dropdown__link\" href=\"https://pypi.org/help/\">Help</a></li>\n      <li><a class=\"dropdown__link\" href=\"https://donate.pypi.org/\">Donate</a></li>\n      <li><a class=\"dropdown__link\" href=\"https://pypi.org/account/login/\">Log in</a></li>\n      <li><a class=\"dropdown__link\" href=\"https://pypi.org/account/register/\">Register</a></li>\n    </ul>\n  </nav>\n</div>\n  </div>\n        </div>\n      </div>\n    </header>\n\n\n    <div class=\"mobile-search\">\n      <form class=\"search-form search-form--fullwidth\" action=\"/search/\" role=\"search\">\n        <label for=\"mobile-search\" class=\"sr-only\">Search PyPI</label>\n        <input id=\"mobile-search\" class=\"search-form__search\" type=\"text\" name=\"q\" placeholder=\"Search projects\" value=\"sqlalchemy\" autocomplete=\"off\" autocapitalize=\"off\" spellcheck=\"false\">\n        <input type=\"hidden\" name=\"o\" value=\"\">\n        <button type=\"submit\" class=\"search-form__button\">\n          <i class=\"fa fa-search\" aria-hidden=\"true\"></i>\n          <span class=\"sr-only\">Search</span>\n        </button>\n      </form>\n    </div>\n\n\n    <main id=\"content\">\n\n<div class=\"horizontal-section horizontal-section--medium\">\n  <div class=\"left-layout\">\n    <div class=\"left-layout__sidebar\">\n      <div class=\"dark-overlay -js-dark-overlay\"></div>\n      <div class=\"filter-panel -js-filter-panel\">\n        <button type=\"button\" class=\"filter-panel__close -js-close-panel\" aria-label=\"Close panel\">\n          <i class=\"fa fa-times\" aria-hidden=\"true\"></i>\n        </button>\n        <h2 class=\"no-top-padding\">\n          Filter by <a href=\"https://pypi.org/classifiers/\">classifier</a>\n        </h2>\n        <form id=\"classifiers\">\n        <input id=\"search\" type=\"hidden\" name=\"q\" value=\"sqlalchemy\">\n        <input type=\"hidden\" name=\"o\" value=\"\">\n\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Framework\">Framework</button>\n            <div id=\"accordion-Framework\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._AiiDA\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: AiiDA\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._AiiDA\">AiiDA</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._AsyncIO\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: AsyncIO\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._AsyncIO\">AsyncIO</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._BEAT\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: BEAT\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._BEAT\">BEAT</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._BFG\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: BFG\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._BFG\">BFG</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Bob\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Bob\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Bob\">Bob</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Bottle\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Bottle\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Bottle\">Bottle</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Buildout\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Buildout\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Buildout\">Buildout</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Buildout_._Extension\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Buildout :: Extension\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Buildout_._Extension\">Extension</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Buildout_._Recipe\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Buildout :: Recipe\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Buildout_._Recipe\">Recipe</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._CastleCMS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: CastleCMS\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._CastleCMS\">CastleCMS</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._CastleCMS_._Theme\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: CastleCMS :: Theme\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._CastleCMS_._Theme\">Theme</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Chandler\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Chandler\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Chandler\">Chandler</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._CherryPy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: CherryPy\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._CherryPy\">CherryPy</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._CubicWeb\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: CubicWeb\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._CubicWeb\">CubicWeb</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Dash\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Dash\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Dash\">Dash</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django\">Django</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.10\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.10\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.10\">1.10</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.11\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.11\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.11\">1.11</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.4\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.4\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.4\">1.4</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.5\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.5\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.5\">1.5</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.6\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.6\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.6\">1.6</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.7\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.7\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.7\">1.7</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.8\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.8\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.8\">1.8</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._1.9\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 1.9\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._1.9\">1.9</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._2.0\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 2.0\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._2.0\">2.0</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._2.1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 2.1\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._2.1\">2.1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._2.2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 2.2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._2.2\">2.2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_._3.0\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django :: 3.0\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_._3.0\">3.0</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_CMS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django CMS\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_CMS\">Django CMS</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_CMS_._3.4\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django CMS :: 3.4\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_CMS_._3.4\">3.4</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_CMS_._3.5\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django CMS :: 3.5\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_CMS_._3.5\">3.5</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_CMS_._3.6\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django CMS :: 3.6\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_CMS_._3.6\">3.6</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Django_CMS_._3.7\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Django CMS :: 3.7\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Django_CMS_._3.7\">3.7</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Flake8\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Flake8\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Flake8\">Flake8</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Flask\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Flask\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Flask\">Flask</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Hypothesis\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Hypothesis\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Hypothesis\">Hypothesis</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._IDLE\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: IDLE\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._IDLE\">IDLE</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._IPython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: IPython\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._IPython\">IPython</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Jupyter\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Jupyter\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Jupyter\">Jupyter</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Lektor\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Lektor\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Lektor\">Lektor</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Masonite\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Masonite\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Masonite\">Masonite</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Nengo\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Nengo\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Nengo\">Nengo</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Odoo\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Odoo\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Odoo\">Odoo</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Opps\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Opps\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Opps\">Opps</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Paste\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Paste\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Paste\">Paste</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pelican\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pelican\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pelican\">Pelican</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pelican_._Plugins\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pelican :: Plugins\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pelican_._Plugins\">Plugins</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pelican_._Themes\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pelican :: Themes\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pelican_._Themes\">Themes</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone\">Plone</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._3.2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 3.2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._3.2\">3.2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._3.3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 3.3\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._3.3\">3.3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._4.0\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 4.0\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._4.0\">4.0</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._4.1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 4.1\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._4.1\">4.1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._4.2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 4.2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._4.2\">4.2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._4.3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 4.3\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._4.3\">4.3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._5.0\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 5.0\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._5.0\">5.0</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._5.1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 5.1\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._5.1\">5.1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._5.2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 5.2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._5.2\">5.2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._5.3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: 5.3\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._5.3\">5.3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._Addon\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: Addon\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._Addon\">Addon</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._Core\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: Core\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._Core\">Core</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Plone_._Theme\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Plone :: Theme\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Plone_._Theme\">Theme</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pylons\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pylons\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pylons\">Pylons</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pyramid\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pyramid\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pyramid\">Pyramid</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Pytest\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Pytest\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Pytest\">Pytest</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Review_Board\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Review Board\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Review_Board\">Review Board</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Robot_Framework\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Robot Framework\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Robot_Framework\">Robot Framework</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Robot_Framework_._Library\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Robot Framework :: Library\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Robot_Framework_._Library\">Library</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Robot_Framework_._Tool\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Robot Framework :: Tool\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Robot_Framework_._Tool\">Tool</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Scrapy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Scrapy\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Scrapy\">Scrapy</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Setuptools_Plugin\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Setuptools Plugin\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Setuptools_Plugin\">Setuptools Plugin</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Sphinx\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Sphinx\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Sphinx\">Sphinx</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Sphinx_._Extension\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Sphinx :: Extension\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Sphinx_._Extension\">Extension</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Sphinx_._Theme\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Sphinx :: Theme\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Sphinx_._Theme\">Theme</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._tox\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: tox\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._tox\">tox</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Trac\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Trac\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Trac\">Trac</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Trio\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Trio\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Trio\">Trio</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Tryton\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Tryton\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Tryton\">Tryton</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._TurboGears\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: TurboGears\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._TurboGears\">TurboGears</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._TurboGears_._Applications\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: TurboGears :: Applications\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._TurboGears_._Applications\">Applications</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._TurboGears_._Widgets\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: TurboGears :: Widgets\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._TurboGears_._Widgets\">Widgets</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Twisted\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Twisted\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Twisted\">Twisted</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Wagtail\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Wagtail\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Wagtail\">Wagtail</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Wagtail_._1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Wagtail :: 1\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Wagtail_._1\">1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Wagtail_._2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Wagtail :: 2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Wagtail_._2\">2</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._ZODB\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: ZODB\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._ZODB\">ZODB</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope\">Zope</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope_._2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope :: 2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope_._2\">2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope_._3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope :: 3\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope_._3\">3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope_._4\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope :: 4\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope_._4\">4</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope2\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope2\">Zope2</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Framework_._Zope3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Framework :: Zope3\">\n    <label class=\"checkbox-tree__label\" for=\"Framework_._Zope3\">Zope3</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Topic\">Topic</button>\n            <div id=\"accordion-Topic\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Adaptive_Technologies\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Adaptive Technologies\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Adaptive_Technologies\">Adaptive Technologies</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Artistic_Software\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Artistic Software\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Artistic_Software\">Artistic Software</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications\">Communications</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._BBS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: BBS\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._BBS\">BBS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Chat\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Chat\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Chat\">Chat</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Chat_._Internet_Relay_Chat\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Chat :: Internet Relay Chat\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Chat_._Internet_Relay_Chat\">Internet Relay Chat</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Conferencing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Conferencing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Conferencing\">Conferencing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email\">Email</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Address_Book\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Address Book\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Address_Book\">Address Book</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Email_Clients_(MUA)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Email Clients (MUA)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Email_Clients_(MUA)\">Email Clients (MUA)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Filters\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Filters\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Filters\">Filters</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Mailing_List_Servers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Mailing List Servers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Mailing_List_Servers\">Mailing List Servers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Mail_Transport_Agents\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Mail Transport Agents\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Mail_Transport_Agents\">Mail Transport Agents</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Post-Office\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Post-Office\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Post-Office\">Post-Office</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Post-Office_._IMAP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Post-Office :: IMAP\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Post-Office_._IMAP\">IMAP</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Email_._Post-Office_._POP3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Email :: Post-Office :: POP3\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Email_._Post-Office_._POP3\">POP3</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Fax\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Fax\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Fax\">Fax</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._File_Sharing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: File Sharing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._File_Sharing\">File Sharing</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._File_Sharing_._Gnutella\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: File Sharing :: Gnutella\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._File_Sharing_._Gnutella\">Gnutella</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Ham_Radio\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Ham Radio\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Ham_Radio\">Ham Radio</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Internet_Phone\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Internet Phone\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Internet_Phone\">Internet Phone</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Telephony\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Telephony\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Telephony\">Telephony</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Communications_._Usenet_News\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Communications :: Usenet News\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Communications_._Usenet_News\">Usenet News</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Database\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Database\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Database\">Database</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Database_._Database_Engines/Servers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Database :: Database Engines/Servers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Database_._Database_Engines/Servers\">Database Engines/Servers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Database_._Front-Ends\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Database :: Front-Ends\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Database_._Front-Ends\">Front-Ends</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment\">Desktop Environment</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._File_Managers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: File Managers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._File_Managers\">File Managers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._Gnome\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: Gnome\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._Gnome\">Gnome</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._GNUstep\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: GNUstep\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._GNUstep\">GNUstep</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._K_Desktop_Environment_(KDE)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: K Desktop Environment (KDE)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._K_Desktop_Environment_(KDE)\">K Desktop Environment (KDE)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._Screen_Savers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: Screen Savers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._Screen_Savers\">Screen Savers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._Window_Managers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: Window Managers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._Window_Managers\">Window Managers</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._Window_Managers_._Fluxbox\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: Window Managers :: Fluxbox\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._Window_Managers_._Fluxbox\">Fluxbox</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Desktop_Environment_._Window_Managers_._XFCE\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Desktop Environment :: Window Managers :: XFCE\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Desktop_Environment_._Window_Managers_._XFCE\">XFCE</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Documentation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Documentation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Documentation\">Documentation</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Documentation_._Sphinx\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Documentation :: Sphinx\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Documentation_._Sphinx\">Sphinx</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Education\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Education\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Education\">Education</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Education_._Computer_Aided_Instruction_(CAI)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Education :: Computer Aided Instruction (CAI)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Education_._Computer_Aided_Instruction_(CAI)\">Computer Aided Instruction (CAI)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Education_._Testing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Education :: Testing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Education_._Testing\">Testing</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment\">Games/Entertainment</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Arcade\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Arcade\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Arcade\">Arcade</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Board_Games\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Board Games\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Board_Games\">Board Games</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._First_Person_Shooters\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: First Person Shooters\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._First_Person_Shooters\">First Person Shooters</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Fortune_Cookies\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Fortune Cookies\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Fortune_Cookies\">Fortune Cookies</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Multi-User_Dungeons_(MUD)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Multi-User_Dungeons_(MUD)\">Multi-User Dungeons (MUD)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Puzzle_Games\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Puzzle Games\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Puzzle_Games\">Puzzle Games</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Real_Time_Strategy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Real Time Strategy\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Real_Time_Strategy\">Real Time Strategy</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Role-Playing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Role-Playing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Role-Playing\">Role-Playing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Side-Scrolling/Arcade_Games\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Side-Scrolling/Arcade_Games\">Side-Scrolling/Arcade Games</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Simulation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Simulation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Simulation\">Simulation</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Games/Entertainment_._Turn_Based_Strategy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Games/Entertainment :: Turn Based Strategy\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Games/Entertainment_._Turn_Based_Strategy\">Turn Based Strategy</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Home_Automation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Home Automation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Home_Automation\">Home Automation</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet\">Internet</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._File_Transfer_Protocol_(FTP)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: File Transfer Protocol (FTP)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._File_Transfer_Protocol_(FTP)\">File Transfer Protocol (FTP)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._Finger\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: Finger\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._Finger\">Finger</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._Log_Analysis\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: Log Analysis\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._Log_Analysis\">Log Analysis</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._Name_Service_(DNS)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: Name Service (DNS)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._Name_Service_(DNS)\">Name Service (DNS)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._Proxy_Servers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: Proxy Servers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._Proxy_Servers\">Proxy Servers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WAP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WAP\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WAP\">WAP</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP\">WWW/HTTP</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Browsers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Browsers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Browsers\">Browsers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content\">Dynamic Content</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._CGI_Tools/Libraries\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._CGI_Tools/Libraries\">CGI Tools/Libraries</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Content_Management_System\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Content_Management_System\">Content Management System</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Message_Boards\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Message_Boards\">Message Boards</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._News/Diary\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._News/Diary\">News/Diary</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Page_Counters\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Page_Counters\">Page Counters</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Wiki\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Wiki\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Dynamic_Content_._Wiki\">Wiki</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._HTTP_Servers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: HTTP Servers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._HTTP_Servers\">HTTP Servers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Indexing/Search\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Indexing/Search\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Indexing/Search\">Indexing/Search</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Session\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Session\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Session\">Session</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Site_Management\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Site Management\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Site_Management\">Site Management</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._Site_Management_._Link_Checking\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._Site_Management_._Link_Checking\">Link Checking</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._WSGI\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: WSGI\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._WSGI\">WSGI</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Application\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: WSGI :: Application\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Application\">Application</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Middleware\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Middleware\">Middleware</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Server\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: WWW/HTTP :: WSGI :: Server\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._WWW/HTTP_._WSGI_._Server\">Server</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._XMPP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: XMPP\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._XMPP\">XMPP</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Internet_._Z39.50\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Internet :: Z39.50\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Internet_._Z39.50\">Z39.50</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia\">Multimedia</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics\">Graphics</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._3D_Modeling\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: 3D Modeling\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._3D_Modeling\">3D Modeling</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._3D_Rendering\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: 3D Rendering\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._3D_Rendering\">3D Rendering</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Capture\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Capture\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Capture\">Capture</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Capture_._Digital_Camera\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Capture_._Digital_Camera\">Digital Camera</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Capture_._Scanners\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Capture :: Scanners\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Capture_._Scanners\">Scanners</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Capture_._Screen_Capture\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Capture :: Screen Capture\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Capture_._Screen_Capture\">Screen Capture</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Editors\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Editors\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Editors\">Editors</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Editors_._Raster-Based\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Editors :: Raster-Based\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Editors_._Raster-Based\">Raster-Based</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Editors_._Vector-Based\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Editors :: Vector-Based\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Editors_._Vector-Based\">Vector-Based</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Graphics_Conversion\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Graphics Conversion\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Graphics_Conversion\">Graphics Conversion</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Presentation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Presentation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Presentation\">Presentation</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Graphics_._Viewers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Graphics :: Viewers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Graphics_._Viewers\">Viewers</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio\">Sound/Audio</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Analysis\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Analysis\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Analysis\">Analysis</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Capture/Recording\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Capture/Recording\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Capture/Recording\">Capture/Recording</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: CD Audio\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio\">CD Audio</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Playing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Playing\">CD Playing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Ripping\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Ripping\">CD Ripping</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Writing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._CD_Audio_._CD_Writing\">CD Writing</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Conversion\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Conversion\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Conversion\">Conversion</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Editors\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Editors\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Editors\">Editors</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._MIDI\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: MIDI\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._MIDI\">MIDI</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Mixers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Mixers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Mixers\">Mixers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Players\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Players\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Players\">Players</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Players_._MP3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Players :: MP3\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Players_._MP3\">MP3</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Sound_Synthesis\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Sound Synthesis\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Sound_Synthesis\">Sound Synthesis</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Sound/Audio_._Speech\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Sound/Audio :: Speech\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Sound/Audio_._Speech\">Speech</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Video\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Video\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Video\">Video</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Video_._Capture\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Video :: Capture\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Video_._Capture\">Capture</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Video_._Conversion\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Video :: Conversion\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Video_._Conversion\">Conversion</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Video_._Display\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Video :: Display\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Video_._Display\">Display</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Multimedia_._Video_._Non-Linear_Editor\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Multimedia :: Video :: Non-Linear Editor\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Multimedia_._Video_._Non-Linear_Editor\">Non-Linear Editor</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business\">Office/Business</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Financial\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Financial\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Financial\">Financial</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Financial_._Accounting\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Financial :: Accounting\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Financial_._Accounting\">Accounting</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Financial_._Investment\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Financial :: Investment\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Financial_._Investment\">Investment</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Financial_._Point-Of-Sale\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Financial :: Point-Of-Sale\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Financial_._Point-Of-Sale\">Point-Of-Sale</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Financial_._Spreadsheet\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Financial :: Spreadsheet\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Financial_._Spreadsheet\">Spreadsheet</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Groupware\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Groupware\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Groupware\">Groupware</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._News/Diary\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: News/Diary\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._News/Diary\">News/Diary</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Office_Suites\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Office Suites\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Office_Suites\">Office Suites</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Office/Business_._Scheduling\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Office/Business :: Scheduling\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Office/Business_._Scheduling\">Scheduling</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Other/Nonlisted_Topic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Other/Nonlisted Topic\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Other/Nonlisted_Topic\">Other/Nonlisted Topic</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Printing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Printing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Printing\">Printing</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Religion\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Religion\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Religion\">Religion</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering\">Scientific/Engineering</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Artificial_Intelligence\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Artificial Intelligence\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Artificial_Intelligence\">Artificial Intelligence</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Artificial_Life\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Artificial Life\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Artificial_Life\">Artificial Life</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Astronomy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Astronomy\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Astronomy\">Astronomy</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Atmospheric_Science\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Atmospheric Science\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Atmospheric_Science\">Atmospheric Science</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Bio-Informatics\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Bio-Informatics\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Bio-Informatics\">Bio-Informatics</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Chemistry\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Chemistry\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Chemistry\">Chemistry</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Electronic_Design_Automation_(EDA)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Electronic_Design_Automation_(EDA)\">Electronic Design Automation (EDA)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._GIS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: GIS\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._GIS\">GIS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Human_Machine_Interfaces\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Human Machine Interfaces\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Human_Machine_Interfaces\">Human Machine Interfaces</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Hydrology\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Hydrology\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Hydrology\">Hydrology</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Image_Recognition\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Image Recognition\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Image_Recognition\">Image Recognition</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Information_Analysis\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Information Analysis\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Information_Analysis\">Information Analysis</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Interface_Engine/Protocol_Translator\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Interface_Engine/Protocol_Translator\">Interface Engine/Protocol Translator</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Mathematics\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Mathematics\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Mathematics\">Mathematics</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Medical_Science_Apps.\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Medical Science Apps.\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Medical_Science_Apps.\">Medical Science Apps.</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Physics\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Physics\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Physics\">Physics</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Scientific/Engineering_._Visualization\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Scientific/Engineering :: Visualization\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Scientific/Engineering_._Visualization\">Visualization</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Security\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Security\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Security\">Security</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Security_._Cryptography\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Security :: Cryptography\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Security_._Cryptography\">Cryptography</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Sociology\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Sociology\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Sociology\">Sociology</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Sociology_._Genealogy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Sociology :: Genealogy\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Sociology_._Genealogy\">Genealogy</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Sociology_._History\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Sociology :: History\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Sociology_._History\">History</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development\">Software Development</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Assemblers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Assemblers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Assemblers\">Assemblers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Bug_Tracking\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Bug Tracking\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Bug_Tracking\">Bug Tracking</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Build_Tools\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Build Tools\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Build_Tools\">Build Tools</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Code_Generators\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Code Generators\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Code_Generators\">Code Generators</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Compilers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Compilers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Compilers\">Compilers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Debuggers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Debuggers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Debuggers\">Debuggers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Disassemblers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Disassemblers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Disassemblers\">Disassemblers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Documentation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Documentation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Documentation\">Documentation</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Embedded_Systems\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Embedded Systems\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Embedded_Systems\">Embedded Systems</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Internationalization\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Internationalization\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Internationalization\">Internationalization</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Interpreters\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Interpreters\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Interpreters\">Interpreters</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries\">Libraries</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Application_Frameworks\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Application Frameworks\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Application_Frameworks\">Application Frameworks</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Java_Libraries\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Java Libraries\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Java_Libraries\">Java Libraries</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Perl_Modules\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Perl Modules\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Perl_Modules\">Perl Modules</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._PHP_Classes\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: PHP Classes\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._PHP_Classes\">PHP Classes</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Pike_Modules\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Pike Modules\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Pike_Modules\">Pike Modules</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._pygame\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: pygame\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._pygame\">pygame</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Python_Modules\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Python Modules\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Python_Modules\">Python Modules</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Ruby_Modules\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Ruby Modules\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Ruby_Modules\">Ruby Modules</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Libraries_._Tcl_Extensions\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Libraries :: Tcl Extensions\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Libraries_._Tcl_Extensions\">Tcl Extensions</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Localization\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Localization\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Localization\">Localization</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Object_Brokering\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Object Brokering\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Object_Brokering\">Object Brokering</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Object_Brokering_._CORBA\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Object Brokering :: CORBA\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Object_Brokering_._CORBA\">CORBA</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Pre-processors\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Pre-processors\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Pre-processors\">Pre-processors</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Quality_Assurance\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Quality Assurance\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Quality_Assurance\">Quality Assurance</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing\">Testing</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing_._Acceptance\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing :: Acceptance\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing_._Acceptance\">Acceptance</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing_._BDD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing :: BDD\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing_._BDD\">BDD</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing_._Mocking\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing :: Mocking\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing_._Mocking\">Mocking</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing_._Traffic_Generation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing :: Traffic Generation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing_._Traffic_Generation\">Traffic Generation</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Testing_._Unit\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Testing :: Unit\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Testing_._Unit\">Unit</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._User_Interfaces\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: User Interfaces\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._User_Interfaces\">User Interfaces</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control\">Version Control</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._Bazaar\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: Bazaar\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._Bazaar\">Bazaar</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._CVS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: CVS\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._CVS\">CVS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._Git\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: Git\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._Git\">Git</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._Mercurial\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: Mercurial\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._Mercurial\">Mercurial</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._RCS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: RCS\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._RCS\">RCS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Version_Control_._SCCS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Version Control :: SCCS\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Version_Control_._SCCS\">SCCS</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Software_Development_._Widget_Sets\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Software Development :: Widget Sets\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Software_Development_._Widget_Sets\">Widget Sets</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System\">System</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Archiving\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Archiving\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Archiving\">Archiving</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Archiving_._Backup\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Archiving :: Backup\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Archiving_._Backup\">Backup</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Archiving_._Compression\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Archiving :: Compression\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Archiving_._Compression\">Compression</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Archiving_._Mirroring\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Archiving :: Mirroring\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Archiving_._Mirroring\">Mirroring</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Archiving_._Packaging\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Archiving :: Packaging\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Archiving_._Packaging\">Packaging</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Benchmark\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Benchmark\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Benchmark\">Benchmark</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Boot\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Boot\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Boot\">Boot</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Boot_._Init\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Boot :: Init\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Boot_._Init\">Init</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Clustering\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Clustering\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Clustering\">Clustering</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Console_Fonts\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Console Fonts\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Console_Fonts\">Console Fonts</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Distributed_Computing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Distributed Computing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Distributed_Computing\">Distributed Computing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Emulators\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Emulators\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Emulators\">Emulators</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Filesystems\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Filesystems\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Filesystems\">Filesystems</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Hardware\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Hardware\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Hardware\">Hardware</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Hardware_._Hardware_Drivers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Hardware :: Hardware Drivers\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Hardware_._Hardware_Drivers\">Hardware Drivers</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Hardware_._Mainframes\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Hardware :: Mainframes\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Hardware_._Mainframes\">Mainframes</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Hardware_._Symmetric_Multi-processing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Hardware :: Symmetric Multi-processing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Hardware_._Symmetric_Multi-processing\">Symmetric Multi-processing</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Installation/Setup\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Installation/Setup\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Installation/Setup\">Installation/Setup</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Logging\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Logging\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Logging\">Logging</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Monitoring\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Monitoring\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Monitoring\">Monitoring</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Networking\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Networking\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Networking\">Networking</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Networking_._Firewalls\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Networking :: Firewalls\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Networking_._Firewalls\">Firewalls</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Networking_._Monitoring\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Networking :: Monitoring\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Networking_._Monitoring\">Monitoring</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Networking_._Monitoring_._Hardware_Watchdog\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Networking :: Monitoring :: Hardware Watchdog\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Networking_._Monitoring_._Hardware_Watchdog\">Hardware Watchdog</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Networking_._Time_Synchronization\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Networking :: Time Synchronization\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Networking_._Time_Synchronization\">Time Synchronization</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Operating_System\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Operating System\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Operating_System\">Operating System</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Operating_System_Kernels\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Operating System Kernels\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Operating_System_Kernels\">Operating System Kernels</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Operating_System_Kernels_._BSD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Operating System Kernels :: BSD\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Operating_System_Kernels_._BSD\">BSD</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Operating_System_Kernels_._GNU_Hurd\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Operating System Kernels :: GNU Hurd\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Operating_System_Kernels_._GNU_Hurd\">GNU Hurd</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Operating_System_Kernels_._Linux\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Operating System Kernels :: Linux\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Operating_System_Kernels_._Linux\">Linux</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Power_(UPS)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Power (UPS)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Power_(UPS)\">Power (UPS)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Recovery_Tools\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Recovery Tools\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Recovery_Tools\">Recovery Tools</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Shells\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Shells\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Shells\">Shells</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Software_Distribution\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Software Distribution\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Software_Distribution\">Software Distribution</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Systems_Administration\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Systems Administration\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Systems_Administration\">Systems Administration</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Systems_Administration_._Authentication/Directory\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Systems Administration :: Authentication/Directory\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Systems_Administration_._Authentication/Directory\">Authentication/Directory</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._Systems_Administration_._Authentication/Directory_._LDAP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._Systems_Administration_._Authentication/Directory_._LDAP\">LDAP</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._System_._System_Shells\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: System :: System Shells\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._System_._System_Shells\">System Shells</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Terminals\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Terminals\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Terminals\">Terminals</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Terminals_._Serial\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Terminals :: Serial\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Terminals_._Serial\">Serial</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Terminals_._Telnet\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Terminals :: Telnet\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Terminals_._Telnet\">Telnet</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Terminals_._Terminal_Emulators/X_Terminals\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Terminals :: Terminal Emulators/X Terminals\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Terminals_._Terminal_Emulators/X_Terminals\">Terminal Emulators/X Terminals</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors\">Text Editors</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors_._Documentation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors :: Documentation\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors_._Documentation\">Documentation</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors_._Emacs\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors :: Emacs\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors_._Emacs\">Emacs</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors_._Integrated_Development_Environments_(IDE)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors :: Integrated Development Environments (IDE)\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors_._Integrated_Development_Environments_(IDE)\">Integrated Development Environments (IDE)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors_._Text_Processing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors :: Text Processing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors_._Text_Processing\">Text Processing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Editors_._Word_Processors\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Editors :: Word Processors\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Editors_._Word_Processors\">Word Processors</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing\">Text Processing</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Filters\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Filters\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Filters\">Filters</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Fonts\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Fonts\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Fonts\">Fonts</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._General\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: General\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._General\">General</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Indexing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Indexing\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Indexing\">Indexing</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Linguistic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Linguistic\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Linguistic\">Linguistic</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup\">Markup</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup_._HTML\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup :: HTML\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup_._HTML\">HTML</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup_._LaTeX\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup :: LaTeX\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup_._LaTeX\">LaTeX</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup_._SGML\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup :: SGML\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup_._SGML\">SGML</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup_._VRML\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup :: VRML\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup_._VRML\">VRML</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Text_Processing_._Markup_._XML\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Text Processing :: Markup :: XML\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Text_Processing_._Markup_._XML\">XML</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Topic_._Utilities\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Topic :: Utilities\">\n    <label class=\"checkbox-tree__label\" for=\"Topic_._Utilities\">Utilities</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Development_Status\">Development Status</button>\n            <div id=\"accordion-Development_Status\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._1_-_Planning\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 1 - Planning\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._1_-_Planning\">1 - Planning</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._2_-_Pre-Alpha\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 2 - Pre-Alpha\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._2_-_Pre-Alpha\">2 - Pre-Alpha</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._3_-_Alpha\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 3 - Alpha\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._3_-_Alpha\">3 - Alpha</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._4_-_Beta\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 4 - Beta\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._4_-_Beta\">4 - Beta</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._5_-_Production/Stable\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 5 - Production/Stable\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._5_-_Production/Stable\">5 - Production/Stable</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._6_-_Mature\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 6 - Mature\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._6_-_Mature\">6 - Mature</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Development_Status_._7_-_Inactive\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Development Status :: 7 - Inactive\">\n    <label class=\"checkbox-tree__label\" for=\"Development_Status_._7_-_Inactive\">7 - Inactive</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-License\">License</button>\n            <div id=\"accordion-License\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Aladdin_Free_Public_License_(AFPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Aladdin Free Public License (AFPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Aladdin_Free_Public_License_(AFPL)\">Aladdin Free Public License (AFPL)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._CC0_1.0_Universal_(CC0_1.0)_Public_Domain_Dedication\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication\">\n    <label class=\"checkbox-tree__label\" for=\"License_._CC0_1.0_Universal_(CC0_1.0)_Public_Domain_Dedication\">CC0 1.0 Universal (CC0 1.0) Public Domain Dedication</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._CeCILL-B_Free_Software_License_Agreement_(CECILL-B)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: CeCILL-B Free Software License Agreement (CECILL-B)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._CeCILL-B_Free_Software_License_Agreement_(CECILL-B)\">CeCILL-B Free Software License Agreement (CECILL-B)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._CeCILL-C_Free_Software_License_Agreement_(CECILL-C)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: CeCILL-C Free Software License Agreement (CECILL-C)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._CeCILL-C_Free_Software_License_Agreement_(CECILL-C)\">CeCILL-C Free Software License Agreement (CECILL-C)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._DFSG_approved\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: DFSG approved\">\n    <label class=\"checkbox-tree__label\" for=\"License_._DFSG_approved\">DFSG approved</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Eiffel_Forum_License_(EFL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Eiffel Forum License (EFL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Eiffel_Forum_License_(EFL)\">Eiffel Forum License (EFL)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Free_For_Educational_Use\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Free For Educational Use\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Free_For_Educational_Use\">Free For Educational Use</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Free_For_Home_Use\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Free For Home Use\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Free_For_Home_Use\">Free For Home Use</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Free_for_non-commercial_use\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Free for non-commercial use\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Free_for_non-commercial_use\">Free for non-commercial use</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Freely_Distributable\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Freely Distributable\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Freely_Distributable\">Freely Distributable</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Free_To_Use_But_Restricted\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Free To Use But Restricted\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Free_To_Use_But_Restricted\">Free To Use But Restricted</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Freeware\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Freeware\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Freeware\">Freeware</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved\">OSI Approved</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Academic_Free_License_(AFL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Academic Free License (AFL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Academic_Free_License_(AFL)\">Academic Free License (AFL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Apache_Software_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Apache Software License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Apache_Software_License\">Apache Software License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Apple_Public_Source_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Apple Public Source License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Apple_Public_Source_License\">Apple Public Source License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Artistic_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Artistic License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Artistic_License\">Artistic License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Attribution_Assurance_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Attribution Assurance License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Attribution_Assurance_License\">Attribution Assurance License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Boost_Software_License_1.0_(BSL-1.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Boost Software License 1.0 (BSL-1.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Boost_Software_License_1.0_(BSL-1.0)\">Boost Software License 1.0 (BSL-1.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._BSD_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: BSD License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._BSD_License\">BSD License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._CEA_CNRS_Inria_Logiciel_Libre_License,_version_2.1_(CeCILL-2.1)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: CEA CNRS Inria Logiciel Libre License, version 2.1 (CeCILL-2.1)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._CEA_CNRS_Inria_Logiciel_Libre_License,_version_2.1_(CeCILL-2.1)\">CEA CNRS Inria Logiciel Libre License, version 2.1 (CeCILL-2.1)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Common_Development_and_Distribution_License_1.0_(CDDL-1.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Common Development and Distribution License 1.0 (CDDL-1.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Common_Development_and_Distribution_License_1.0_(CDDL-1.0)\">Common Development and Distribution License 1.0 (CDDL-1.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Common_Public_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Common Public License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Common_Public_License\">Common Public License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Eclipse_Public_License_1.0_(EPL-1.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Eclipse Public License 1.0 (EPL-1.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Eclipse_Public_License_1.0_(EPL-1.0)\">Eclipse Public License 1.0 (EPL-1.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Eclipse_Public_License_2.0_(EPL-2.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Eclipse Public License 2.0 (EPL-2.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Eclipse_Public_License_2.0_(EPL-2.0)\">Eclipse Public License 2.0 (EPL-2.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Eiffel_Forum_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Eiffel Forum License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Eiffel_Forum_License\">Eiffel Forum License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._European_Union_Public_Licence_1.0_(EUPL_1.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._European_Union_Public_Licence_1.0_(EUPL_1.0)\">European Union Public Licence 1.0 (EUPL 1.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._European_Union_Public_Licence_1.1_(EUPL_1.1)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._European_Union_Public_Licence_1.1_(EUPL_1.1)\">European Union Public Licence 1.1 (EUPL 1.1)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._European_Union_Public_Licence_1.2_(EUPL_1.2)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._European_Union_Public_Licence_1.2_(EUPL_1.2)\">European Union Public Licence 1.2 (EUPL 1.2)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Affero_General_Public_License_v3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Affero General Public License v3\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Affero_General_Public_License_v3\">GNU Affero General Public License v3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Affero_General_Public_License_v3_or_later_(AGPLv3+)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Affero_General_Public_License_v3_or_later_(AGPLv3+)\">GNU Affero General Public License v3 or later (AGPLv3+)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Free_Documentation_License_(FDL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Free Documentation License (FDL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Free_Documentation_License_(FDL)\">GNU Free Documentation License (FDL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_General_Public_License_(GPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU General Public License (GPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_General_Public_License_(GPL)\">GNU General Public License (GPL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_General_Public_License_v2_(GPLv2)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU General Public License v2 (GPLv2)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_General_Public_License_v2_(GPLv2)\">GNU General Public License v2 (GPLv2)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_General_Public_License_v2_or_later_(GPLv2+)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_General_Public_License_v2_or_later_(GPLv2+)\">GNU General Public License v2 or later (GPLv2+)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_General_Public_License_v3_(GPLv3)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU General Public License v3 (GPLv3)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_General_Public_License_v3_(GPLv3)\">GNU General Public License v3 (GPLv3)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_General_Public_License_v3_or_later_(GPLv3+)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_General_Public_License_v3_or_later_(GPLv3+)\">GNU General Public License v3 or later (GPLv3+)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v2_(LGPLv2)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v2_(LGPLv2)\">GNU Lesser General Public License v2 (LGPLv2)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v2_or_later_(LGPLv2+)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v2_or_later_(LGPLv2+)\">GNU Lesser General Public License v2 or later (LGPLv2+)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v3_(LGPLv3)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v3_(LGPLv3)\">GNU Lesser General Public License v3 (LGPLv3)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v3_or_later_(LGPLv3+)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Lesser_General_Public_License_v3_or_later_(LGPLv3+)\">GNU Lesser General Public License v3 or later (LGPLv3+)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._GNU_Library_or_Lesser_General_Public_License_(LGPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._GNU_Library_or_Lesser_General_Public_License_(LGPL)\">GNU Library or Lesser General Public License (LGPL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Historical_Permission_Notice_and_Disclaimer_(HPND)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Historical_Permission_Notice_and_Disclaimer_(HPND)\">Historical Permission Notice and Disclaimer (HPND)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._IBM_Public_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: IBM Public License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._IBM_Public_License\">IBM Public License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._ISC_License_(ISCL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: ISC License (ISCL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._ISC_License_(ISCL)\">ISC License (ISCL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._MirOS_License_(MirOS)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: MirOS License (MirOS)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._MirOS_License_(MirOS)\">MirOS License (MirOS)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._MIT_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: MIT License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._MIT_License\">MIT License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Mozilla_Public_License_1.0_(MPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Mozilla Public License 1.0 (MPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Mozilla_Public_License_1.0_(MPL)\">Mozilla Public License 1.0 (MPL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Mozilla_Public_License_1.1_(MPL_1.1)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Mozilla_Public_License_1.1_(MPL_1.1)\">Mozilla Public License 1.1 (MPL 1.1)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Mozilla_Public_License_2.0_(MPL_2.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Mozilla_Public_License_2.0_(MPL_2.0)\">Mozilla Public License 2.0 (MPL 2.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Nokia_Open_Source_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Nokia Open Source License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Nokia_Open_Source_License\">Nokia Open Source License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Open_Group_Test_Suite_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Open Group Test Suite License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Open_Group_Test_Suite_License\">Open Group Test Suite License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Open_Software_License_3.0_(OSL-3.0)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Open Software License 3.0 (OSL-3.0)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Open_Software_License_3.0_(OSL-3.0)\">Open Software License 3.0 (OSL-3.0)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._PostgreSQL_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: PostgreSQL License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._PostgreSQL_License\">PostgreSQL License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Python_License_(CNRI_Python_License)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Python License (CNRI Python License)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Python_License_(CNRI_Python_License)\">Python License (CNRI Python License)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Python_Software_Foundation_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Python Software Foundation License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Python_Software_Foundation_License\">Python Software Foundation License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Qt_Public_License_(QPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Qt Public License (QPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Qt_Public_License_(QPL)\">Qt Public License (QPL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._SIL_Open_Font_License_1.1_(OFL-1.1)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: SIL Open Font License 1.1 (OFL-1.1)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._SIL_Open_Font_License_1.1_(OFL-1.1)\">SIL Open Font License 1.1 (OFL-1.1)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Sleepycat_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Sleepycat License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Sleepycat_License\">Sleepycat License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Sun_Public_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Sun Public License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Sun_Public_License\">Sun Public License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Universal_Permissive_License_(UPL)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Universal Permissive License (UPL)\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Universal_Permissive_License_(UPL)\">Universal Permissive License (UPL)</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._University_of_Illinois/NCSA_Open_Source_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: University of Illinois/NCSA Open Source License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._University_of_Illinois/NCSA_Open_Source_License\">University of Illinois/NCSA Open Source License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._W3C_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: W3C License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._W3C_License\">W3C License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._zlib/libpng_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: zlib/libpng License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._zlib/libpng_License\">zlib/libpng License</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._OSI_Approved_._Zope_Public_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: OSI Approved :: Zope Public License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._OSI_Approved_._Zope_Public_License\">Zope Public License</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Other/Proprietary_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Other/Proprietary License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Other/Proprietary_License\">Other/Proprietary License</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Public_Domain\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Public Domain\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Public_Domain\">Public Domain</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"License_._Repoze_Public_License\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"License :: Repoze Public License\">\n    <label class=\"checkbox-tree__label\" for=\"License_._Repoze_Public_License\">Repoze Public License</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Programming_Language\">Programming Language</button>\n            <div id=\"accordion-Programming_Language\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._APL\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: APL\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._APL\">APL</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._ASP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: ASP\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._ASP\">ASP</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Assembly\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Assembly\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Assembly\">Assembly</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Awk\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Awk\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Awk\">Awk</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Basic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Basic\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Basic\">Basic</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._C\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: C\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._C\">C</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._C#\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: C#\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._C#\">C#</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._C++\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: C++\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._C++\">C++</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Cython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Cython\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Cython\">Cython</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Delphi/Kylix\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Delphi/Kylix\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Delphi/Kylix\">Delphi/Kylix</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Emacs-Lisp\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Emacs-Lisp\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Emacs-Lisp\">Emacs-Lisp</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Erlang\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Erlang\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Erlang\">Erlang</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Euler\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Euler\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Euler\">Euler</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Forth\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Forth\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Forth\">Forth</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Fortran\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Fortran\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Fortran\">Fortran</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Haskell\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Haskell\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Haskell\">Haskell</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Java\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Java\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Java\">Java</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._JavaScript\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: JavaScript\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._JavaScript\">JavaScript</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Lisp\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Lisp\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Lisp\">Lisp</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Logo\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Logo\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Logo\">Logo</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._ML\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: ML\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._ML\">ML</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Objective_C\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Objective C\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Objective_C\">Objective C</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Object_Pascal\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Object Pascal\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Object_Pascal\">Object Pascal</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._OCaml\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: OCaml\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._OCaml\">OCaml</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Other\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Other\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Other\">Other</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Other_Scripting_Engines\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Other Scripting Engines\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Other_Scripting_Engines\">Other Scripting Engines</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Pascal\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Pascal\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Pascal\">Pascal</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Perl\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Perl\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Perl\">Perl</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._PHP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: PHP\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._PHP\">PHP</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Pike\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Pike\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Pike\">Pike</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._PL/SQL\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: PL/SQL\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._PL/SQL\">PL/SQL</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Prolog\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Prolog\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Prolog\">Prolog</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python\">Python</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2\">2</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2_._Only\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2 :: Only\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2_._Only\">Only</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2.3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2.3\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2.3\">2.3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2.4\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2.4\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2.4\">2.4</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2.5\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2.5\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2.5\">2.5</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2.6\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2.6\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2.6\">2.6</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._2.7\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 2.7\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._2.7\">2.7</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3\">3</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3_._Only\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3 :: Only\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3_._Only\">Only</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.0\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.0\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.0\">3.0</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.1\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.1\">3.1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.2\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.2\">3.2</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.3\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.3\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.3\">3.3</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.4\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.4\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.4\">3.4</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.5\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.5\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.5\">3.5</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.6\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.6\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.6\">3.6</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.7\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.7\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.7\">3.7</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.8\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.8\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.8\">3.8</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._3.9\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: 3.9\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._3.9\">3.9</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation\">Implementation</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._CPython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: CPython\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._CPython\">CPython</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._IronPython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: IronPython\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._IronPython\">IronPython</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._Jython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: Jython\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._Jython\">Jython</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._MicroPython\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: MicroPython\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._MicroPython\">MicroPython</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._PyPy\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: PyPy\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._PyPy\">PyPy</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Python_._Implementation_._Stackless\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Python :: Implementation :: Stackless\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Python_._Implementation_._Stackless\">Stackless</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._R\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: R\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._R\">R</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._REBOL\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: REBOL\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._REBOL\">REBOL</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Rexx\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Rexx\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Rexx\">Rexx</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Ruby\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Ruby\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Ruby\">Ruby</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Rust\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Rust\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Rust\">Rust</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Scheme\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Scheme\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Scheme\">Scheme</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._SQL\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: SQL\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._SQL\">SQL</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Tcl\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Tcl\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Tcl\">Tcl</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Unix_Shell\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Unix Shell\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Unix_Shell\">Unix Shell</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Visual_Basic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Visual Basic\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Visual_Basic\">Visual Basic</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._YACC\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: YACC\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._YACC\">YACC</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Programming_Language_._Zope\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Programming Language :: Zope\">\n    <label class=\"checkbox-tree__label\" for=\"Programming_Language_._Zope\">Zope</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Operating_System\">Operating System</button>\n            <div id=\"accordion-Operating_System\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Android\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Android\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Android\">Android</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._BeOS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: BeOS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._BeOS\">BeOS</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._iOS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: iOS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._iOS\">iOS</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._MacOS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: MacOS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._MacOS\">MacOS</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._MacOS_._MacOS_9\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: MacOS :: MacOS 9\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._MacOS_._MacOS_9\">MacOS 9</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._MacOS_._MacOS_X\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: MacOS :: MacOS X\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._MacOS_._MacOS_X\">MacOS X</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft\">Microsoft</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._MS-DOS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: MS-DOS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._MS-DOS\">MS-DOS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows\">Windows</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_10\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 10\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_10\">Windows 10</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_3.1_or_Earlier\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_3.1_or_Earlier\">Windows 3.1 or Earlier</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_7\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 7\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_7\">Windows 7</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_8\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 8\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_8\">Windows 8</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_8.1\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 8.1\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_8.1\">Windows 8.1</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_95/98/2000\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows 95/98/2000\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_95/98/2000\">Windows 95/98/2000</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_CE\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows CE\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_CE\">Windows CE</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_NT/2000\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows NT/2000\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_NT/2000\">Windows NT/2000</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_Server_2003\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows Server 2003\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_Server_2003\">Windows Server 2003</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_Server_2008\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows Server 2008\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_Server_2008\">Windows Server 2008</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_Vista\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows Vista\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_Vista\">Windows Vista</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Microsoft_._Windows_._Windows_XP\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Microsoft :: Windows :: Windows XP\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Microsoft_._Windows_._Windows_XP\">Windows XP</label>\n  </li></ul>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._OS/2\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: OS/2\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._OS/2\">OS/2</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._OS_Independent\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: OS Independent\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._OS_Independent\">OS Independent</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Other_OS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Other OS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Other_OS\">Other OS</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._PalmOS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: PalmOS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._PalmOS\">PalmOS</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._PDA_Systems\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: PDA Systems\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._PDA_Systems\">PDA Systems</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX\">POSIX</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._AIX\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: AIX\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._AIX\">AIX</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._BSD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: BSD\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._BSD\">BSD</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._BSD_._BSD/OS\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: BSD :: BSD/OS\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._BSD_._BSD/OS\">BSD/OS</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._BSD_._FreeBSD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: BSD :: FreeBSD\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._BSD_._FreeBSD\">FreeBSD</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._BSD_._NetBSD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: BSD :: NetBSD\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._BSD_._NetBSD\">NetBSD</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._BSD_._OpenBSD\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: BSD :: OpenBSD\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._BSD_._OpenBSD\">OpenBSD</label>\n  </li></ul>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._GNU_Hurd\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: GNU Hurd\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._GNU_Hurd\">GNU Hurd</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._HP-UX\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: HP-UX\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._HP-UX\">HP-UX</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._IRIX\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: IRIX\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._IRIX\">IRIX</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._Linux\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: Linux\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._Linux\">Linux</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._Other\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: Other\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._Other\">Other</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._SCO\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: SCO\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._SCO\">SCO</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._POSIX_._SunOS/Solaris\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: POSIX :: SunOS/Solaris\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._POSIX_._SunOS/Solaris\">SunOS/Solaris</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Operating_System_._Unix\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Operating System :: Unix\">\n    <label class=\"checkbox-tree__label\" for=\"Operating_System_._Unix\">Unix</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Environment\">Environment</button>\n            <div id=\"accordion-Environment\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Console\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Console\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Console\">Console</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Console_._Curses\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Console :: Curses\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Console_._Curses\">Curses</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Console_._Framebuffer\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Console :: Framebuffer\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Console_._Framebuffer\">Framebuffer</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Console_._Newt\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Console :: Newt\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Console_._Newt\">Newt</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Console_._svgalib\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Console :: svgalib\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Console_._svgalib\">svgalib</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Handhelds/PDA's\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Handhelds/PDA's\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Handhelds/PDA's\">Handhelds/PDA's</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._MacOS_X\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: MacOS X\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._MacOS_X\">MacOS X</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._MacOS_X_._Aqua\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: MacOS X :: Aqua\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._MacOS_X_._Aqua\">Aqua</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._MacOS_X_._Carbon\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: MacOS X :: Carbon\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._MacOS_X_._Carbon\">Carbon</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._MacOS_X_._Cocoa\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: MacOS X :: Cocoa\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._MacOS_X_._Cocoa\">Cocoa</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._No_Input/Output_(Daemon)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: No Input/Output (Daemon)\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._No_Input/Output_(Daemon)\">No Input/Output (Daemon)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._OpenStack\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: OpenStack\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._OpenStack\">OpenStack</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Other_Environment\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Other Environment\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Other_Environment\">Other Environment</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Plugins\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Plugins\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Plugins\">Plugins</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Web_Environment\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Web Environment\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Web_Environment\">Web Environment</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Web_Environment_._Buffet\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Web Environment :: Buffet\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Web_Environment_._Buffet\">Buffet</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Web_Environment_._Mozilla\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Web Environment :: Mozilla\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Web_Environment_._Mozilla\">Mozilla</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Web_Environment_._ToscaWidgets\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Web Environment :: ToscaWidgets\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Web_Environment_._ToscaWidgets\">ToscaWidgets</label>\n  </li></ul>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._Win32_(MS_Windows)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: Win32 (MS Windows)\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._Win32_(MS_Windows)\">Win32 (MS Windows)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._X11_Applications\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: X11 Applications\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._X11_Applications\">X11 Applications</label>\n    <ul>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._X11_Applications_._Gnome\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: X11 Applications :: Gnome\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._X11_Applications_._Gnome\">Gnome</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._X11_Applications_._GTK\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: X11 Applications :: GTK\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._X11_Applications_._GTK\">GTK</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._X11_Applications_._KDE\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: X11 Applications :: KDE\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._X11_Applications_._KDE\">KDE</label>\n  </li>\n\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Environment_._X11_Applications_._Qt\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Environment :: X11 Applications :: Qt\">\n    <label class=\"checkbox-tree__label\" for=\"Environment_._X11_Applications_._Qt\">Qt</label>\n  </li></ul>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Intended_Audience\">Intended Audience</button>\n            <div id=\"accordion-Intended_Audience\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Customer_Service\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Customer Service\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Customer_Service\">Customer Service</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Developers\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Developers\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Developers\">Developers</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Education\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Education\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Education\">Education</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._End_Users/Desktop\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: End Users/Desktop\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._End_Users/Desktop\">End Users/Desktop</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Financial_and_Insurance_Industry\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Financial and Insurance Industry\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Financial_and_Insurance_Industry\">Financial and Insurance Industry</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Healthcare_Industry\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Healthcare Industry\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Healthcare_Industry\">Healthcare Industry</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Information_Technology\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Information Technology\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Information_Technology\">Information Technology</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Legal_Industry\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Legal Industry\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Legal_Industry\">Legal Industry</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Manufacturing\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Manufacturing\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Manufacturing\">Manufacturing</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Other_Audience\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Other Audience\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Other_Audience\">Other Audience</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Religion\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Religion\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Religion\">Religion</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Science/Research\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Science/Research\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Science/Research\">Science/Research</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._System_Administrators\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: System Administrators\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._System_Administrators\">System Administrators</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Intended_Audience_._Telecommunications_Industry\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Intended Audience :: Telecommunications Industry\">\n    <label class=\"checkbox-tree__label\" for=\"Intended_Audience_._Telecommunications_Industry\">Telecommunications Industry</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Natural_Language\">Natural Language</button>\n            <div id=\"accordion-Natural_Language\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Afrikaans\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Afrikaans\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Afrikaans\">Afrikaans</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Arabic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Arabic\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Arabic\">Arabic</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Bengali\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Bengali\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Bengali\">Bengali</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Bosnian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Bosnian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Bosnian\">Bosnian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Bulgarian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Bulgarian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Bulgarian\">Bulgarian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Cantonese\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Cantonese\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Cantonese\">Cantonese</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Catalan\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Catalan\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Catalan\">Catalan</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Chinese_(Simplified)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Chinese (Simplified)\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Chinese_(Simplified)\">Chinese (Simplified)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Chinese_(Traditional)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Chinese (Traditional)\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Chinese_(Traditional)\">Chinese (Traditional)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Croatian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Croatian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Croatian\">Croatian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Czech\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Czech\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Czech\">Czech</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Danish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Danish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Danish\">Danish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Dutch\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Dutch\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Dutch\">Dutch</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._English\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: English\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._English\">English</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Esperanto\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Esperanto\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Esperanto\">Esperanto</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Finnish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Finnish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Finnish\">Finnish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._French\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: French\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._French\">French</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Galician\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Galician\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Galician\">Galician</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._German\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: German\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._German\">German</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Greek\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Greek\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Greek\">Greek</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Hebrew\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Hebrew\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Hebrew\">Hebrew</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Hindi\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Hindi\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Hindi\">Hindi</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Hungarian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Hungarian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Hungarian\">Hungarian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Icelandic\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Icelandic\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Icelandic\">Icelandic</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Indonesian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Indonesian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Indonesian\">Indonesian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Italian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Italian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Italian\">Italian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Japanese\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Japanese\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Japanese\">Japanese</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Javanese\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Javanese\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Javanese\">Javanese</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Korean\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Korean\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Korean\">Korean</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Latin\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Latin\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Latin\">Latin</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Latvian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Latvian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Latvian\">Latvian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Macedonian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Macedonian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Macedonian\">Macedonian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Malay\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Malay\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Malay\">Malay</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Marathi\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Marathi\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Marathi\">Marathi</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Norwegian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Norwegian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Norwegian\">Norwegian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Panjabi\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Panjabi\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Panjabi\">Panjabi</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Persian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Persian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Persian\">Persian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Polish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Polish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Polish\">Polish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Portuguese\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Portuguese\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Portuguese\">Portuguese</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Portuguese_(Brazilian)\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Portuguese (Brazilian)\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Portuguese_(Brazilian)\">Portuguese (Brazilian)</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Romanian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Romanian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Romanian\">Romanian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Russian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Russian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Russian\">Russian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Serbian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Serbian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Serbian\">Serbian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Slovak\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Slovak\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Slovak\">Slovak</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Slovenian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Slovenian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Slovenian\">Slovenian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Spanish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Spanish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Spanish\">Spanish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Swedish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Swedish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Swedish\">Swedish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Tamil\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Tamil\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Tamil\">Tamil</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Telugu\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Telugu\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Telugu\">Telugu</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Thai\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Thai\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Thai\">Thai</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Tibetan\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Tibetan\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Tibetan\">Tibetan</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Turkish\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Turkish\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Turkish\">Turkish</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Ukrainian\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Ukrainian\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Ukrainian\">Ukrainian</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Urdu\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Urdu\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Urdu\">Urdu</label>\n  </li>\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Natural_Language_._Vietnamese\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Natural Language :: Vietnamese\">\n    <label class=\"checkbox-tree__label\" for=\"Natural_Language_._Vietnamese\">Vietnamese</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n\n          <div class=\"accordion accordion--closed\">\n            <button type=\"button\" class=\"accordion__link -js-accordion-trigger\" aria-expanded=\"false\" aria-controls=\"accordion-Typing\">Typing</button>\n            <div id=\"accordion-Typing\" aria-hidden=\"true\" class=\"accordion__content\">\n              <div class=\"checkbox-tree\">\n                <ul>\n\n\n\n\n  <li>\n    <input name=\"c\" type=\"checkbox\" id=\"Typing_._Typed\" class=\"-js-form-submit-trigger checkbox-tree__checkbox\" value=\"Typing :: Typed\">\n    <label class=\"checkbox-tree__label\" for=\"Typing_._Typed\">Typed</label>\n  </li>\n                </ul>\n              </div>\n            </div>\n          </div>\n\n\n        </form>\n      </div>\n    </div>\n\n    <div class=\"left-layout__main\">\n      <h2 class=\"sr-only\">Search results</h2>\n\n      <form action=\"/search/\">\n        <div class=\"split-layout split-layout--table split-layout--wrap-on-tablet\">\n          <div>\n\n            <p>\n\n\n\n\n\n              <strong>1,847</strong> projects\n\n\n\n                for \"sqlalchemy\"\n\n\n\n\n\n            </p>\n\n          </div>\n          <div>\n\n            <input id=\"search\" type=\"hidden\" name=\"q\" value=\"sqlalchemy\">\n            <label for=\"order\">Order by &nbsp;</label>\n            <select class=\"-js-form-submit-trigger\" id=\"order\" name=\"o\">\n              <option value=\"\" selected=\"selected\">Relevance</option>\n              <option value=\"-created\">Date last updated</option>\n              <option value=\"-zscore\">Trending</option>\n            </select>\n\n          </div>\n        </div>\n\n        <div class=\"applied-filters\">\n\n          <span class=\"applied-filters__add-button\">\n            <button type=\"button\" class=\"-js-add-filter button button--small\">Add filter</button>\n          </span>\n        </div>\n\n\n        <div>\n\n          <ul class=\"unstyled\" aria-label=\"Search results\">\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">SQLAlchemy</span>\n      <span class=\"package-snippet__version\">1.3.10</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Database Abstraction Library</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-dao/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">SQLAlchemy-Dao</span>\n      <span class=\"package-snippet__version\">1.3.1</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Simple wrapper for sqlalchemy.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/graphene-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">graphene-sqlalchemy</span>\n      <span class=\"package-snippet__version\">2.2.2</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Graphene SQLAlchemy integration</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-utcdatetime/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">SQLAlchemy-UTCDateTime</span>\n      <span class=\"package-snippet__version\">1.0.4</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Convert to/from timezone aware datetimes when storing in a DBMS</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/paginate-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">paginate_sqlalchemy</span>\n      <span class=\"package-snippet__version\">0.3.0</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Extension to paginate.Page that supports SQLAlchemy queries</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-audit/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy_audit</span>\n      <span class=\"package-snippet__version\">0.1.0</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">sqlalchemy-audit provides an easy way to set up revision tracking for your data.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/transmogrify-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">transmogrify.sqlalchemy</span>\n      <span class=\"package-snippet__version\">1.0.2</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Feed data from SQLAlchemy into a transmogrifier pipeline</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-schemadisplay/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy_schemadisplay</span>\n      <span class=\"package-snippet__version\">1.3</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Turn SQLAlchemy DB Model into a graph</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-traversal/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy_traversal</span>\n      <span class=\"package-snippet__version\">0.5.2</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">UNKNOWN</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-filters/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy-filters</span>\n      <span class=\"package-snippet__version\">0.10.0</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">A library to filter SQLAlchemy queries.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-wrap/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">SQLAlchemy-wrap</span>\n      <span class=\"package-snippet__version\">2.1.7</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Python wrapper for the CircleCI API</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-nav/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy-nav</span>\n      <span class=\"package-snippet__version\">0.0.2</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">SQLAlchemy-Nav provides SQLAlchemy Mixins for creating navigation bars compatible with Bootstrap</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-repr/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy-repr</span>\n      <span class=\"package-snippet__version\">0.0.1</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Automatically generates pretty repr of a SQLAlchemy model.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-diff/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy-diff</span>\n      <span class=\"package-snippet__version\">0.1.3</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Compare two database schemas using sqlalchemy.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-equivalence/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">SQLAlchemy-Equivalence</span>\n      <span class=\"package-snippet__version\">0.1.1</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Provides natural equivalence support for SQLAlchemy declarative models.</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/broadway-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">Broadway-SQLAlchemy</span>\n      <span class=\"package-snippet__version\">0.0.1</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">A broadway extension wrapping Flask-SQLAlchemy</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/jsonql-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">jsonql-sqlalchemy</span>\n      <span class=\"package-snippet__version\">1.0.1</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Simple JSON-Based CRUD Query Language for SQLAlchemy</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-plus/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy-plus</span>\n      <span class=\"package-snippet__version\">0.2.0</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Create Views and Materialized Views with SqlAlchemy</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/cherrypy-sqlalchemy/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">CherryPy-SQLAlchemy</span>\n      <span class=\"package-snippet__version\">0.5.3</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">Use SQLAlchemy with CherryPy</p>\n  </a>\n</li>\n\n              <li>\n  <a class=\"package-snippet\" href=\"https://pypi.org/project/sqlalchemy-sqlany/\">\n    <h3 class=\"package-snippet__title\">\n      <span class=\"package-snippet__name\">sqlalchemy_sqlany</span>\n      <span class=\"package-snippet__version\">1.0.3</span>\n\n    </h3>\n    <p class=\"package-snippet__description\">SAP Sybase SQL Anywhere dialect for SQLAlchemy</p>\n  </a>\n</li>\n\n          </ul>\n\n\n\n<div class=\"button-group button-group--pagination\">\n\n\n  <a class=\"button button-group__button button--disabled\">Previous</a>\n\n\n\n\n\n\n\n    <span class=\"button button-group__button button--primary\">1</span>\n\n\n\n    <a href=\"https://pypi.org/search/?q=sqlalchemy&amp;page=2\" class=\"button button-group__button\">2</a>\n\n\n\n    <a href=\"https://pypi.org/search/?q=sqlalchemy&amp;page=3\" class=\"button button-group__button\">3</a>\n\n\n\n    <span class=\"button button-group__button\">...</span>\n\n\n\n\n\n    <a href=\"https://pypi.org/search/?q=sqlalchemy&amp;page=93\" class=\"button button-group__button\">93</a>\n\n\n\n\n    <a href=\"https://pypi.org/search/?q=sqlalchemy&amp;page=2\" class=\"button button-group__button\">Next</a>\n\n</div>\n\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n\n    </main>\n\n    <footer class=\"footer\">\n      <div class=\"footer__logo\">\n        <img src=\"search_files/white-cube.svg\" alt=\"\" class=\"-js-white-cube\">\n      </div>\n\n      <div class=\"footer__menus\">\n        <div class=\"footer__menu\">\n          <h2>Help</h2>\n          <nav aria-label=\"Help navigation\">\n            <ul>\n              <li><a href=\"https://packaging.python.org/installing/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Installing packages</a></li>\n              <li><a href=\"https://packaging.python.org/tutorials/packaging-projects/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Uploading packages</a></li>\n              <li><a href=\"https://packaging.python.org/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">User guide</a></li>\n              <li><a href=\"https://pypi.org/help/\">FAQs</a></li>\n            </ul>\n          </nav>\n        </div>\n\n        <div class=\"footer__menu\">\n          <h2>About PyPI</h2>\n          <nav aria-label=\"About PyPI navigation\">\n            <ul>\n              <li><a href=\"https://twitter.com/PyPI\" title=\"External link\" target=\"_blank\" rel=\"noopener\">PyPI on Twitter</a></li>\n              <li><a href=\"https://dtdg.co/pypi\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Infrastructure dashboard</a></li>\n              <li><a href=\"https://www.python.org/dev/peps/pep-0541/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Package index name retention</a></li>\n              <li><a href=\"https://pypi.org/sponsors/\">Our sponsors</a></li>\n            </ul>\n          </nav>\n        </div>\n\n        <div class=\"footer__menu\">\n          <h2>Contributing to PyPI</h2>\n          <nav aria-label=\"How to contribute navigation\">\n            <ul>\n              <li><a href=\"https://pypi.org/help/#feedback\">Bugs and feedback</a></li>\n              <li><a href=\"https://github.com/pypa/warehouse\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Contribute on GitHub</a></li>\n              <li><a href=\"https://hosted.weblate.org/projects/pypa/warehouse/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Translate PyPI</a></li>\n              <li><a href=\"https://github.com/pypa/warehouse/graphs/contributors\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Development credits</a></li>\n            </ul>\n          </nav>\n        </div>\n\n        <div class=\"footer__menu\">\n          <h2>Using PyPI</h2>\n          <nav aria-label=\"Using PyPI navigation\">\n            <ul>\n              <li><a href=\"https://www.pypa.io/en/latest/code-of-conduct/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Code of conduct</a></li>\n              <li><a href=\"https://pypi.org/security/\">Report security issue</a></li>\n              <li><a href=\"https://www.python.org/privacy/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Privacy policy</a></li>\n              <li><a href=\"https://pypi.org/policy/terms-of-use/\">Terms of use</a></li>\n            </ul>\n          </nav>\n        </div>\n      </div>\n\n      <hr class=\"footer__divider\">\n\n      <div class=\"footer__text\">\n\n        <p>Status: <a href=\"https://status.python.org/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">\n          <span data-statuspage-domain=\"https://2p66nmmycsj3.statuspage.io\">All Systems Operational</span></a>\n        </p>\n\n        <p>\n          Developed and maintained by the Python community, for the Python community.\n          <br>\n          <a href=\"https://donate.pypi.org/\">Donate today!</a>\n        </p>\n        <p>© 2019 <a href=\"https://www.python.org/psf/\" title=\"External link\" target=\"_blank\" rel=\"noopener\">Python Software Foundation</a><br>\n          <a href=\"https://pypi.org/sitemap/\">Site map</a>\n        </p>\n      </div>\n\n      <div class=\"centered hide-on-desktop\">\n        <button type=\"button\" class=\"button button--switch-to-desktop\" data-target=\"viewport-toggle.switchToDesktop\" data-action=\"viewport-toggle#switchToDesktop\">\n          Switch to desktop version\n        </button>\n      </div>\n    </footer>\n\n\n    <div class=\"language-switcher\">\n      <form action=\"/locale/\">\n        <ul>\n\n          <li>\n            <button class=\"language-switcher__selected\" name=\"locale_id\" value=\"en\" type=\"submit\">English</button>\n          </li>\n\n          <li>\n            <button class=\"\" name=\"locale_id\" value=\"es\" type=\"submit\">Spanish</button>\n          </li>\n\n          <li>\n            <button class=\"\" name=\"locale_id\" value=\"fr\" type=\"submit\">French</button>\n          </li>\n\n          <li>\n            <button class=\"\" name=\"locale_id\" value=\"ja\" type=\"submit\">Japanese</button>\n          </li>\n\n          <li>\n            <button class=\"\" name=\"locale_id\" value=\"pt_BR\" type=\"submit\">Portuguese (Brazil)</button>\n          </li>\n\n          <li>\n            <button class=\"\" name=\"locale_id\" value=\"uk\" type=\"submit\">Ukrainian</button>\n          </li>\n\n        </ul>\n      </form>\n    </div>\n\n\n\n\n<div class=\"sponsors\">\n  <p class=\"sponsors__title\">Supported by</p>\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.elastic.co/\">\n        <img class=\"sponsors__image\" alt=\"Elastic\" src=\"search_files/elastic.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">Elastic</span>\n        <span class=\"sponsors__service\">Search</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.pingdom.com/\">\n        <img class=\"sponsors__image\" alt=\"Pingdom\" src=\"search_files/pingdom.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">Pingdom</span>\n        <span class=\"sponsors__service\">Monitoring</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://cloud.google.com/\">\n        <img class=\"sponsors__image\" alt=\"Google\" src=\"search_files/google.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">Google</span>\n        <span class=\"sponsors__service\">BigQuery</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://getsentry.com/for/python\">\n        <img class=\"sponsors__image\" alt=\"Sentry\" src=\"search_files/sentry.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">Sentry</span>\n        <span class=\"sponsors__service\">Error logging</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://aws.amazon.com/\">\n        <img class=\"sponsors__image\" alt=\"AWS\" src=\"search_files/aws.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">AWS</span>\n        <span class=\"sponsors__service\">Cloud computing</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.datadoghq.com/\">\n        <img class=\"sponsors__image\" alt=\"DataDog\" src=\"search_files/datadog.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">DataDog</span>\n        <span class=\"sponsors__service\">Monitoring</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.fastly.com/\">\n        <img class=\"sponsors__image\" alt=\"Fastly\" src=\"search_files/fastly.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">Fastly</span>\n        <span class=\"sponsors__service\">CDN</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.signalfx.com/\">\n        <img class=\"sponsors__image\" alt=\"SignalFx\" src=\"search_files/signalfx.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">SignalFx</span>\n        <span class=\"sponsors__service\">Supporter</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://www.digicert.com/\">\n        <img class=\"sponsors__image\" alt=\"DigiCert\" src=\"search_files/digicert.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">DigiCert</span>\n        <span class=\"sponsors__service\">EV certificate</span>\n      </a>\n\n\n\n      <a class=\"sponsors__sponsor\" target=\"_blank\" rel=\"noopener\" href=\"https://statuspage.io/\">\n        <img class=\"sponsors__image\" alt=\"StatusPage\" src=\"search_files/statuspage.png\" style=\"display: none !important;\" hidden=\"\">\n        <span class=\"sponsors__name\">StatusPage</span>\n        <span class=\"sponsors__service\">Status page</span>\n      </a>\n\n\n</div>\n\n\n\n\n</body></html>\n"
  },
  {
    "path": "tests/repositories/fixtures/pypi.py",
    "content": "from __future__ import annotations\n\nimport json\nimport re\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nimport pytest\nimport responses\n\nfrom packaging.utils import parse_sdist_filename\nfrom packaging.utils import parse_wheel_filename\n\nfrom poetry.repositories.pypi_repository import PyPiRepository\nfrom tests.helpers import FIXTURE_PATH_DISTRIBUTIONS\nfrom tests.helpers import FIXTURE_PATH_REPOSITORIES_PYPI\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from pathlib import Path\n\n    from requests import PreparedRequest\n\n    from tests.types import HttpRequestCallback\n    from tests.types import HttpResponse\n    from tests.types import PackageDistributionLookup\n\n\npytest_plugins = [\n    \"tests.repositories.fixtures.legacy\",\n    \"tests.repositories.fixtures.python_hosted\",\n]\n\n\n@pytest.fixture\ndef package_distribution_locations() -> list[Path]:\n    return [\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"dists\",\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"dists\" / \"mocked\",\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"stubbed\",\n        FIXTURE_PATH_DISTRIBUTIONS,\n    ]\n\n\n@pytest.fixture\ndef package_json_locations() -> list[Path]:\n    return [\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"json\",\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"json\" / \"mocked\",\n    ]\n\n\n@pytest.fixture\ndef package_metadata_locations() -> list[Path]:\n    return [\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"metadata\",\n        FIXTURE_PATH_REPOSITORIES_PYPI / \"metadata\" / \"mocked\",\n    ]\n\n\n@pytest.fixture\ndef package_distribution_lookup(\n    package_distribution_locations: list[Path],\n) -> PackageDistributionLookup:\n    def lookup(name: str) -> Path | None:\n        for location in package_distribution_locations:\n            fixture = location / name\n            if fixture.exists():\n                return fixture\n        return None\n\n    return lookup\n\n\n@pytest.fixture\ndef with_disallowed_pypi_search_html(\n    http: responses.RequestsMock, pypi_repository: PyPiRepository\n) -> None:\n    def search_callback(request: PreparedRequest) -> HttpResponse:\n        search_html = FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\n            \"search\", \"search-disallowed.html\"\n        )\n        return 200, {}, search_html.read_bytes()\n\n    search_url_regex = re.compile(r\"https://pypi\\.org/search(\\?(.*))?$\")\n    http.remove(responses.GET, search_url_regex)\n    http.add_callback(\n        responses.GET,\n        search_url_regex,\n        callback=search_callback,\n    )\n\n\n@pytest.fixture(autouse=True)\ndef pypi_repository(\n    http: responses.RequestsMock,\n    legacy_repository_html_callback: HttpRequestCallback,\n    package_json_locations: list[Path],\n    mock_files_python_hosted: None,\n) -> PyPiRepository:\n    def default_callback(request: PreparedRequest) -> HttpResponse:\n        return 404, {}, b\"Not Found\"\n\n    def search_callback(request: PreparedRequest) -> HttpResponse:\n        search_html = FIXTURE_PATH_REPOSITORIES_PYPI.joinpath(\"search\", \"search.html\")\n        return 200, {}, search_html.read_bytes()\n\n    def simple_callback(request: PreparedRequest) -> HttpResponse:\n        if request.headers.get(\"Accept\") == \"application/vnd.pypi.simple.v1+json\":\n            return json_callback(request)\n        return legacy_repository_html_callback(request)\n\n    def _get_json_filepath(name: str, version: str | None = None) -> Path | None:\n        for base in package_json_locations:\n            if not version:\n                fixture = base / f\"{name}.json\"\n            else:\n                fixture = base / name / f\"{version}.json\"\n\n            if fixture.exists():\n                return fixture\n\n        return None\n\n    def json_callback(request: PreparedRequest) -> HttpResponse:\n        assert request.url\n        path = urlparse(request.url).path\n        parts = path.rstrip(\"/\").split(\"/\")[2:]\n        name = parts[0]\n        version = parts[1] if len(parts) == 3 else None\n        fixture = _get_json_filepath(name, version)\n\n        if fixture is None or not fixture.exists():\n            return default_callback(request)\n\n        return 200, {}, fixture.read_bytes()\n\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"https://pypi\\.org/search(\\?(.*))?$\"),\n        callback=search_callback,\n    )\n\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"https://pypi\\.org/pypi/(.*)?/json$\"),\n        callback=json_callback,\n    )\n\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"https://pypi\\.org/pypi/(?!.*?/json$)(.*)$\"),\n        callback=default_callback,\n    )\n\n    http.add_callback(\n        responses.GET,\n        re.compile(r\"https://pypi\\.org/simple/?(.*)?$\"),\n        callback=simple_callback,\n    )\n\n    return PyPiRepository(disable_cache=True, fallback=False)\n\n\n@pytest.fixture\ndef get_pypi_file_info(\n    package_json_locations: list[Path],\n) -> Callable[[str], dict[str, Any]]:\n    def get_file_info(name: str) -> dict[str, Any]:\n        if name.endswith(\".whl\"):\n            package_name, version, _build, _tags = parse_wheel_filename(name)\n        else:\n            package_name, version = parse_sdist_filename(name)\n        path = package_json_locations[0] / package_name\n        if not path.exists():\n            raise RuntimeError(\n                f\"Fixture for {package_name} not found in pypi.org json fixtures\"\n            )\n        path /= f\"{version}.json\"\n        if not path.exists():\n            raise RuntimeError(\n                f\"Fixture for {package_name} {version} not found in pypi.org json fixtures\"\n            )\n        with path.open(\"rb\") as f:\n            content = json.load(f)\n        for url in content[\"urls\"]:\n            if url[\"filename\"] == name:\n                return url  # type: ignore[no-any-return]\n        raise RuntimeError(f\"No URL in pypi.org json fixture of {name} {version}\")\n\n    return get_file_info\n"
  },
  {
    "path": "tests/repositories/fixtures/python_hosted.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom urllib.parse import urlparse\n\nimport pytest\nimport responses\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from requests import PreparedRequest\n\n    from tests.types import HttpResponse\n    from tests.types import PythonHostedFileMocker\n\n\n@pytest.fixture\ndef mock_files_python_hosted_factory(\n    http: responses.RequestsMock,\n) -> PythonHostedFileMocker:\n    def factory(\n        distribution_locations: list[Path], metadata_locations: list[Path]\n    ) -> None:\n        def file_callback(request: PreparedRequest) -> HttpResponse:\n            assert request.url\n            name = Path(urlparse(request.url).path).name\n\n            locations = (\n                metadata_locations\n                if name.endswith(\".metadata\")\n                else distribution_locations\n            )\n\n            for location in locations:\n                fixture = location / name\n                if fixture.exists():\n                    return 200, {}, fixture.read_bytes()\n\n            return 404, {}, b\"Not Found\"\n\n        def mock_file_callback(request: PreparedRequest) -> HttpResponse:\n            return 200, {}, b\"\"\n\n        http.add_callback(\n            responses.GET,\n            re.compile(r\"^https://files\\.pythonhosted\\.org/.*$\"),\n            callback=file_callback,\n        )\n\n        http.add_callback(\n            responses.GET,\n            re.compile(r\"^https://mock\\.pythonhosted\\.org/.*$\"),\n            callback=mock_file_callback,\n        )\n\n    return factory\n\n\n@pytest.fixture\ndef mock_files_python_hosted(\n    mock_files_python_hosted_factory: PythonHostedFileMocker,\n    package_distribution_locations: list[Path],\n    package_metadata_locations: list[Path],\n) -> Iterator[None]:\n    mock_files_python_hosted_factory(\n        distribution_locations=package_distribution_locations,\n        metadata_locations=package_metadata_locations,\n    )\n    yield None\n"
  },
  {
    "path": "tests/repositories/fixtures/single-page/jax_releases.html",
    "content": "<!DOCTYPE html>\n<html>\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head>\n<body>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.0-cp310-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.0-cp310-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.0-cp37-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.0-cp37-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.0-cp38-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.0-cp38-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.0-cp39-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.0-cp39-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.2-cp310-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.2-cp310-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.2-cp37-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.2-cp37-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.2-cp38-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.2-cp38-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.2-cp39-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.2-cp39-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.5-cp310-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.5-cp310-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.5-cp37-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.5-cp37-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.5-cp38-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.5-cp38-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.5-cp39-none-manylinux2010_x86_64.whl\">nocuda/jaxlib-0.3.5-cp39-none-manylinux2010_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.7-cp310-none-manylinux2014_x86_64.whl\">nocuda/jaxlib-0.3.7-cp310-none-manylinux2014_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.7-cp37-none-manylinux2014_x86_64.whl\">nocuda/jaxlib-0.3.7-cp37-none-manylinux2014_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.7-cp38-none-manylinux2014_x86_64.whl\">nocuda/jaxlib-0.3.7-cp38-none-manylinux2014_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/nocuda/jaxlib-0.3.7-cp39-none-manylinux2014_x86_64.whl\">nocuda/jaxlib-0.3.7-cp39-none-manylinux2014_x86_64.whl</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/jax/jax-0.3.0.tar.gz\">jax/jax-0.3.0.tar.gz</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/jax/jax-0.3.2.tar.gz\">jax/jax-0.3.2.tar.gz</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/jax/jax-0.3.5.tar.gz\">jax/jax-0.3.5.tar.gz</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/jax/jax-0.3.6.tar.gz\">jax/jax-0.3.6.tar.gz</a><br>\n<a href=\"https://storage.googleapis.com/jax-releases/jax/jax-0.3.7.tar.gz\">jax/jax-0.3.7.tar.gz</a><br>\n</body>\n</html>\n"
  },
  {
    "path": "tests/repositories/fixtures/single-page/mmcv_torch_releases.html",
    "content": "<a href=\"../torch1.12.0/mmcv-2.0.0-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc1-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc1-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc2-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc2-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc3-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc3-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.0rc4-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.0rc4-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.1-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.0.1-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.1-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.0.1-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.1-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.0.1-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.0.1-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.0.1-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.0.1-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.1.0-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv-2.1.0-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.1.0-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv-2.1.0-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.1.0-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv-2.1.0-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv-2.1.0-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv-2.1.0-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv-2.1.0-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.0-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.0-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.1-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.1-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.6.2-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.6.2-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.0-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.0-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.1-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.1-cp39-cp39-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp310-cp310-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp310-cp310-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp310-cp310-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp310-cp310-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp37-cp37m-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp37-cp37m-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp37-cp37m-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp37-cp37m-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp38-cp38-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp38-cp38-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp38-cp38-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp38-cp38-win_amd64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp39-cp39-manylinux1_x86_64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp39-cp39-manylinux1_x86_64.whl</a><br>\n<a href=\"../torch1.12.0/mmcv_full-1.7.2-cp39-cp39-win_amd64.whl\">../torch1.12.0/mmcv_full-1.7.2-cp39-cp39-win_amd64.whl</a><br>\n"
  },
  {
    "path": "tests/repositories/link_sources/__init__.py",
    "content": ""
  },
  {
    "path": "tests/repositories/link_sources/test_base.py",
    "content": "from __future__ import annotations\n\nfrom collections import defaultdict\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\nfrom unittest.mock import PropertyMock\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.repositories.link_sources.base import LinkSource\nfrom poetry.repositories.link_sources.base import SimpleRepositoryRootPage\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    from pytest_mock import MockerFixture\n\n\n@pytest.fixture\ndef root_page() -> SimpleRepositoryRootPage:\n    class TestRootPage(SimpleRepositoryRootPage):\n        @cached_property\n        def package_names(self) -> list[str]:\n            return [\"poetry\", \"poetry-core\", \"requests\", \"urllib3\"]\n\n    return TestRootPage()\n\n\n@pytest.fixture\ndef link_source(mocker: MockerFixture) -> LinkSource:\n    url = \"https://example.org\"\n    link_source = LinkSource(url)\n    mocker.patch(\n        f\"{LinkSource.__module__}.{LinkSource.__qualname__}._link_cache\",\n        new_callable=PropertyMock,\n        return_value=defaultdict(\n            lambda: defaultdict(list),\n            {\n                canonicalize_name(\"demo\"): defaultdict(\n                    list,\n                    {\n                        Version.parse(\"0.1.0\"): [\n                            Link(f\"{url}/demo-0.1.0.tar.gz\"),\n                            Link(f\"{url}/demo-0.1.0-py2.py3-none-any.whl\"),\n                        ],\n                        Version.parse(\"0.1.1\"): [Link(f\"{url}/demo-0.1.1.tar.gz\")],\n                    },\n                ),\n            },\n        ),\n    )\n    return link_source\n\n\n@pytest.mark.parametrize(\n    \"filename, expected\",\n    [\n        (\"demo-0.1.0-py2.py3-none-any.whl\", Package(\"demo\", \"0.1.0\")),\n        (\"demo-0.1.0.tar.gz\", Package(\"demo\", \"0.1.0\")),\n        (\"demo-0.1.0.egg\", Package(\"demo\", \"0.1.0\")),\n        (\"demo-0.1.0_invalid-py2.py3-none-any.whl\", None),  # invalid version\n        (\"demo-0.1.0_invalid.egg\", None),  # invalid version\n        (\"no-package-at-all.txt\", None),\n    ],\n)\ndef test_link_package_data(filename: str, expected: Package | None) -> None:\n    link = Link(f\"https://example.org/{filename}\")\n    assert LinkSource.link_package_data(link) == expected\n\n\n@pytest.mark.parametrize(\n    \"name, expected\",\n    [\n        (\"demo\", {Version.parse(\"0.1.0\"), Version.parse(\"0.1.1\")}),\n        (\"invalid\", set()),\n    ],\n)\ndef test_versions(name: str, expected: set[Version], link_source: LinkSource) -> None:\n    assert set(link_source.versions(canonicalize_name(name))) == expected\n\n\ndef test_packages(link_source: LinkSource) -> None:\n    expected = {\n        Package(\"demo\", \"0.1.0\"),\n        Package(\"demo\", \"0.1.0\"),\n        Package(\"demo\", \"0.1.1\"),\n    }\n    assert set(link_source.packages) == expected\n\n\n@pytest.mark.parametrize(\n    \"version_string, filenames\",\n    [\n        (\"0.1.0\", [\"demo-0.1.0.tar.gz\", \"demo-0.1.0-py2.py3-none-any.whl\"]),\n        (\"0.1.1\", [\"demo-0.1.1.tar.gz\"]),\n        (\"0.1.2\", []),\n    ],\n)\ndef test_links_for_version(\n    version_string: str, filenames: Iterable[str], link_source: LinkSource\n) -> None:\n    version = Version.parse(version_string)\n    expected = {Link(f\"{link_source.url}/{name}\") for name in filenames}\n    assert (\n        set(link_source.links_for_version(canonicalize_name(\"demo\"), version))\n        == expected\n    )\n\n\n@pytest.mark.parametrize(\n    \"query, expected\",\n    [\n        (\"poetry\", [\"poetry\", \"poetry-core\"]),\n        ([\"requests\", \"urllib3\"], [\"requests\", \"urllib3\"]),\n        (\"lib\", [\"urllib3\"]),\n        (\"nonexistent\", []),\n    ],\n)\ndef test_root_page_search(\n    root_page: SimpleRepositoryRootPage, query: str | list[str], expected: list[str]\n) -> None:\n    assert root_page.search(query) == expected\n"
  },
  {
    "path": "tests/repositories/link_sources/test_html.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.repositories.link_sources.html import HTMLPage\nfrom poetry.repositories.link_sources.html import SimpleRepositoryHTMLRootPage\n\n\nif TYPE_CHECKING:\n    from tests.types import HTMLPageGetter\n\n\n@pytest.fixture\ndef root_page() -> SimpleRepositoryHTMLRootPage:\n    names = [\"poetry\", \"poetry-core\", \"requests\"]\n    hrefs = [f'<a href=\"{name}/\">{name}</a><br>' for name in names]\n\n    return SimpleRepositoryHTMLRootPage(f\"\"\"\\\n<!DOCTYPE html>\n<html>\n    <head>\n        Legacy Repository\n    </head>\n    <body>\n    {\"\".join(hrefs)}\n    </body>\n</html>\n\"\"\")\n\n\ndef test_root_page_package_names(root_page: SimpleRepositoryHTMLRootPage) -> None:\n    assert root_page.package_names == [\"poetry\", \"poetry-core\", \"requests\"]\n\n\n@pytest.mark.parametrize(\n    \"attributes, expected_link\",\n    [\n        (\"\", Link(\"https://example.org/demo-0.1.whl\")),\n        (\n            'data-requires-python=\"&gt;=3.7\"',\n            Link(\"https://example.org/demo-0.1.whl\", requires_python=\">=3.7\"),\n        ),\n        (\n            \"data-yanked\",\n            Link(\"https://example.org/demo-0.1.whl\", yanked=True),\n        ),\n        (\n            'data-yanked=\"\"',\n            Link(\"https://example.org/demo-0.1.whl\", yanked=True),\n        ),\n        (\n            'data-yanked=\"&lt;reason&gt;\"',\n            Link(\"https://example.org/demo-0.1.whl\", yanked=\"<reason>\"),\n        ),\n        (\n            'data-requires-python=\"&gt;=3.7\" data-yanked',\n            Link(\n                \"https://example.org/demo-0.1.whl\", requires_python=\">=3.7\", yanked=True\n            ),\n        ),\n    ],\n)\ndef test_link_attributes(\n    html_page_content: HTMLPageGetter, attributes: str, expected_link: Link\n) -> None:\n    anchor = (\n        f'<a href=\"https://example.org/demo-0.1.whl\" {attributes}>demo-0.1.whl</a><br/>'\n    )\n    content = html_page_content(anchor)\n    page = HTMLPage(\"https://example.org\", content)\n\n    assert len(list(page.links)) == 1\n    link = next(iter(page.links))\n    assert link.url == expected_link.url\n    assert link.requires_python == expected_link.requires_python\n    assert link.yanked == expected_link.yanked\n    assert link.yanked_reason == expected_link.yanked_reason\n\n\ndef test_hash_from_url(html_page_content: HTMLPageGetter) -> None:\n    anchor = (\n        '<a href=\"https://files.pythonhosted.org/packages/16/52/cafe/'\n        'demo-1.0.0.whl#sha256=abcd1234\">demo-1.0.0.whl</a><br/>'\n    )\n    content = html_page_content(anchor)\n    page = HTMLPage(\"https://example.org\", content)\n\n    assert len(list(page.links)) == 1\n    link = next(iter(page.links))\n    assert link.hashes == {\"sha256\": \"abcd1234\"}\n\n\n@pytest.mark.parametrize(\n    \"yanked_attrs, expected\",\n    [\n        ((\"\", \"\"), False),\n        ((\"data-yanked\", \"\"), False),\n        ((\"\", \"data-yanked\"), False),\n        ((\"data-yanked\", \"data-yanked\"), True),\n        ((\"data-yanked='reason'\", \"data-yanked\"), \"reason\"),\n        ((\"data-yanked\", \"data-yanked='reason'\"), \"reason\"),\n        ((\"data-yanked='reason'\", \"data-yanked=''\"), \"reason\"),\n        ((\"data-yanked=''\", \"data-yanked='reason'\"), \"reason\"),\n        ((\"data-yanked='reason'\", \"data-yanked='reason'\"), \"reason\"),\n        ((\"data-yanked='reason 1'\", \"data-yanked='reason 2'\"), \"reason 1\\nreason 2\"),\n    ],\n)\ndef test_yanked(\n    html_page_content: HTMLPageGetter,\n    yanked_attrs: tuple[str, str],\n    expected: bool | str,\n) -> None:\n    anchors = (\n        f'<a href=\"https://example.org/demo-0.1.tar.gz\" {yanked_attrs[0]}>'\n        \"demo-0.1.tar.gz</a>\"\n        f'<a href=\"https://example.org/demo-0.1.whl\" {yanked_attrs[1]}>demo-0.1.whl</a>'\n    )\n    content = html_page_content(anchors)\n    page = HTMLPage(\"https://example.org\", content)\n\n    assert page.yanked(canonicalize_name(\"demo\"), Version.parse(\"0.1\")) == expected\n\n\n@pytest.mark.parametrize(\n    (\"metadata\", \"expected_has_metadata\", \"expected_metadata_hashes\"),\n    [\n        (\"\", False, {}),\n        # new\n        (\"data-core-metadata\", True, {}),\n        (\"data-core-metadata=''\", True, {}),\n        (\"data-core-metadata='foo'\", True, {}),\n        (\"data-core-metadata='sha256=abcd'\", True, {\"sha256\": \"abcd\"}),\n        # old\n        (\"data-dist-info-metadata\", True, {}),\n        (\"data-dist-info-metadata=''\", True, {}),\n        (\"data-dist-info-metadata='foo'\", True, {}),\n        (\"data-dist-info-metadata='sha256=abcd'\", True, {\"sha256\": \"abcd\"}),\n        # conflicting (new wins)\n        (\"data-core-metadata data-dist-info-metadata='sha256=abcd'\", True, {}),\n        (\"data-dist-info-metadata='sha256=abcd' data-core-metadata\", True, {}),\n        (\n            \"data-core-metadata='sha256=abcd' data-dist-info-metadata\",\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n        (\n            \"data-dist-info-metadata data-core-metadata='sha256=abcd'\",\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n        (\n            \"data-core-metadata='sha256=abcd' data-dist-info-metadata='sha256=1234'\",\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n        (\n            \"data-dist-info-metadata='sha256=1234' data-core-metadata='sha256=abcd'\",\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n    ],\n)\ndef test_metadata(\n    html_page_content: HTMLPageGetter,\n    metadata: str,\n    expected_has_metadata: bool,\n    expected_metadata_hashes: dict[str, str],\n) -> None:\n    anchors = f'<a href=\"https://example.org/demo-0.1.whl\" {metadata}>demo-0.1.whl</a>'\n    content = html_page_content(anchors)\n    page = HTMLPage(\"https://example.org\", content)\n\n    link = next(page.links)\n    assert link.has_metadata is expected_has_metadata\n    assert link.metadata_hashes == expected_metadata_hashes\n\n\n@pytest.mark.parametrize(\n    \"anchor, base_url, repo_url, expected\",\n    (\n        (\n            '<a href=\"https://example.org/demo-0.1.whl\">demo-0.1.whl</a>',\n            None,\n            \"https://example.org/simple/\",\n            \"https://example.org/demo-0.1.whl\",\n        ),\n        (\n            '<a href=\"https://example.org/demo-0.1.whl\">demo-0.1.whl</a>',\n            \"https://example.org/files/\",\n            \"https://example.org/simple/\",\n            \"https://example.org/demo-0.1.whl\",\n        ),\n        (\n            '<a href=\"demo-0.1.whl\">demo-0.1.whl</a>',\n            \"https://example.org/files/\",\n            \"https://example.org/simple/\",\n            \"https://example.org/files/demo-0.1.whl\",\n        ),\n        (\n            '<a href=\"demo-0.1.whl\">demo-0.1.whl</a>',\n            None,\n            \"https://example.org/simple/\",\n            \"https://example.org/simple/demo-0.1.whl\",\n        ),\n    ),\n)\ndef test_base_url(\n    html_page_content: HTMLPageGetter,\n    anchor: str,\n    base_url: str | None,\n    repo_url: str,\n    expected: str,\n) -> None:\n    content = html_page_content(anchor, base_url)\n    page = HTMLPage(repo_url, content)\n    link = next(iter(page.links))\n    assert link.url == expected\n"
  },
  {
    "path": "tests/repositories/link_sources/test_json.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version.version import Version\n\nfrom poetry.repositories.link_sources.json import SimpleJsonPage\nfrom poetry.repositories.link_sources.json import SimpleRepositoryJsonRootPage\n\n\n@pytest.fixture\ndef root_page() -> SimpleRepositoryJsonRootPage:\n    names = [\"poetry\", \"poetry-core\", \"requests\"]\n\n    return SimpleRepositoryJsonRootPage(\n        {\n            \"meta\": {\"api-version\": \"1.4\"},\n            \"projects\": [{\"name\": name} for name in names],\n        }\n    )\n\n\ndef test_root_page_package_names(root_page: SimpleRepositoryJsonRootPage) -> None:\n    assert root_page.package_names == [\"poetry\", \"poetry-core\", \"requests\"]\n\n\ndef test_attributes() -> None:\n    content = {\n        \"files\": [\n            # minimal\n            {\"url\": \"https://example.org/demo-0.1.whl\"},\n            # all (with non-default values)\n            {\n                \"url\": \"https://example.org/demo-0.1.tar.gz\",\n                \"requires-python\": \">=3.6\",\n                \"yanked\": True,\n                \"hashes\": {\"sha256\": \"abcd1234\"},\n                \"core-metadata\": True,\n            },\n        ]\n    }\n    page = SimpleJsonPage(\"https://example.org\", content)\n\n    assert page.url == \"https://example.org\"\n    links = list(page.links)\n    assert len(links) == 2\n\n    assert links[0].url == \"https://example.org/demo-0.1.whl\"\n    assert links[0].requires_python is None\n    assert links[0].yanked is False\n    assert links[0].hashes == {}\n    assert links[0].has_metadata is False\n\n    assert links[1].url == \"https://example.org/demo-0.1.tar.gz\"\n    assert links[1].requires_python == \">=3.6\"\n    assert links[1].yanked is True\n    assert links[1].hashes == {\"sha256\": \"abcd1234\"}\n    assert links[1].has_metadata is True\n\n\n@pytest.mark.parametrize(\n    (\"yanked\", \"expected\"),\n    [\n        ((None, None), False),\n        ((False, False), False),\n        ((True, False), False),\n        ((False, True), False),\n        ((True, True), True),\n        ((\"reason\", True), \"reason\"),\n        ((True, \"reason\"), \"reason\"),\n        ((\"reason\", \"reason\"), \"reason\"),\n        ((\"reason 1\", \"reason 2\"), \"reason 1\\nreason 2\"),\n    ],\n)\ndef test_yanked(\n    yanked: tuple[str | None, str | None],\n    expected: bool | str,\n) -> None:\n    content = {\n        \"files\": [\n            {\"url\": \"https://example.org/demo-0.1.tar.gz\", \"yanked\": yanked[0]},\n            {\"url\": \"https://example.org/demo-0.1.whl\", \"yanked\": yanked[1]},\n        ]\n    }\n    if yanked[0] is None:\n        del content[\"files\"][0][\"yanked\"]\n    if yanked[1] is None:\n        del content[\"files\"][1][\"yanked\"]\n    page = SimpleJsonPage(\"https://example.org\", content)\n\n    assert page.yanked(canonicalize_name(\"demo\"), Version.parse(\"0.1\")) == expected\n\n\n@pytest.mark.parametrize(\n    (\"metadata\", \"expected_has_metadata\", \"expected_metadata_hashes\"),\n    [\n        ({}, False, {}),\n        # new\n        ({\"core-metadata\": False}, False, {}),\n        ({\"core-metadata\": True}, True, {}),\n        (\n            {\"core-metadata\": {\"sha1\": \"1234\", \"sha256\": \"abcd\"}},\n            True,\n            {\"sha1\": \"1234\", \"sha256\": \"abcd\"},\n        ),\n        ({\"core-metadata\": {}}, False, {}),\n        (\n            {\"core-metadata\": {\"sha1\": \"1234\", \"sha256\": \"abcd\"}},\n            True,\n            {\"sha1\": \"1234\", \"sha256\": \"abcd\"},\n        ),\n        # old\n        ({\"dist-info-metadata\": False}, False, {}),\n        ({\"dist-info-metadata\": True}, True, {}),\n        ({\"dist-info-metadata\": {\"sha256\": \"abcd\"}}, True, {\"sha256\": \"abcd\"}),\n        ({\"dist-info-metadata\": {}}, False, {}),\n        (\n            {\"dist-info-metadata\": {\"sha1\": \"1234\", \"sha256\": \"abcd\"}},\n            True,\n            {\"sha1\": \"1234\", \"sha256\": \"abcd\"},\n        ),\n        # conflicting (new wins)\n        ({\"core-metadata\": False, \"dist-info-metadata\": True}, False, {}),\n        (\n            {\"core-metadata\": False, \"dist-info-metadata\": {\"sha256\": \"abcd\"}},\n            False,\n            {},\n        ),\n        ({\"core-metadata\": True, \"dist-info-metadata\": False}, True, {}),\n        (\n            {\"core-metadata\": True, \"dist-info-metadata\": {\"sha256\": \"abcd\"}},\n            True,\n            {},\n        ),\n        (\n            {\"core-metadata\": {\"sha256\": \"abcd\"}, \"dist-info-metadata\": False},\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n        (\n            {\"core-metadata\": {\"sha256\": \"abcd\"}, \"dist-info-metadata\": True},\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n        (\n            {\n                \"core-metadata\": {\"sha256\": \"abcd\"},\n                \"dist-info-metadata\": {\"sha256\": \"1234\"},\n            },\n            True,\n            {\"sha256\": \"abcd\"},\n        ),\n    ],\n)\ndef test_metadata(\n    metadata: dict[str, bool | dict[str, str]],\n    expected_has_metadata: bool,\n    expected_metadata_hashes: dict[str, str],\n) -> None:\n    content = {\"files\": [{\"url\": \"https://example.org/demo-0.1.whl\", **metadata}]}\n    page = SimpleJsonPage(\"https://example.org\", content)\n\n    link = next(page.links)\n    assert link.has_metadata is expected_has_metadata\n    assert link.metadata_hashes == expected_metadata_hashes\n\n\n@pytest.mark.parametrize(\n    (\"url\", \"repo_url\", \"expected\"),\n    (\n        (\n            \"https://example.org/files/demo-0.1.whl\",\n            \"https://example.org/simple/\",\n            \"https://example.org/files/demo-0.1.whl\",\n        ),\n        (\n            \"demo-0.1.whl\",\n            \"https://example.org/simple/\",\n            \"https://example.org/simple/demo-0.1.whl\",\n        ),\n    ),\n)\ndef test_base_url(url: str, repo_url: str, expected: str) -> None:\n    page = SimpleJsonPage(repo_url, {\"files\": [{\"url\": url}]})\n    link = next(iter(page.links))\n    assert link.url == expected\n"
  },
  {
    "path": "tests/repositories/parsers/__init__.py",
    "content": ""
  },
  {
    "path": "tests/repositories/parsers/test_html_page_parser.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.repositories.parsers.html_page_parser import HTMLPageParser\n\n\nif TYPE_CHECKING:\n    from tests.types import HTMLPageGetter\n\n\n@pytest.fixture()\ndef html_page(html_page_content: HTMLPageGetter) -> str:\n    links = \"\"\"\n        <a href=\"https://example.org/demo-0.1.whl\">demo-0.1.whl</a><br/>\n        <a href=\"https://example.org/demo-0.1.whl\"\n            data-requires-python=\">=3.7\">demo-0.1.whl</a><br/>\n        <a href=\"https://example.org/demo-0.1.whl\" data-yanked>demo-0.1.whl</a><br/>\n        <a href=\"https://example.org/demo-0.1.whl\" data-yanked=\"\">demo-0.1.whl</a><br/>\n        <a href=\"https://example.org/demo-0.1.whl\"\n            data-yanked=\"<reason>\"\n        >demo-0.1.whl</a><br/>\n        <a href=\"https://example.org/demo-0.1.whl\"\n            data-requires-python=\">=3.7\"\n            data-yanked\n         >demo-0.1.whl</a><br/>\n    \"\"\"\n    return html_page_content(links)\n\n\ndef test_html_page_parser_anchors(html_page: str) -> None:\n    parser = HTMLPageParser()\n    parser.feed(html_page)\n\n    assert parser.anchors == [\n        {\"href\": \"https://example.org/demo-0.1.whl\"},\n        {\"data-requires-python\": \">=3.7\", \"href\": \"https://example.org/demo-0.1.whl\"},\n        {\"data-yanked\": None, \"href\": \"https://example.org/demo-0.1.whl\"},\n        {\"data-yanked\": \"\", \"href\": \"https://example.org/demo-0.1.whl\"},\n        {\"data-yanked\": \"<reason>\", \"href\": \"https://example.org/demo-0.1.whl\"},\n        {\n            \"data-requires-python\": \">=3.7\",\n            \"data-yanked\": None,\n            \"href\": \"https://example.org/demo-0.1.whl\",\n        },\n    ]\n\n\ndef test_html_page_parser_base_url() -> None:\n    content = \"\"\"\n        <!DOCTYPE html>\n        <html>\n          <head>\n            <base href=\"https://example.org/\">\n            <meta name=\"pypi:repository-version\" content=\"1.0\">\n            <title>Links for demo</title>\n          </head>\n          <body>\n            <h1>Links for demo</h1>\n            <a href=\"demo-0.1.whl\">demo-0.1.whl</a><br/>\n            </body>\n        </html>\n    \"\"\"\n    parser = HTMLPageParser()\n    parser.feed(content)\n\n    assert parser.base_url == \"https://example.org/\"\n"
  },
  {
    "path": "tests/repositories/parsers/test_pypi_search_parser.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\n\nimport pytest\n\nfrom poetry.repositories.parsers.pypi_search_parser import Result\nfrom poetry.repositories.parsers.pypi_search_parser import SearchResultParser\n\n\nFIXTURES_DIRECTORY = Path(__file__).parent.parent / \"fixtures\" / \"pypi.org\" / \"search\"\n\n\n@pytest.fixture\ndef search_page_data() -> str:\n    with FIXTURES_DIRECTORY.joinpath(\"search.html\").open(encoding=\"utf-8\") as f:\n        return f.read()\n\n\ndef test_search_parser(search_page_data: str) -> None:\n    parser = SearchResultParser()\n    parser.feed(search_page_data)\n    assert parser.results == [\n        Result(\n            name=\"SQLAlchemy\",\n            version=\"1.3.10\",\n            description=\"Database Abstraction Library\",\n        ),\n        Result(\n            name=\"SQLAlchemy-Dao\",\n            version=\"1.3.1\",\n            description=\"Simple wrapper for sqlalchemy.\",\n        ),\n        Result(\n            name=\"graphene-sqlalchemy\",\n            version=\"2.2.2\",\n            description=\"Graphene SQLAlchemy integration\",\n        ),\n        Result(\n            name=\"SQLAlchemy-UTCDateTime\",\n            version=\"1.0.4\",\n            description=(\n                \"Convert to/from timezone aware datetimes when storing in a DBMS\"\n            ),\n        ),\n        Result(\n            name=\"paginate_sqlalchemy\",\n            version=\"0.3.0\",\n            description=\"Extension to paginate.Page that supports SQLAlchemy queries\",\n        ),\n        Result(\n            name=\"sqlalchemy_audit\",\n            version=\"0.1.0\",\n            description=(\n                \"sqlalchemy-audit provides an easy way to set up revision \"\n                \"tracking for your data.\"\n            ),\n        ),\n        Result(\n            name=\"transmogrify.sqlalchemy\",\n            version=\"1.0.2\",\n            description=\"Feed data from SQLAlchemy into a transmogrifier pipeline\",\n        ),\n        Result(\n            name=\"sqlalchemy_schemadisplay\",\n            version=\"1.3\",\n            description=\"Turn SQLAlchemy DB Model into a graph\",\n        ),\n        Result(name=\"sqlalchemy_traversal\", version=\"0.5.2\", description=\"UNKNOWN\"),\n        Result(\n            name=\"sqlalchemy-filters\",\n            version=\"0.10.0\",\n            description=\"A library to filter SQLAlchemy queries.\",\n        ),\n        Result(\n            name=\"SQLAlchemy-wrap\",\n            version=\"2.1.7\",\n            description=\"Python wrapper for the CircleCI API\",\n        ),\n        Result(\n            name=\"sqlalchemy-nav\",\n            version=\"0.0.2\",\n            description=(\n                \"SQLAlchemy-Nav provides SQLAlchemy Mixins for creating \"\n                \"navigation bars compatible with Bootstrap\"\n            ),\n        ),\n        Result(\n            name=\"sqlalchemy-repr\",\n            version=\"0.0.1\",\n            description=\"Automatically generates pretty repr of a SQLAlchemy model.\",\n        ),\n        Result(\n            name=\"sqlalchemy-diff\",\n            version=\"0.1.3\",\n            description=\"Compare two database schemas using sqlalchemy.\",\n        ),\n        Result(\n            name=\"SQLAlchemy-Equivalence\",\n            version=\"0.1.1\",\n            description=(\n                \"Provides natural equivalence support for SQLAlchemy \"\n                \"declarative models.\"\n            ),\n        ),\n        Result(\n            name=\"Broadway-SQLAlchemy\",\n            version=\"0.0.1\",\n            description=\"A broadway extension wrapping Flask-SQLAlchemy\",\n        ),\n        Result(\n            name=\"jsonql-sqlalchemy\",\n            version=\"1.0.1\",\n            description=\"Simple JSON-Based CRUD Query Language for SQLAlchemy\",\n        ),\n        Result(\n            name=\"sqlalchemy-plus\",\n            version=\"0.2.0\",\n            description=\"Create Views and Materialized Views with SqlAlchemy\",\n        ),\n        Result(\n            name=\"CherryPy-SQLAlchemy\",\n            version=\"0.5.3\",\n            description=\"Use SQLAlchemy with CherryPy\",\n        ),\n        Result(\n            name=\"sqlalchemy_sqlany\",\n            version=\"1.0.3\",\n            description=\"SAP Sybase SQL Anywhere dialect for SQLAlchemy\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/repositories/test_cached_repository.py",
    "content": "from __future__ import annotations\n\nfrom typing import Any\n\nimport pytest\n\nfrom packaging.utils import NormalizedName\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.repositories.cached_repository import CachedRepository\n\n\nclass MockCachedRepository(CachedRepository):\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]:\n        raise NotImplementedError\n\n\n@pytest.fixture\ndef release_info() -> PackageInfo:\n    return PackageInfo(\n        name=\"mylib\",\n        version=\"1.0\",\n        summary=\"\",\n        requires_dist=[],\n        requires_python=\">=3.9\",\n        files=[\n            {\n                \"file\": \"mylib-1.0-py3-none-any.whl\",\n                \"hash\": \"sha256:dummyhashvalue1234567890abcdef\",\n            },\n            {\n                \"file\": \"mylib-1.0.tar.gz\",\n                \"hash\": \"sha256:anotherdummyhashvalueabcdef1234567890\",\n            },\n        ],\n        cache_version=str(CachedRepository.CACHE_VERSION),\n    )\n\n\n@pytest.fixture\ndef outdated_release_info() -> PackageInfo:\n    return PackageInfo(\n        name=\"mylib\",\n        version=\"1.0\",\n        summary=\"\",\n        requires_dist=[],\n        requires_python=\">=3.9\",\n        files=[\n            {\n                \"file\": \"mylib-1.0-py3-none-any.whl\",\n                \"hash\": \"sha256:dummyhashvalue1234567890abcdef\",\n            }\n        ],\n        cache_version=str(CachedRepository.CACHE_VERSION),\n    )\n\n\n@pytest.mark.parametrize(\"disable_cache\", [False, True])\ndef test_get_release_info_cache(\n    release_info: PackageInfo, outdated_release_info: PackageInfo, disable_cache: bool\n) -> None:\n    repo = MockCachedRepository(\"mock\", disable_cache=disable_cache)\n    repo._get_release_info = lambda name, version: outdated_release_info.asdict()  # type: ignore[method-assign]\n\n    name = canonicalize_name(\"mylib\")\n    version = Version.parse(\"1.0\")\n    assert len(repo.get_release_info(name, version).files) == 1\n\n    # without disable_cache: cached value is returned even if the underlying data has changed\n    # with disable_cache: cached value is ignored and updated data is returned\n    repo._get_release_info = lambda name, version: release_info.asdict()  # type: ignore[method-assign]\n    assert len(repo.get_release_info(name, version).files) == (\n        2 if disable_cache else 1\n    )\n\n    # after clearing the cache entry, updated data is returned\n    repo.forget(name, version)\n    assert len(repo.get_release_info(name, version).files) == 2\n"
  },
  {
    "path": "tests/repositories/test_http_repository.py",
    "content": "from __future__ import annotations\n\nimport contextlib\nimport shutil\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom zipfile import ZipFile\n\nimport pytest\n\nfrom packaging.metadata import parse_email\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.inspection.info import PackageInfoError\nfrom poetry.inspection.lazy_wheel import HTTPRangeRequestUnsupportedError\nfrom poetry.repositories.http_repository import HTTPRepository\nfrom poetry.utils.helpers import HTTPRangeRequestSupportedError\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n    from poetry.core.constraints.version import Version\n    from pytest_mock import MockerFixture\n\n\nclass MockRepository(HTTPRepository):\n    DIST_FIXTURES = Path(__file__).parent / \"fixtures\" / \"pypi.org\" / \"dists\"\n\n    def __init__(self, lazy_wheel: bool = True) -> None:\n        super().__init__(\"foo\", \"https://foo.com\")\n        self._lazy_wheel = lazy_wheel\n\n    def _get_release_info(\n        self, name: NormalizedName, version: Version\n    ) -> dict[str, Any]:\n        raise NotImplementedError\n\n\n@pytest.mark.parametrize(\"lazy_wheel\", [False, True])\n@pytest.mark.parametrize(\"supports_range_requests\", [None, False, True])\ndef test_get_info_from_wheel(\n    mocker: MockerFixture, lazy_wheel: bool, supports_range_requests: bool | None\n) -> None:\n    filename = \"poetry_core-1.5.0-py3-none-any.whl\"\n    filepath = MockRepository.DIST_FIXTURES / filename\n    with ZipFile(filepath) as zf:\n        metadata, _ = parse_email(zf.read(\"poetry_core-1.5.0.dist-info/METADATA\"))\n\n    mock_metadata_from_wheel_url = mocker.patch(\n        \"poetry.repositories.http_repository.metadata_from_wheel_url\",\n        return_value=metadata,\n    )\n    mock_download = mocker.patch(\n        \"poetry.repositories.http_repository.download_file\",\n        side_effect=lambda _, dest, *args, **kwargs: shutil.copy(filepath, dest),\n    )\n\n    domain = \"foo.com\"\n    url = f\"https://{domain}/{filename}\"\n    repo = MockRepository(lazy_wheel)\n    assert not repo._supports_range_requests\n    if lazy_wheel and supports_range_requests is not None:\n        repo._supports_range_requests[domain] = supports_range_requests\n\n    info = repo._get_info_from_wheel(Link(url))\n    assert info.name == \"poetry-core\"\n    assert info.version == \"1.5.0\"\n    assert info.requires_dist == [\n        'importlib-metadata (>=1.7.0) ; python_version < \"3.8\"'\n    ]\n\n    if lazy_wheel and supports_range_requests is not False:\n        mock_metadata_from_wheel_url.assert_called_once_with(\n            filename, url, repo.session\n        )\n        mock_download.assert_not_called()\n        assert repo._supports_range_requests[domain] is True\n    else:\n        mock_metadata_from_wheel_url.assert_not_called()\n        mock_download.assert_called_once_with(\n            url,\n            mocker.ANY,\n            session=repo.session,\n            raise_accepts_ranges=lazy_wheel,\n            max_retries=0,\n        )\n        if lazy_wheel:\n            assert repo._supports_range_requests[domain] is False\n        else:\n            assert domain not in repo._supports_range_requests\n\n\ndef test_get_info_from_wheel_state_sequence(mocker: MockerFixture) -> None:\n    \"\"\"\n    1. We know nothing:\n       Try range requests, which are not supported and fall back to complete download.\n    2. Range requests were not supported so far:\n       We do not try range requests again.\n    3. Range requests were still not supported so far:\n       We do not try range requests again, but we notice that the response header\n       contains \"Accept-Ranges: bytes\", so range requests are at least supported\n       for some files, which means we want to try again.\n    4. Range requests are supported for some files:\n       We try range requests (success).\n    5. Range requests are supported for some files:\n       We try range requests (failure), but do not forget that range requests are\n       supported for some files.\n    6. Range requests are supported for some files:\n       We try range requests (success).\n    \"\"\"\n    mock_metadata_from_wheel_url = mocker.patch(\n        \"poetry.repositories.http_repository.metadata_from_wheel_url\"\n    )\n    mock_download = mocker.patch(\"poetry.repositories.http_repository.download_file\")\n\n    filename = \"poetry_core-1.5.0-py3-none-any.whl\"\n    domain = \"foo.com\"\n    link = Link(f\"https://{domain}/{filename}\")\n    repo = MockRepository()\n\n    # 1. range request and download\n    mock_metadata_from_wheel_url.side_effect = HTTPRangeRequestUnsupportedError\n\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 1\n    assert mock_download.call_count == 1\n    assert mock_download.call_args[1][\"raise_accepts_ranges\"] is False\n\n    # 2. only download\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 1\n    assert mock_download.call_count == 2\n    assert mock_download.call_args[1][\"raise_accepts_ranges\"] is True\n\n    # 3. download and range request\n    mock_metadata_from_wheel_url.side_effect = None\n    mock_download.side_effect = HTTPRangeRequestSupportedError\n\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 2\n    assert mock_download.call_count == 3\n    assert mock_download.call_args[1][\"raise_accepts_ranges\"] is True\n\n    # 4. only range request\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 3\n    assert mock_download.call_count == 3\n\n    # 5. range request and download\n    mock_metadata_from_wheel_url.side_effect = HTTPRangeRequestUnsupportedError\n    mock_download.side_effect = None\n\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 4\n    assert mock_download.call_count == 4\n    assert mock_download.call_args[1][\"raise_accepts_ranges\"] is False\n\n    # 6. only range request\n    mock_metadata_from_wheel_url.side_effect = None\n\n    with contextlib.suppress(PackageInfoError):\n        repo._get_info_from_wheel(link)\n\n    assert mock_metadata_from_wheel_url.call_count == 5\n    assert mock_download.call_count == 4\n\n\n@pytest.mark.parametrize(\n    \"mock_hashes\",\n    [\n        None,\n        {\"sha256\": \"e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\"},\n        {\"md5\": \"be7589b4902793e66d7d979bd8581591\"},\n    ],\n)\ndef test_calculate_sha256(\n    mocker: MockerFixture, mock_hashes: dict[str, Any] | None\n) -> None:\n    filename = \"poetry_core-1.5.0-py3-none-any.whl\"\n    filepath = MockRepository.DIST_FIXTURES / filename\n    mock_download = mocker.patch(\n        \"poetry.repositories.http_repository.download_file\",\n        side_effect=lambda _, dest, *args, **kwargs: shutil.copy(filepath, dest),\n    )\n    domain = \"foo.com\"\n    link = Link(f\"https://{domain}/{filename}\", hashes=mock_hashes)\n    repo = MockRepository()\n\n    calculated_hash = repo.calculate_sha256(link)\n\n    assert mock_download.call_count == 1\n    assert (\n        calculated_hash\n        == \"sha256:e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\"\n    )\n\n\ndef test_calculate_sha256_defaults_to_sha256_on_md5_errors(\n    mocker: MockerFixture,\n) -> None:\n    raised_value_error = False\n\n    def mock_hashlib_md5_error() -> None:\n        nonlocal raised_value_error\n        raised_value_error = True\n        raise ValueError(\n            \"[digital envelope routines: EVP_DigestInit_ex] disabled for FIPS\"\n        )\n\n    filename = \"poetry_core-1.5.0-py3-none-any.whl\"\n    filepath = MockRepository.DIST_FIXTURES / filename\n    mock_download = mocker.patch(\n        \"poetry.repositories.http_repository.download_file\",\n        side_effect=lambda _, dest, *args, **kwargs: shutil.copy(filepath, dest),\n    )\n    mock_hashlib_md5 = mocker.patch(\"hashlib.md5\", side_effect=mock_hashlib_md5_error)\n\n    domain = \"foo.com\"\n    link = Link(\n        f\"https://{domain}/{filename}\",\n        hashes={\"md5\": \"be7589b4902793e66d7d979bd8581591\"},\n    )\n    repo = MockRepository()\n\n    calculated_hash = repo.calculate_sha256(link)\n\n    assert raised_value_error\n    assert mock_download.call_count == 1\n    assert mock_hashlib_md5.call_count == 1\n    assert (\n        calculated_hash\n        == \"sha256:e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84\"\n    )\n"
  },
  {
    "path": "tests/repositories/test_installed_repository.py",
    "content": "from __future__ import annotations\n\nimport os\nimport shutil\nimport zipfile\n\nfrom functools import cached_property\nfrom importlib import metadata\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import NamedTuple\n\nimport pytest\n\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.utils._compat import getencoding\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env import VirtualEnv\nfrom tests.helpers import with_working_directory\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from poetry.core.packages.package import Package\n    from pytest import LogCaptureFixture\n    from pytest_mock.plugin import MockerFixture\n\n    from poetry.poetry import Poetry\n    from poetry.utils.env.base_env import PythonVersion\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture(scope=\"session\")\ndef env_dir(tmp_session_working_directory: Path) -> Iterator[Path]:\n    source = Path(__file__).parent / \"fixtures\" / \"installed\"\n    target = tmp_session_working_directory / source.name\n\n    with with_working_directory(source=source, target=target) as path:\n        yield path\n\n\n@pytest.fixture(scope=\"session\")\ndef site_purelib(env_dir: Path) -> Path:\n    return env_dir / \"lib\" / \"python3.7\" / \"site-packages\"\n\n\n@pytest.fixture(scope=\"session\")\ndef site_platlib(env_dir: Path) -> Path:\n    return env_dir / \"lib64\" / \"python3.7\" / \"site-packages\"\n\n\n@pytest.fixture(scope=\"session\")\ndef src_dir(env_dir: Path) -> Path:\n    return env_dir / \"src\"\n\n\n@pytest.fixture(scope=\"session\")\ndef installed_results(\n    site_purelib: Path, site_platlib: Path, src_dir: Path\n) -> list[metadata.PathDistribution]:\n    return [\n        metadata.PathDistribution(site_purelib / \"cleo-0.7.6.dist-info\"),\n        metadata.PathDistribution(src_dir / \"pendulum\" / \"pendulum.egg-info\"),\n        metadata.PathDistribution(\n            zipfile.Path(  # type: ignore[arg-type]\n                site_purelib / \"foo-0.1.0-py3.8.egg\",\n                \"EGG-INFO\",\n            )\n        ),\n        metadata.PathDistribution(site_purelib / \"standard-1.2.3.dist-info\"),\n        metadata.PathDistribution(site_purelib / \"editable-2.3.4.dist-info\"),\n        metadata.PathDistribution(site_purelib / \"editable-src-dir-2.3.4.dist-info\"),\n        metadata.PathDistribution(\n            site_purelib / \"editable-with-import-2.3.4.dist-info\"\n        ),\n        metadata.PathDistribution(site_platlib / \"lib64-2.3.4.dist-info\"),\n        metadata.PathDistribution(site_platlib / \"bender-2.0.5.dist-info\"),\n        metadata.PathDistribution(site_purelib / \"git_pep_610-1.2.3.dist-info\"),\n        metadata.PathDistribution(\n            site_purelib / \"git_pep_610_no_requested_version-1.2.3.dist-info\"\n        ),\n        metadata.PathDistribution(\n            site_purelib / \"git_pep_610_subdirectory-1.2.3.dist-info\"\n        ),\n        metadata.PathDistribution(site_purelib / \"url_pep_610-1.2.3.dist-info\"),\n        metadata.PathDistribution(site_purelib / \"file_pep_610-1.2.3.dist-info\"),\n        metadata.PathDistribution(site_purelib / \"directory_pep_610-1.2.3.dist-info\"),\n        metadata.PathDistribution(\n            site_purelib / \"editable_directory_pep_610-1.2.3.dist-info\"\n        ),\n    ]\n\n\n@pytest.fixture\ndef env(\n    env_dir: Path, site_purelib: Path, site_platlib: Path, src_dir: Path\n) -> MockEnv:\n    class _MockEnv(MockEnv):\n        @cached_property\n        def paths(self) -> dict[str, str]:\n            return {\n                \"purelib\": site_purelib.as_posix(),\n                \"platlib\": site_platlib.as_posix(),\n            }\n\n        @property\n        def sys_path(self) -> list[str]:\n            return [str(path) for path in [env_dir, site_platlib, site_purelib]]\n\n    return _MockEnv(path=env_dir)\n\n\n@pytest.fixture(autouse=True)\ndef mock_git_info(mocker: MockerFixture) -> None:\n    class GitRepoLocalInfo(NamedTuple):\n        origin: str\n        revision: str\n\n    mocker.patch(\n        \"poetry.vcs.git.Git.info\",\n        return_value=GitRepoLocalInfo(\n            origin=\"https://github.com/sdispater/pendulum.git\",\n            revision=\"bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6\",\n        ),\n    )\n\n\n@pytest.fixture\ndef repository(\n    mocker: MockerFixture,\n    env: MockEnv,\n    installed_results: list[metadata.PathDistribution],\n) -> InstalledRepository:\n    mocker.patch(\n        \"importlib.metadata.Distribution.discover\",\n        return_value=installed_results,\n    )\n    return InstalledRepository.load(env)\n\n\ndef get_package_from_repository(\n    name: str, repository: InstalledRepository\n) -> Package | None:\n    for pkg in repository.packages:\n        if pkg.name == name:\n            return pkg\n    return None\n\n\n@pytest.fixture\ndef poetry(\n    project_factory: ProjectFactory,\n    fixture_dir: FixtureDirGetter,\n    installed_results: list[metadata.PathDistribution],\n) -> Poetry:\n    return project_factory(\"simple\", source=fixture_dir(\"simple_project\"))\n\n\n@pytest.fixture(scope=\"session\")\ndef editable_source_directory_path() -> str:\n    return Path(\"/path/to/editable\").resolve(strict=False).as_posix()\n\n\n@pytest.fixture(scope=\"session\", autouse=(os.name == \"nt\"))\ndef fix_editable_path_for_windows(\n    site_purelib: Path, editable_source_directory_path: str\n) -> None:\n    # we handle this as a special case since in certain scenarios (eg: on Windows GHA runners)\n    # the temp directory is on a different drive causing path resolutions without drive letters\n    # to give inconsistent results at different phases of the test suite execution; additionally\n    # this represents a more realistic scenario\n    editable_pth_file = site_purelib / \"editable.pth\"\n    editable_pth_file.write_text(editable_source_directory_path, encoding=getencoding())\n\n\ndef test_load_successful(\n    repository: InstalledRepository, installed_results: list[metadata.PathDistribution]\n) -> None:\n    assert len(repository.packages) == len(installed_results)\n\n\ndef test_load_successful_with_invalid_distribution(\n    caplog: LogCaptureFixture,\n    mocker: MockerFixture,\n    env: MockEnv,\n    tmp_path: Path,\n    installed_results: list[metadata.PathDistribution],\n) -> None:\n    invalid_dist_info = tmp_path / \"site-packages\" / \"invalid-0.1.0.dist-info\"\n    invalid_dist_info.mkdir(parents=True)\n    mocker.patch(\n        \"importlib.metadata.Distribution.discover\",\n        return_value=[*installed_results, metadata.PathDistribution(invalid_dist_info)],\n    )\n    repository_with_invalid_distribution = InstalledRepository.load(env)\n\n    assert len(repository_with_invalid_distribution.packages) == len(installed_results)\n    assert len(caplog.messages) == 1\n\n    message = caplog.messages[0]\n    assert message.startswith(\"Project environment contains an invalid distribution\")\n    assert str(invalid_dist_info) in message\n\n\ndef test_loads_in_correct_sys_path_order(\n    tmp_path: Path, current_python: PythonVersion, fixture_dir: FixtureDirGetter\n) -> None:\n    path1 = tmp_path / \"path1\"\n    path1.mkdir()\n    path2 = tmp_path / \"path2\"\n    path2.mkdir()\n    env = MockEnv(path=tmp_path, sys_path=[str(path1), str(path2)])\n    fixtures = fixture_dir(\"project_plugins\")\n    dist_info_1 = \"my_application_plugin-1.0.dist-info\"\n    dist_info_2 = \"my_application_plugin-2.0.dist-info\"\n    dist_info_other = \"my_other_plugin-1.0.dist-info\"\n    shutil.copytree(fixtures / dist_info_1, path1 / dist_info_1)\n    shutil.copytree(fixtures / dist_info_2, path2 / dist_info_2)\n    shutil.copytree(fixtures / dist_info_other, path2 / dist_info_other)\n\n    repo = InstalledRepository.load(env)\n\n    assert {f\"{p.name} {p.version}\" for p in repo.packages} == {\n        \"my-application-plugin 1.0\",\n        \"my-other-plugin 1.0\",\n    }\n\n\ndef test_load_ensure_isolation(repository: InstalledRepository) -> None:\n    package = get_package_from_repository(\"attrs\", repository)\n    assert package is None\n\n\ndef test_load_standard_package(repository: InstalledRepository) -> None:\n    cleo = get_package_from_repository(\"cleo\", repository)\n    assert cleo is not None\n    assert cleo.name == \"cleo\"\n    assert cleo.version.text == \"0.7.6\"\n    assert (\n        cleo.description\n        == \"Cleo allows you to create beautiful and testable command-line interfaces.\"\n    )\n\n    foo = get_package_from_repository(\"foo\", repository)\n    assert foo is not None\n    assert foo.version.text == \"0.1.0\"\n\n\ndef test_load_git_package(repository: InstalledRepository) -> None:\n    pendulum = get_package_from_repository(\"pendulum\", repository)\n    assert pendulum is not None\n    assert pendulum.name == \"pendulum\"\n    assert pendulum.version.text == \"2.0.5\"\n    assert pendulum.description == \"Python datetimes made easy\"\n    assert pendulum.source_type == \"git\"\n    assert pendulum.source_url in [\n        \"git@github.com:sdispater/pendulum.git\",\n        \"https://github.com/sdispater/pendulum.git\",\n    ]\n    assert pendulum.source_reference == \"bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6\"\n\n\ndef test_load_git_package_pth(repository: InstalledRepository) -> None:\n    bender = get_package_from_repository(\"bender\", repository)\n    assert bender is not None\n    assert bender.name == \"bender\"\n    assert bender.version.text == \"2.0.5\"\n    assert bender.source_type == \"git\"\n\n\ndef test_load_platlib_package(repository: InstalledRepository) -> None:\n    lib64 = get_package_from_repository(\"lib64\", repository)\n    assert lib64 is not None\n    assert lib64.name == \"lib64\"\n    assert lib64.version.text == \"2.3.4\"\n\n\ndef test_load_editable_package(\n    repository: InstalledRepository, editable_source_directory_path: str\n) -> None:\n    # test editable package with text .pth file\n    editable = get_package_from_repository(\"editable\", repository)\n    assert editable is not None\n    assert editable.name == \"editable\"\n    assert editable.version.text == \"2.3.4\"\n    assert editable.source_type == \"directory\"\n    assert editable.source_url == editable_source_directory_path\n\n\ndef test_load_editable_src_dir_package(\n    repository: InstalledRepository, editable_source_directory_path: str\n) -> None:\n    # test editable package with src layout with text .pth file\n    editable = get_package_from_repository(\"editable-src-dir\", repository)\n    assert editable is not None\n    assert editable.name == \"editable-src-dir\"\n    assert editable.version.text == \"2.3.4\"\n    assert editable.source_type == \"directory\"\n    assert editable.source_url == editable_source_directory_path\n\n\ndef test_load_editable_with_import_package(repository: InstalledRepository) -> None:\n    # test editable package with executable .pth file\n    editable = get_package_from_repository(\"editable-with-import\", repository)\n    assert editable is not None\n    assert editable.name == \"editable-with-import\"\n    assert editable.version.text == \"2.3.4\"\n    assert editable.source_type is None\n    assert editable.source_url is None\n\n\ndef test_load_standard_package_with_pth_file(repository: InstalledRepository) -> None:\n    # test standard packages with .pth file is not treated as editable\n    standard = get_package_from_repository(\"standard\", repository)\n    assert standard is not None\n    assert standard.name == \"standard\"\n    assert standard.version.text == \"1.2.3\"\n    assert standard.source_type is None\n    assert standard.source_url is None\n\n\ndef test_load_pep_610_compliant_git_packages(repository: InstalledRepository) -> None:\n    package = get_package_from_repository(\"git-pep-610\", repository)\n\n    assert package is not None\n    assert package.name == \"git-pep-610\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"git\"\n    assert package.source_url == \"https://github.com/demo/git-pep-610.git\"\n    assert package.source_reference == \"my-branch\"\n    assert package.source_resolved_reference == \"123456\"\n\n\ndef test_load_pep_610_compliant_git_packages_no_requested_version(\n    repository: InstalledRepository,\n) -> None:\n    package = get_package_from_repository(\n        \"git-pep-610-no-requested-version\", repository\n    )\n\n    assert package is not None\n    assert package.name == \"git-pep-610-no-requested-version\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"git\"\n    assert (\n        package.source_url\n        == \"https://github.com/demo/git-pep-610-no-requested-version.git\"\n    )\n    assert package.source_resolved_reference == \"123456\"\n    assert package.source_reference == package.source_resolved_reference\n\n\ndef test_load_pep_610_compliant_git_packages_with_subdirectory(\n    repository: InstalledRepository,\n) -> None:\n    package = get_package_from_repository(\"git-pep-610-subdirectory\", repository)\n    assert package is not None\n    assert package.name == \"git-pep-610-subdirectory\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"git\"\n    assert package.source_url == \"https://github.com/demo/git-pep-610-subdirectory.git\"\n    assert package.source_reference == \"my-branch\"\n    assert package.source_resolved_reference == \"123456\"\n    assert package.source_subdirectory == \"subdir\"\n\n\ndef test_load_pep_610_compliant_url_packages(repository: InstalledRepository) -> None:\n    package = get_package_from_repository(\"url-pep-610\", repository)\n\n    assert package is not None\n    assert package.name == \"url-pep-610\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"url\"\n    assert (\n        package.source_url\n        == \"https://mock.pythonhosted.org/distributions/url-pep-610-1.2.3.tar.gz\"\n    )\n\n\ndef test_load_pep_610_compliant_file_packages(repository: InstalledRepository) -> None:\n    package = get_package_from_repository(\"file-pep-610\", repository)\n\n    assert package is not None\n    assert package.name == \"file-pep-610\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"file\"\n    assert package.source_url == \"/path/to/distributions/file-pep-610-1.2.3.tar.gz\"\n\n\ndef test_load_pep_610_compliant_directory_packages(\n    repository: InstalledRepository,\n) -> None:\n    package = get_package_from_repository(\"directory-pep-610\", repository)\n\n    assert package is not None\n    assert package.name == \"directory-pep-610\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"directory\"\n    assert package.source_url == \"/path/to/distributions/directory-pep-610\"\n    assert not package.develop\n\n\ndef test_load_pep_610_compliant_editable_directory_packages(\n    repository: InstalledRepository,\n) -> None:\n    package = get_package_from_repository(\"editable-directory-pep-610\", repository)\n\n    assert package is not None\n    assert package.name == \"editable-directory-pep-610\"\n    assert package.version.text == \"1.2.3\"\n    assert package.source_type == \"directory\"\n    assert package.source_url == \"/path/to/distributions/directory-pep-610\"\n    assert package.develop\n\n\n@pytest.mark.parametrize(\"with_system_site_packages\", [False, True])\ndef test_system_site_packages(\n    tmp_path: Path,\n    mocker: MockerFixture,\n    poetry: Poetry,\n    site_purelib: Path,\n    with_system_site_packages: bool,\n) -> None:\n    venv_path = tmp_path / \"venv\"\n    site_path = tmp_path / \"site\"\n    cleo_dist_info = \"cleo-0.7.6.dist-info\"\n    shutil.copytree(site_purelib / cleo_dist_info, site_path / cleo_dist_info)\n\n    EnvManager(poetry).build_venv(\n        path=venv_path, flags={\"system-site-packages\": with_system_site_packages}\n    )\n    env = VirtualEnv(venv_path)\n    standard_dist_info = \"standard-1.2.3.dist-info\"\n    shutil.copytree(site_purelib / standard_dist_info, env.purelib / standard_dist_info)\n    orig_sys_path = env.sys_path\n    if with_system_site_packages:\n        # on some environments, there could be multiple system-site, filter out those and inject our test site\n        mocker.patch(\n            \"poetry.utils.env.virtual_env.VirtualEnv.sys_path\",\n            [p for p in orig_sys_path if p.startswith(str(tmp_path))]\n            + [str(site_path)],\n        )\n    mocker.patch(\n        \"poetry.utils.env.generic_env.GenericEnv.get_paths\",\n        return_value={\"purelib\": str(site_path)},\n    )\n\n    installed_repository = InstalledRepository.load(env)\n\n    expected_system_site_packages = {\"cleo\"} if with_system_site_packages else set()\n    expected_packages = {\"standard\"}\n    expected_packages |= expected_system_site_packages\n    assert {p.name for p in installed_repository.packages} == expected_packages\n    assert {\n        p.name for p in installed_repository.system_site_packages\n    } == expected_system_site_packages\n\n\ndef test_system_site_packages_source_type(\n    tmp_path: Path, mocker: MockerFixture, poetry: Poetry, site_purelib: Path\n) -> None:\n    \"\"\"\n    The source type of system site packages\n    must not be falsely identified as \"directory\".\n    \"\"\"\n    venv_path = tmp_path / \"venv\"\n    site_path = tmp_path / \"site\"\n    for dist_info in {\"cleo-0.7.6.dist-info\", \"directory_pep_610-1.2.3.dist-info\"}:\n        shutil.copytree(site_purelib / dist_info, site_path / dist_info)\n    mocker.patch(\"poetry.utils.env.virtual_env.VirtualEnv.sys_path\", [str(site_path)])\n    mocker.patch(\n        \"poetry.utils.env.generic_env.GenericEnv.get_paths\",\n        return_value={\"purelib\": str(site_path)},\n    )\n\n    EnvManager(poetry).build_venv(path=venv_path, flags={\"system-site-packages\": True})\n    env = VirtualEnv(venv_path)\n\n    installed_repository = InstalledRepository.load(env)\n\n    assert installed_repository.packages == installed_repository.system_site_packages\n    source_types = {\n        package.name: package.source_type for package in installed_repository.packages\n    }\n    assert source_types == {\"cleo\": None, \"directory-pep-610\": \"directory\"}\n\n\ndef test_pipx_shared_lib_site_packages(\n    tmp_path: Path,\n    poetry: Poetry,\n    site_purelib: Path,\n    caplog: LogCaptureFixture,\n) -> None:\n    \"\"\"\n    Simulate pipx shared/lib/site-packages which is not relative to the venv path.\n    \"\"\"\n    venv_path = tmp_path / \"venv\"\n    shared_lib_site_path = tmp_path / \"site\"\n    env = MockEnv(\n        path=venv_path, sys_path=[str(venv_path / \"purelib\"), str(shared_lib_site_path)]\n    )\n    dist_info = \"cleo-0.7.6.dist-info\"\n    shutil.copytree(site_purelib / dist_info, shared_lib_site_path / dist_info)\n    installed_repository = InstalledRepository.load(env)\n\n    assert len(installed_repository.packages) == 1\n    assert installed_repository.system_site_packages == []\n    cleo_package = installed_repository.packages[0]\n    cleo_package.to_dependency()\n    # There must not be a warning\n    # that the package does not seem to be a valid Python package.\n    assert caplog.messages == []\n    assert cleo_package.source_type is None\n"
  },
  {
    "path": "tests/repositories/test_legacy_repository.py",
    "content": "from __future__ import annotations\n\nimport base64\nimport re\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\nimport requests\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.factory import Factory\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.exceptions import RepositoryError\nfrom poetry.repositories.legacy_repository import LegacyRepository\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    import responses\n\n    from pytest import MonkeyPatch\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from tests.repositories.fixtures.legacy import TestLegacyRepository\n    from tests.types import DistributionHashGetter\n\n\n@pytest.fixture(autouse=True)\ndef _use_simple_keyring(with_simple_keyring: None) -> None:\n    pass\n\n\ndef test_page_relative_links_path_are_correct(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    page = repo.get_page(\"relative\")\n    assert page is not None\n\n    for link in page.links:\n        assert link.netloc == \"legacy.foo.bar\"\n        assert link.path.startswith(\"/relative/poetry\")\n\n\ndef test_page_absolute_links_path_are_correct(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    page = repo.get_page(\"absolute\")\n    assert page is not None\n\n    for link in page.links:\n        assert link.netloc == \"files.pythonhosted.org\"\n        assert link.path.startswith(\"/packages/\")\n\n\ndef test_page_clean_link(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n\n    page = repo.get_page(\"relative\")\n    assert page is not None\n\n    cleaned = page.clean_link('https://legacy.foo.bar/test /the\"/cleaning\\0')\n    assert cleaned == \"https://legacy.foo.bar/test%20/the%22/cleaning%00\"\n\n\ndef test_page_invalid_version_link(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n\n    page = repo.get_page(\"invalid-version\")\n    assert page is not None\n\n    links = list(page.links)\n    assert len(links) == 1\n\n    versions = list(page.versions(canonicalize_name(\"poetry\")))\n    assert len(versions) == 1\n    assert versions[0].to_string() == \"0.1.0\"\n\n    packages = list(page.packages)\n    assert len(packages) == 1\n    assert packages[0].name == \"poetry\"\n    assert packages[0].version.to_string() == \"0.1.0\"\n\n\ndef test_page_filters_out_invalid_package_names(\n    legacy_repository_with_extra_packages: LegacyRepository,\n    get_legacy_dist_url: Callable[[str], str],\n    dist_hash_getter: DistributionHashGetter,\n) -> None:\n    repo = legacy_repository_with_extra_packages\n    packages = repo.find_packages(Factory.create_dependency(\"pytest\", \"*\"))\n    assert len(packages) == 1\n    assert packages[0].name == \"pytest\"\n    assert packages[0].version == Version.parse(\"3.5.0\")\n\n    package = repo.package(\"pytest\", Version.parse(\"3.5.0\"))\n    assert package.files == [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n            \"url\": get_legacy_dist_url(filename),\n        }\n        for filename in [\n            f\"{package.name}-{package.version}-py2.py3-none-any.whl\",\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n\n\ndef test_sdist_format_support(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n    page = repo.get_page(\"relative\")\n    assert page is not None\n    bz2_links = list(filter(lambda link: link.ext == \".tar.bz2\", page.links))\n    assert len(bz2_links) == 1\n    assert bz2_links[0].filename == \"poetry-0.1.1.tar.bz2\"\n\n\ndef test_missing_version(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n\n    with pytest.raises(PackageNotFoundError):\n        repo._get_release_info(\n            canonicalize_name(\"missing_version\"), Version.parse(\"1.1.0\")\n        )\n\n\ndef test_get_package_information_fallback_read_setup(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"jupyter\", Version.parse(\"1.0.0\"))\n\n    assert package.source_type == \"legacy\"\n    assert package.source_reference == repo.name\n    assert package.source_url == repo.url\n    assert package.name == \"jupyter\"\n    assert package.version.text == \"1.0.0\"\n    assert (\n        package.description\n        == \"Jupyter metapackage. Install all the Jupyter components in one go.\"\n    )\n\n\ndef test_get_package_information_pep_658(\n    mocker: MockerFixture, legacy_repository: LegacyRepository\n) -> None:\n    repo = legacy_repository\n\n    isort_package = repo.package(\"isort\", Version.parse(\"4.3.4\"))\n\n    spy = mocker.spy(repo, \"_get_info_from_metadata\")\n\n    try:\n        package = repo.package(\"isort-metadata\", Version.parse(\"4.3.4\"))\n    except FileNotFoundError:\n        pytest.fail(\"Metadata was not successfully retrieved\")\n    else:\n        assert spy.call_count > 0\n        assert spy.spy_return is not None\n\n        assert package.source_type == isort_package.source_type == \"legacy\"\n        assert package.source_reference == isort_package.source_reference == repo.name\n        assert package.source_url == isort_package.source_url == repo.url\n        assert package.name == \"isort-metadata\"\n        assert package.version.text == isort_package.version.text == \"4.3.4\"\n        assert package.description == isort_package.description\n        assert (\n            package.requires == isort_package.requires == [Dependency(\"futures\", \"*\")]\n        )\n        assert (\n            str(package.python_constraint)\n            == str(isort_package.python_constraint)\n            == \">=2.7,<3.0.dev0 || >=3.4.dev0\"\n        )\n\n\ndef test_get_package_information_skips_dependencies_with_invalid_constraints(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"python-language-server\", Version.parse(\"0.21.2\"))\n\n    assert package.name == \"python-language-server\"\n    assert package.version.text == \"0.21.2\"\n    assert (\n        package.description == \"Python Language Server for the Language Server Protocol\"\n    )\n\n    assert len(package.requires) == 25\n    assert sorted(\n        (r for r in package.requires if not r.is_optional()), key=lambda r: r.name\n    ) == [\n        Dependency(\"configparser\", \"*\"),\n        Dependency(\"future\", \">=0.14.0\"),\n        Dependency(\"futures\", \"*\"),\n        Dependency(\"jedi\", \">=0.12\"),\n        Dependency(\"pluggy\", \"*\"),\n        Dependency(\"python-jsonrpc-server\", \"*\"),\n    ]\n\n    all_extra = package.extras[canonicalize_name(\"all\")]\n\n    # rope>-0.10.5 should be discarded\n    assert sorted(all_extra, key=lambda r: r.name) == [\n        Dependency(\"autopep8\", \"*\"),\n        Dependency(\"mccabe\", \"*\"),\n        Dependency(\"pycodestyle\", \"*\"),\n        Dependency(\"pydocstyle\", \">=2.0.0\"),\n        Dependency(\"pyflakes\", \">=1.6.0\"),\n        Dependency(\"yapf\", \"*\"),\n    ]\n\n\ndef test_package_not_canonicalized(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"discord.py\", Version.parse(\"2.0.0\"))\n\n    assert package.name == \"discord-py\"\n    assert package.pretty_name == \"discord.py\"\n\n\ndef test_find_packages_no_prereleases(legacy_repository: LegacyRepository) -> None:\n    repo = legacy_repository\n\n    packages = repo.find_packages(Factory.create_dependency(\"pyyaml\", \"*\"))\n\n    assert len(packages) == 1\n\n    assert packages[0].source_type == \"legacy\"\n    assert packages[0].source_reference == repo.name\n    assert packages[0].source_url == repo.url\n\n\n@pytest.mark.parametrize(\n    [\"constraint\", \"count\"], [(\"*\", 1), (\">=1\", 1), (\"<=18\", 0), (\">=19.0.0a0\", 1)]\n)\ndef test_find_packages_only_prereleases(\n    constraint: str, count: int, legacy_repository: LegacyRepository\n) -> None:\n    repo = legacy_repository\n    packages = repo.find_packages(Factory.create_dependency(\"black\", constraint))\n\n    assert len(packages) == count\n\n    if count >= 0:\n        for package in packages:\n            assert package.source_type == \"legacy\"\n            assert package.source_reference == repo.name\n            assert package.source_url == repo.url\n\n\n@pytest.mark.parametrize(\n    [\"constraint\", \"expected\"],\n    [\n        # yanked 21.11b0 is ignored except for pinned version\n        (\"*\", [\"19.10b0\"]),\n        (\">=19.0a0\", [\"19.10b0\"]),\n        (\">=20.0a0\", []),\n        (\">=21.11b0\", []),\n        (\"==21.11b0\", [\"21.11b0\"]),\n    ],\n)\ndef test_find_packages_yanked(\n    constraint: str, expected: list[str], legacy_repository: LegacyRepository\n) -> None:\n    repo = legacy_repository\n    packages = repo.find_packages(Factory.create_dependency(\"black\", constraint))\n\n    assert [str(p.version) for p in packages] == expected\n\n\ndef test_get_package_information_chooses_correct_distribution(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"isort\", Version.parse(\"4.3.4\"))\n\n    assert package.name == \"isort\"\n    assert package.version.text == \"4.3.4\"\n\n    assert package.requires == [Dependency(\"futures\", \"*\")]\n    futures_dep = package.requires[0]\n    assert futures_dep.python_versions == \"~2.7\"\n\n\ndef test_get_package_information_includes_python_requires(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"futures\", Version.parse(\"3.2.0\"))\n\n    assert package.name == \"futures\"\n    assert package.version.text == \"3.2.0\"\n    assert package.python_versions == \">=2.6, <3\"\n\n\ndef test_get_package_information_includes_files(\n    legacy_repository: TestLegacyRepository,\n    dist_hash_getter: DistributionHashGetter,\n    get_legacy_dist_url: Callable[[str], str],\n    get_legacy_dist_size_and_upload_time: Callable[\n        [str], tuple[int | None, str | None]\n    ],\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"futures\", Version.parse(\"3.2.0\"))\n\n    expected: list[dict[str, Any]] = [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n            \"url\": get_legacy_dist_url(filename),\n        }\n        for filename in [\n            f\"{package.name}-{package.version}-py2-none-any.whl\",\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n    if repo.json:\n        for file_info in expected:\n            size, upload_time = get_legacy_dist_size_and_upload_time(file_info[\"file\"])\n            if size is not None:\n                file_info[\"size\"] = size\n            if upload_time is not None:\n                file_info[\"upload_time\"] = upload_time\n\n    assert package.files == expected\n\n\ndef test_get_package_information_sets_appropriate_python_versions_if_wheels_only(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"futures\", Version.parse(\"3.2.0\"))\n\n    assert package.name == \"futures\"\n    assert package.version.text == \"3.2.0\"\n    assert package.python_versions == \">=2.6, <3\"\n\n\ndef test_get_package_from_both_py2_and_py3_specific_wheels(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"ipython\", Version.parse(\"5.7.0\"))\n\n    assert package.name == \"ipython\"\n    assert package.version.text == \"5.7.0\"\n    assert package.python_versions == \"*\"\n    assert len(package.requires) == 41\n\n    expected = [\n        Dependency(\"appnope\", \"*\"),\n        Dependency(\"backports.shutil-get-terminal-size\", \"*\"),\n        Dependency(\"colorama\", \"*\"),\n        Dependency(\"decorator\", \"*\"),\n        Dependency(\"pathlib2\", \"*\"),\n        Dependency(\"pexpect\", \"*\"),\n        Dependency(\"pickleshare\", \"*\"),\n        Dependency(\"prompt-toolkit\", \">=1.0.4,<2.0.0\"),\n        Dependency(\"pygments\", \"*\"),\n        Dependency(\"setuptools\", \">=18.5\"),\n        Dependency(\"simplegeneric\", \">0.8\"),\n        Dependency(\"traitlets\", \">=4.2\"),\n        Dependency(\"win-unicode-console\", \">=0.5\"),\n    ]\n    required = [r for r in package.requires if not r.is_optional()]\n    assert required == expected\n\n    assert str(required[1].marker) == 'python_version == \"2.7\"'\n    assert (\n        str(required[12].marker) == 'sys_platform == \"win32\" and python_version < \"3.6\"'\n    )\n    assert (\n        str(required[4].marker) == 'python_version == \"2.7\" or python_version == \"3.3\"'\n    )\n    assert str(required[5].marker) == 'sys_platform != \"win32\"'\n\n\ndef test_get_package_from_both_py2_and_py3_specific_wheels_python_constraint(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"poetry-test-py2-py3-metadata-merge\", Version.parse(\"0.1.0\"))\n\n    assert package.name == \"poetry-test-py2-py3-metadata-merge\"\n    assert package.version.text == \"0.1.0\"\n    assert package.python_versions == \">=2.7,<2.8 || >=3.7,<4.0\"\n\n\ndef test_get_package_with_dist_and_universal_py3_wheel(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"ipython\", Version.parse(\"7.5.0\"))\n\n    assert package.name == \"ipython\"\n    assert package.version.text == \"7.5.0\"\n    assert package.python_versions == \">=3.5\"\n\n    expected = [\n        Dependency(\"appnope\", \"*\"),\n        Dependency(\"backcall\", \"*\"),\n        Dependency(\"colorama\", \"*\"),\n        Dependency(\"decorator\", \"*\"),\n        Dependency(\"jedi\", \">=0.10\"),\n        Dependency(\"pexpect\", \"*\"),\n        Dependency(\"pickleshare\", \"*\"),\n        Dependency(\"prompt-toolkit\", \">=2.0.0,<2.1.0\"),\n        Dependency(\"pygments\", \"*\"),\n        Dependency(\"setuptools\", \">=18.5\"),\n        Dependency(\"traitlets\", \">=4.2\"),\n        Dependency(\"typing\", \"*\"),\n        Dependency(\"win-unicode-console\", \">=0.5\"),\n    ]\n    required = [r for r in package.requires if not r.is_optional()]\n    assert sorted(required, key=lambda dep: dep.name) == expected\n\n\ndef test_get_package_retrieves_non_sha256_hashes(\n    legacy_repository: TestLegacyRepository,\n    dist_hash_getter: DistributionHashGetter,\n    get_legacy_dist_url: Callable[[str], str],\n    get_legacy_dist_size_and_upload_time: Callable[\n        [str], tuple[int | None, str | None]\n    ],\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"ipython\", Version.parse(\"7.5.0\"))\n\n    expected: list[dict[str, Any]] = [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n            \"url\": get_legacy_dist_url(filename),\n        }\n        for filename in [\n            f\"{package.name}-{package.version}-py3-none-any.whl\",\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n    if repo.json:\n        for file_info in expected:\n            size, upload_time = get_legacy_dist_size_and_upload_time(file_info[\"file\"])\n            if size is not None:\n                file_info[\"size\"] = size\n            if upload_time is not None:\n                file_info[\"upload_time\"] = upload_time\n\n    assert package.files == expected\n\n\ndef test_get_package_retrieves_non_sha256_hashes_mismatching_known_hash(\n    legacy_repository: TestLegacyRepository,\n    dist_hash_getter: DistributionHashGetter,\n    get_legacy_dist_url: Callable[[str], str],\n    get_legacy_dist_size_and_upload_time: Callable[\n        [str], tuple[int | None, str | None]\n    ],\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"ipython\", Version.parse(\"5.7.0\"))\n\n    expected: list[dict[str, Any]] = [\n        {\n            \"file\": \"ipython-5.7.0-py2-none-any.whl\",\n            # in the links provided by the legacy repository, this file only has a md5 hash,\n            # the sha256 is generated on the fly\n            \"hash\": f\"sha256:{dist_hash_getter('ipython-5.7.0-py2-none-any.whl').sha256}\",\n            \"url\": get_legacy_dist_url(\"ipython-5.7.0-py2-none-any.whl\"),\n        },\n        {\n            \"file\": \"ipython-5.7.0-py3-none-any.whl\",\n            \"hash\": f\"sha256:{dist_hash_getter('ipython-5.7.0-py3-none-any.whl').sha256}\",\n            \"url\": get_legacy_dist_url(\"ipython-5.7.0-py3-none-any.whl\"),\n        },\n        {\n            \"file\": \"ipython-5.7.0.tar.gz\",\n            \"hash\": f\"sha256:{dist_hash_getter('ipython-5.7.0.tar.gz').sha256}\",\n            \"url\": get_legacy_dist_url(\"ipython-5.7.0.tar.gz\"),\n        },\n    ]\n    if repo.json:\n        for file_info in expected:\n            size, upload_time = get_legacy_dist_size_and_upload_time(file_info[\"file\"])\n            if size is not None:\n                file_info[\"size\"] = size\n            if upload_time is not None:\n                file_info[\"upload_time\"] = upload_time\n\n    assert package.files == expected\n\n\ndef test_get_package_retrieves_packages_with_no_hashes(\n    legacy_repository: LegacyRepository,\n    dist_hash_getter: DistributionHashGetter,\n    get_legacy_dist_url: Callable[[str], str],\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(\"jupyter\", Version.parse(\"1.0.0\"))\n\n    assert [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n            \"url\": get_legacy_dist_url(filename),\n        }\n        for filename in [\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ] == package.files\n\n\n@pytest.mark.parametrize(\n    \"package_name, version, yanked, yanked_reason\",\n    [\n        (\"black\", \"19.10b0\", False, \"\"),\n        (\"black\", \"21.11b0\", True, \"Broken regex dependency. Use 21.11b1 instead.\"),\n    ],\n)\ndef test_package_yanked(\n    package_name: str,\n    version: str,\n    yanked: bool,\n    yanked_reason: str,\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(package_name, Version.parse(version))\n\n    assert package.name == package_name\n    assert str(package.version) == version\n    assert package.yanked is yanked\n    assert package.yanked_reason == yanked_reason\n\n\ndef test_package_partial_yank(\n    legacy_repository_html: LegacyRepository,\n    legacy_repository_partial_yank: LegacyRepository,\n) -> None:\n    repo = legacy_repository_html\n    package = repo.package(\"futures\", Version.parse(\"3.2.0\"))\n    assert len(package.files) == 2\n\n    repo = legacy_repository_partial_yank\n    package = repo.package(\"futures\", Version.parse(\"3.2.0\"))\n    assert len(package.files) == 1\n    assert package.files[0][\"file\"].endswith(\".tar.gz\")\n\n\n@pytest.mark.parametrize(\n    \"package_name, version, yanked, yanked_reason\",\n    [\n        (\"black\", \"19.10b0\", False, \"\"),\n        (\"black\", \"21.11b0\", True, \"Broken regex dependency. Use 21.11b1 instead.\"),\n    ],\n)\ndef test_find_links_for_package_yanked(\n    package_name: str,\n    version: str,\n    yanked: bool,\n    yanked_reason: str,\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n\n    package = repo.package(package_name, Version.parse(version))\n    links = repo.find_links_for_package(package)\n\n    assert len(links) == 2\n    for link in links:\n        assert link.yanked == yanked\n        assert link.yanked_reason == yanked_reason\n\n\ndef test_cached_or_downloaded_file_supports_trailing_slash(\n    legacy_repository: LegacyRepository,\n) -> None:\n    repo = legacy_repository\n    with repo._cached_or_downloaded_file(\n        Link(\"https://files.pythonhosted.org/pytest-3.5.0-py2.py3-none-any.whl/\")\n    ) as filepath:\n        assert filepath.name == \"pytest-3.5.0-py2.py3-none-any.whl\"\n\n\nclass MockHttpRepository(LegacyRepository):\n    def __init__(\n        self, endpoint_responses: dict[str, int], http: responses.RequestsMock\n    ) -> None:\n        base_url = \"http://legacy.foo.bar\"\n        super().__init__(\"legacy\", url=base_url, disable_cache=True)\n\n        for endpoint, response in endpoint_responses.items():\n            url = base_url + endpoint\n            http.get(url, status=response)\n\n\ndef test_get_200_returns_page(http: responses.RequestsMock) -> None:\n    repo = MockHttpRepository({\"/foo/\": 200}, http)\n\n    _ = repo.get_page(\"foo\")\n\n\n@pytest.mark.parametrize(\"status_code\", [401, 403, 404])\ndef test_get_40x_and_returns_none(\n    http: responses.RequestsMock, status_code: int\n) -> None:\n    repo = MockHttpRepository({\"/foo/\": status_code}, http)\n\n    with pytest.raises(PackageNotFoundError):\n        repo.get_page(\"foo\")\n\n\ndef test_get_5xx_raises(\n    http: responses.RequestsMock, disable_http_status_force_list: None\n) -> None:\n    repo = MockHttpRepository({\"/foo/\": 500}, http)\n\n    with pytest.raises(RepositoryError):\n        repo.get_page(\"foo\")\n\n\ndef test_get_redirected_response_url(\n    http: responses.RequestsMock, monkeypatch: MonkeyPatch\n) -> None:\n    repo = MockHttpRepository({\"/foo/\": 200}, http)\n    redirect_url = \"http://legacy.redirect.bar\"\n\n    def get_mock(*args: Any, **kwargs: Any) -> requests.Response:\n        response = requests.Response()\n        response.status_code = 200\n        response.url = redirect_url + \"/foo\"\n        return response\n\n    monkeypatch.setattr(repo.session, \"get\", get_mock)\n    page = repo.get_page(\"foo\")\n    assert page is not None\n    assert page._url == \"http://legacy.redirect.bar/foo\"\n\n\ndef test_get_page_prefers_json(http: responses.RequestsMock) -> None:\n    repo = MockHttpRepository({\"/foo/\": 200}, http)\n\n    _ = repo.get_page(\"foo\")\n\n    accepted = [\n        item.strip()\n        for item in http.calls[-1].request.headers.get(\"Accept\", \"\").split(\",\")\n    ]\n    preferred = [item for item in accepted if \"q=0\" not in item.split(\";\")[-1]]\n\n    assert preferred == [\"application/vnd.pypi.simple.v1+json\"]\n    assert any(\"*/*\" in item for item in accepted)\n\n\ndef test_root_page_prefers_json(http: responses.RequestsMock) -> None:\n    repo = MockHttpRepository({\"/\": 200}, http)\n\n    _ = repo.root_page\n\n    accepted = [\n        item.strip()\n        for item in http.calls[-1].request.headers.get(\"Accept\", \"\").split(\",\")\n    ]\n    preferred = [item for item in accepted if \"q=0\" not in item.split(\";\")[-1]]\n\n    assert preferred == [\"application/vnd.pypi.simple.v1+json\"]\n    assert any(\"*/*\" in item for item in accepted)\n\n\n@pytest.mark.parametrize(\n    (\"repositories\",),\n    [\n        ({},),\n        # ensure path is respected\n        ({\"publish\": {\"url\": \"https://foo.bar/legacy\"}},),\n        # ensure path length does not give incorrect results\n        ({\"publish\": {\"url\": \"https://foo.bar/upload/legacy\"}},),\n    ],\n)\ndef test_authenticator_with_implicit_repository_configuration(\n    http: responses.RequestsMock,\n    config: Config,\n    repositories: dict[str, dict[str, str]],\n) -> None:\n    http.get(\n        re.compile(\"^https?://foo.bar/(.+?)$\"),\n    )\n\n    config.merge(\n        {\n            \"repositories\": repositories,\n            \"http-basic\": {\n                \"source\": {\"username\": \"foo\", \"password\": \"bar\"},\n                \"publish\": {\"username\": \"baz\", \"password\": \"qux\"},\n            },\n        }\n    )\n\n    repo = LegacyRepository(name=\"source\", url=\"https://foo.bar/simple\", config=config)\n    repo.get_page(\"/foo\")\n\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n"
  },
  {
    "path": "tests/repositories/test_lockfile_repository.py",
    "content": "from __future__ import annotations\n\nfrom copy import deepcopy\n\nimport pytest\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.repositories.lockfile_repository import LockfileRepository\n\n\n@pytest.fixture(scope=\"module\")\ndef packages() -> list[Package]:\n    return [\n        Package(\"a\", \"1.0\", source_type=\"url\", source_url=\"https://example.org/a.whl\"),\n        Package(\"a\", \"1.0\"),\n        Package(\n            \"a\", \"1.0\", source_type=\"url\", source_url=\"https://example.org/a-1.whl\"\n        ),\n    ]\n\n\ndef test_has_package(packages: list[Package]) -> None:\n    url_package, pypi_package, url_package_2 = packages\n    repo = LockfileRepository()\n\n    assert not repo.has_package(url_package)\n    repo.add_package(url_package)\n\n    assert not repo.has_package(pypi_package)\n    repo.add_package(pypi_package)\n\n    assert not repo.has_package(url_package_2)\n    repo.add_package(url_package_2)\n\n    assert len(repo.packages) == 3\n    assert repo.has_package(deepcopy(url_package))\n    assert repo.has_package(deepcopy(pypi_package))\n    assert repo.has_package(deepcopy(url_package_2))\n"
  },
  {
    "path": "tests/repositories/test_pypi_repository.py",
    "content": "from __future__ import annotations\n\nfrom io import BytesIO\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.packages.dependency import Dependency\nfrom requests.exceptions import TooManyRedirects\nfrom requests.models import Response\n\nfrom poetry.factory import Factory\nfrom poetry.repositories.pypi_repository import PyPiRepository\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\n    from pytest_mock import MockerFixture\n\n    from tests.types import DistributionHashGetter\n\n\n@pytest.fixture(autouse=True)\ndef _use_simple_keyring(with_simple_keyring: None) -> None:\n    pass\n\n\ndef test_find_packages(pypi_repository: PyPiRepository) -> None:\n    repo = pypi_repository\n    packages = repo.find_packages(Factory.create_dependency(\"requests\", \"~2.18.0\"))\n\n    assert len(packages) == 5\n\n\ndef test_find_packages_with_prereleases(pypi_repository: PyPiRepository) -> None:\n    repo = pypi_repository\n    packages = repo.find_packages(Factory.create_dependency(\"toga\", \">=0.3.0.dev2\"))\n\n    assert len(packages) == 2\n\n\ndef test_find_packages_does_not_select_prereleases_if_not_allowed(\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n    packages = repo.find_packages(Factory.create_dependency(\"pyyaml\", \"*\"))\n\n    assert len(packages) == 1\n\n\n@pytest.mark.parametrize(\n    [\"constraint\", \"count\"], [(\"*\", 1), (\">=1\", 1), (\"<=18\", 0), (\">=19.0.0a0\", 1)]\n)\ndef test_find_packages_only_prereleases(\n    constraint: str, count: int, pypi_repository: PyPiRepository\n) -> None:\n    repo = pypi_repository\n    packages = repo.find_packages(Factory.create_dependency(\"black\", constraint))\n\n    assert len(packages) == count\n\n\n@pytest.mark.parametrize(\n    [\"constraint\", \"expected\"],\n    [\n        # yanked 21.11b0 is ignored except for pinned version\n        (\"*\", [\"19.10b0\"]),\n        (\">=19.0a0\", [\"19.10b0\"]),\n        (\">=20.0a0\", []),\n        (\">=21.11b0\", []),\n        (\"==21.11b0\", [\"21.11b0\"]),\n    ],\n)\ndef test_find_packages_yanked(\n    constraint: str, expected: list[str], pypi_repository: PyPiRepository\n) -> None:\n    repo = pypi_repository\n    packages = repo.find_packages(Factory.create_dependency(\"black\", constraint))\n\n    assert [str(p.version) for p in packages] == expected\n\n\ndef test_package(\n    pypi_repository: PyPiRepository,\n    dist_hash_getter: DistributionHashGetter,\n    get_pypi_file_info: Callable[[str], dict[str, Any]],\n) -> None:\n    repo = pypi_repository\n\n    package = repo.package(\"requests\", Version.parse(\"2.18.4\"))\n\n    assert package.name == \"requests\"\n    assert len(package.requires) == 9\n    assert len([r for r in package.requires if r.is_optional()]) == 5\n    assert len(package.extras[canonicalize_name(\"security\")]) == 3\n    assert len(package.extras[canonicalize_name(\"socks\")]) == 2\n\n    assert package.files == [\n        {\n            \"file\": filename,\n            \"hash\": f\"sha256:{dist_hash_getter(filename).sha256}\",\n            \"url\": (file_info := get_pypi_file_info(filename))[\"url\"],\n            \"size\": file_info[\"size\"],\n            \"upload_time\": file_info[\"upload_time_iso_8601\"],\n        }\n        for filename in [\n            f\"{package.name}-{package.version}-py2.py3-none-any.whl\",\n            f\"{package.name}-{package.version}.tar.gz\",\n        ]\n    ]\n    win_inet = package.extras[canonicalize_name(\"socks\")][1]\n    assert win_inet.name == \"win-inet-pton\"\n    assert win_inet.python_versions in {\"~2.7 || ~2.6\", \">=2.6 <2.8\"}\n\n    # Different versions of poetry-core simplify the following marker differently,\n    # either is fine.\n    marker1 = (\n        'sys_platform == \"win32\" and (python_version == \"2.7\" or python_version =='\n        ' \"2.6\") and extra == \"socks\"'\n    )\n    marker2 = (\n        'sys_platform == \"win32\" and python_version == \"2.7\" and extra == \"socks\" or'\n        ' sys_platform == \"win32\" and python_version == \"2.6\" and extra == \"socks\"'\n    )\n    marker3 = (\n        'sys_platform == \"win32\" and python_version >= \"2.6\" and python_version < '\n        '\"2.8\" and extra == \"socks\"'\n    )\n    assert str(win_inet.marker) in {marker1, marker2, marker3}\n\n\n@pytest.mark.parametrize(\n    \"package_name, version, yanked, yanked_reason\",\n    [\n        (\"black\", \"19.10b0\", False, \"\"),\n        (\"black\", \"21.11b0\", True, \"Broken regex dependency. Use 21.11b1 instead.\"),\n    ],\n)\ndef test_package_yanked(\n    package_name: str,\n    version: str,\n    yanked: bool,\n    yanked_reason: str,\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n\n    package = repo.package(package_name, Version.parse(version))\n\n    assert package.name == package_name\n    assert str(package.version) == version\n    assert package.yanked is yanked\n    assert package.yanked_reason == yanked_reason\n\n\n@pytest.mark.parametrize(\"fallback\", [False, True])\ndef test_package_yanked_no_dependencies(\n    pypi_repository: PyPiRepository, fallback: bool\n) -> None:\n    repo = pypi_repository\n    repo._fallback = fallback\n\n    package = repo.package(\"isodate\", Version.parse(\"0.7.0\"))\n\n    assert package.name == \"isodate\"\n    assert str(package.version) == \"0.7.0\"\n    assert package.yanked is True\n    assert package.yanked_reason == \"fails for py2.7 but is not marked as py3 only.\"\n\n\ndef test_package_not_canonicalized(pypi_repository: PyPiRepository) -> None:\n    repo = pypi_repository\n\n    package = repo.package(\"discord.py\", Version.parse(\"2.0.0\"))\n\n    assert package.name == \"discord-py\"\n    assert package.pretty_name == \"discord.py\"\n\n\n@pytest.mark.parametrize(\n    \"package_name, version, yanked, yanked_reason\",\n    [\n        (\"black\", \"19.10b0\", False, \"\"),\n        (\"black\", \"21.11b0\", True, \"Broken regex dependency. Use 21.11b1 instead.\"),\n    ],\n)\ndef test_find_links_for_package_yanked(\n    package_name: str,\n    version: str,\n    yanked: bool,\n    yanked_reason: str,\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n\n    package = repo.package(package_name, Version.parse(version))\n    links = repo.find_links_for_package(package)\n\n    assert len(links) == 2\n    for link in links:\n        assert link.yanked == yanked\n        assert link.yanked_reason == yanked_reason\n\n\ndef test_fallback_on_downloading_packages(pypi_repository: PyPiRepository) -> None:\n    repo = pypi_repository\n    repo._fallback = True\n\n    package = repo.package(\"jupyter\", Version.parse(\"1.0.0\"))\n\n    assert package.name == \"jupyter\"\n    assert len(package.requires) == 6\n\n    dependency_names = sorted(dep.name for dep in package.requires)\n    assert dependency_names == [\n        \"ipykernel\",\n        \"ipywidgets\",\n        \"jupyter-console\",\n        \"nbconvert\",\n        \"notebook\",\n        \"qtconsole\",\n    ]\n\n\ndef test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found(\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n    repo._fallback = True\n\n    package = repo.package(\"isort\", Version.parse(\"4.3.4\"))\n\n    assert package.name == \"isort\"\n    assert len(package.requires) == 1\n\n    dep = package.requires[0]\n    assert dep.name == \"futures\"\n    assert dep.python_versions == \"~2.7\"\n\n\ndef test_fallback_pep_658_metadata(\n    mocker: MockerFixture, pypi_repository: PyPiRepository\n) -> None:\n    repo = pypi_repository\n    repo._fallback = True\n\n    spy = mocker.spy(repo, \"_get_info_from_metadata\")\n\n    try:\n        package = repo.package(\"isort-metadata\", Version.parse(\"4.3.4\"))\n    except FileNotFoundError:\n        pytest.fail(\"Metadata was not successfully retrieved\")\n    else:\n        assert spy.call_count > 0\n        assert spy.spy_return is not None\n\n        assert package.name == \"isort-metadata\"\n        assert len(package.requires) == 1\n\n        dep = package.requires[0]\n        assert dep.name == \"futures\"\n        assert dep.python_versions == \"~2.7\"\n\n\ndef test_pypi_repository_supports_reading_bz2_files(\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n    repo._fallback = True\n    package = repo.package(\"twisted\", Version.parse(\"18.9.0\"))\n\n    assert package.name == \"twisted\"\n    assert len(package.requires) == 71\n    assert sorted(\n        (r for r in package.requires if not r.is_optional()), key=lambda r: r.name\n    ) == [\n        Dependency(\"attrs\", \">=17.4.0\"),\n        Dependency(\"Automat\", \">=0.3.0\"),\n        Dependency(\"constantly\", \">=15.1\"),\n        Dependency(\"hyperlink\", \">=17.1.1\"),\n        Dependency(\"incremental\", \">=16.10.1\"),\n        Dependency(\"PyHamcrest\", \">=1.9.0\"),\n        Dependency(\"zope.interface\", \">=4.4.2\"),\n    ]\n\n    expected_extras = {\n        \"all-non-platform\": [\n            Dependency(\"appdirs\", \">=1.4.0\"),\n            Dependency(\"cryptography\", \">=1.5\"),\n            Dependency(\"h2\", \">=3.0,<4.0\"),\n            Dependency(\"idna\", \">=0.6,!=2.3\"),\n            Dependency(\"priority\", \">=1.1.0,<2.0\"),\n            Dependency(\"pyasn1\", \"*\"),\n            Dependency(\"pyopenssl\", \">=16.0.0\"),\n            Dependency(\"pyserial\", \">=3.0\"),\n            Dependency(\"service_identity\", \"*\"),\n            Dependency(\"soappy\", \"*\"),\n        ]\n    }\n\n    for name, expected_extra in expected_extras.items():\n        assert (\n            sorted(package.extras[canonicalize_name(name)], key=lambda r: r.name)\n            == expected_extra\n        )\n\n\ndef test_invalid_versions_ignored(pypi_repository: PyPiRepository) -> None:\n    repo = pypi_repository\n\n    # the json metadata for this package contains one malformed version\n    # and a correct one.\n    packages = repo.find_packages(\n        Factory.create_dependency(\"invalid-version-package\", \"*\")\n    )\n    assert len(packages) == 1\n\n\n@pytest.mark.usefixtures(\"pypi_repository\")\ndef test_get_should_invalid_cache_on_too_many_redirects_error(\n    mocker: MockerFixture,\n) -> None:\n    delete_cache = mocker.patch(\"cachecontrol.caches.file_cache.FileCache.delete\")\n\n    response = Response()\n    response.status_code = 200\n    response.encoding = \"utf-8\"\n    response.raw = BytesIO(b'{\"foo\": \"bar\"}')\n    mocker.patch(\n        \"poetry.utils.authenticator.Authenticator.get\",\n        side_effect=[TooManyRedirects(), response],\n    )\n    repository = PyPiRepository()\n    repository._get(\"https://pypi.org/pypi/async-timeout/json\")\n\n    assert delete_cache.called\n\n\ndef test_urls(pypi_repository: PyPiRepository) -> None:\n    repository = pypi_repository\n\n    assert repository.url == \"https://pypi.org/simple/\"\n    assert repository.authenticated_url == \"https://pypi.org/simple/\"\n\n\ndef test_find_links_for_package_of_supported_types(\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n    package = repo.find_packages(Factory.create_dependency(\"hbmqtt\", \"0.9.6\"))\n\n    assert len(package) == 1\n\n    links = repo.find_links_for_package(package[0])\n\n    assert len(links) == 1\n    assert links[0].is_sdist\n    assert links[0].show_url == \"hbmqtt-0.9.6.tar.gz\"\n\n\ndef test_get_release_info_includes_only_supported_types(\n    pypi_repository: PyPiRepository,\n) -> None:\n    repo = pypi_repository\n\n    release_info = repo._get_release_info(\n        name=canonicalize_name(\"hbmqtt\"), version=Version.parse(\"0.9.6\")\n    )\n\n    assert len(release_info[\"files\"]) == 1\n    assert release_info[\"files\"][0][\"file\"] == \"hbmqtt-0.9.6.tar.gz\"\n\n\n@pytest.mark.parametrize(\n    (\"query\", \"count\"),\n    [\n        (\"non-existent\", 0),  # no match\n        (\"requests\", 6),  # exact match\n        (\"hbmqtt==0.9.6\", 1),  # exact dependency match\n        (\"requests>=2.18.4\", 2),  # range dependency match\n        (\"request\", 0),  # partial match\n        (\"reques*\", 0),  # bad token\n        (\"reques t\", 0),  # bad token\n        ([\"requests\", \"hbmqtt\"], 7),  # list of tokens\n    ],\n)\ndef test_search_fallbacks_to_find_packages(\n    query: str | list[str],\n    count: int,\n    pypi_repository: PyPiRepository,\n    with_disallowed_pypi_search_html: None,\n) -> None:\n    repo = pypi_repository\n    packages = repo.search(query)\n    assert len(packages) == count\n"
  },
  {
    "path": "tests/repositories/test_repository.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.factory import Factory\nfrom poetry.repositories import Repository\nfrom tests.helpers import get_package\n\n\n@pytest.fixture(scope=\"module\")\ndef repository() -> Repository:\n    repo = Repository(\"repo\")\n\n    # latest version pre-release\n    repo.add_package(get_package(\"foo\", \"1.0\"))\n    repo.add_package(get_package(\"foo\", \"2.0b0\"))\n\n    # latest version yanked\n    repo.add_package(get_package(\"black\", \"19.10b0\"))\n    repo.add_package(get_package(\"black\", \"21.11b0\", yanked=\"reason\"))\n\n    return repo\n\n\n@pytest.mark.parametrize(\n    (\"allow_prereleases\", \"constraint\", \"expected\"),\n    [\n        (None, \">=1.0\", [\"1.0\"]),\n        (False, \">=1.0\", [\"1.0\"]),\n        (True, \">=1.0\", [\"1.0\", \"2.0b0\"]),\n        (None, \">=1.5\", [\"2.0b0\"]),\n        (False, \">=1.5\", []),\n        (True, \">=1.5\", [\"2.0b0\"]),\n    ],\n)\ndef test_find_packages_allow_prereleases(\n    repository: Repository,\n    allow_prereleases: bool | None,\n    constraint: str,\n    expected: list[str],\n) -> None:\n    packages = repository.find_packages(\n        Factory.create_dependency(\n            \"foo\", {\"version\": constraint, \"allow-prereleases\": allow_prereleases}\n        )\n    )\n\n    assert [str(p.version) for p in packages] == expected\n\n\n@pytest.mark.parametrize(\n    [\"constraint\", \"expected\"],\n    [\n        # yanked 21.11b0 is ignored except for pinned version\n        (\"*\", [\"19.10b0\"]),\n        (\">=19.0a0\", [\"19.10b0\"]),\n        (\">=20.0a0\", []),\n        (\">=21.11b0\", []),\n        (\"==21.11b0\", [\"21.11b0\"]),\n    ],\n)\ndef test_find_packages_yanked(\n    repository: Repository, constraint: str, expected: list[str]\n) -> None:\n    packages = repository.find_packages(Factory.create_dependency(\"black\", constraint))\n\n    assert [str(p.version) for p in packages] == expected\n\n\n@pytest.mark.parametrize(\n    \"package_name, version, yanked, yanked_reason\",\n    [\n        (\"black\", \"19.10b0\", False, \"\"),\n        (\"black\", \"21.11b0\", True, \"reason\"),\n    ],\n)\ndef test_package_yanked(\n    repository: Repository,\n    package_name: str,\n    version: str,\n    yanked: bool,\n    yanked_reason: str,\n) -> None:\n    package = repository.package(package_name, Version.parse(version))\n\n    assert package.name == package_name\n    assert str(package.version) == version\n    assert package.yanked is yanked\n    assert package.yanked_reason == yanked_reason\n\n\ndef test_package_pretty_name_is_kept() -> None:\n    pretty_name = \"Not_canoni-calized.name\"\n    repo = Repository(\"repo\")\n    repo.add_package(get_package(pretty_name, \"1.0\"))\n    package = repo.package(pretty_name, Version.parse(\"1.0\"))\n\n    assert package.pretty_name == pretty_name\n\n\ndef test_search() -> None:\n    package_foo1 = get_package(\"foo\", \"1.0.0\")\n    package_foo2 = get_package(\"foo\", \"2.0.0\")\n    package_foobar = get_package(\"foobar\", \"1.0.0\")\n    repo = Repository(\"repo\", [package_foo1, package_foo2, package_foobar])\n\n    assert repo.search(\"foo\") == [package_foo1, package_foo2, package_foobar]\n    assert repo.search(\"bar\") == [package_foobar]\n    assert repo.search(\"nothing\") == []\n"
  },
  {
    "path": "tests/repositories/test_repository_pool.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.repositories import Repository\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.repositories.repository_pool import Priority\nfrom tests.helpers import get_dependency\nfrom tests.helpers import get_package\n\n\ndef test_pool() -> None:\n    pool = RepositoryPool()\n\n    assert len(pool.repositories) == 0\n    assert not pool.has_primary_repositories()\n\n\ndef test_pool_with_initial_repositories() -> None:\n    repo = Repository(\"repo\")\n    pool = RepositoryPool([repo])\n\n    assert len(pool.repositories) == 1\n    assert pool.has_primary_repositories()\n    assert pool.get_priority(\"repo\") == Priority.PRIMARY\n\n\ndef test_repository_no_repository() -> None:\n    pool = RepositoryPool()\n\n    with pytest.raises(IndexError):\n        pool.repository(\"foo\")\n\n\ndef test_adding_repositories_with_same_name_twice_raises_value_error() -> None:\n    repo1 = Repository(\"repo\")\n    repo2 = Repository(\"repo\")\n\n    with pytest.raises(ValueError):\n        RepositoryPool([repo1, repo2])\n\n    with pytest.raises(ValueError):\n        RepositoryPool([repo1]).add_repository(repo2)\n\n\n@pytest.mark.parametrize(\"priority\", (p for p in Priority))\ndef test_repository_from_single_repo_pool(priority: Priority) -> None:\n    repo = LegacyRepository(\"foo\", \"https://foo.bar\")\n    pool = RepositoryPool()\n\n    pool.add_repository(repo, priority=priority)\n\n    assert pool.repository(\"foo\") is repo\n    assert pool.get_priority(\"foo\") == priority\n\n\ndef test_repository_with_all_prio_repositories() -> None:\n    supplemental = LegacyRepository(\"supplemental\", \"https://supplemental.com\")\n    repo1 = LegacyRepository(\"foo\", \"https://foo.bar\")\n    repo2 = LegacyRepository(\"bar\", \"https://bar.baz\")\n    explicit = LegacyRepository(\"explicit\", \"https://bar.baz\")\n\n    pool = RepositoryPool()\n    pool.add_repository(repo1)\n    pool.add_repository(repo2)\n    pool.add_repository(supplemental, priority=Priority.SUPPLEMENTAL)\n    pool.add_repository(explicit, priority=Priority.EXPLICIT)\n\n    assert pool.repository(\"foo\") is repo1\n    assert pool.repository(\"bar\") is repo2\n    assert pool.repository(\"supplemental\") is supplemental\n    assert pool.repository(\"explicit\") is explicit\n    assert pool.has_primary_repositories()\n\n\ndef test_repository_supplemental_repositories_do_show() -> None:\n    supplemental = LegacyRepository(\"supplemental\", \"https://supplemental.com\")\n\n    pool = RepositoryPool()\n    pool.add_repository(supplemental, priority=Priority.SUPPLEMENTAL)\n\n    assert pool.repository(\"supplemental\") is supplemental\n    assert pool.repositories == [supplemental]\n\n\ndef test_repository_explicit_repositories_do_not_show() -> None:\n    explicit = LegacyRepository(\"explicit\", \"https://explicit.com\")\n    primary = LegacyRepository(\"primary\", \"https://primary.com\")\n\n    pool = RepositoryPool()\n    pool.add_repository(explicit, priority=Priority.EXPLICIT)\n    pool.add_repository(primary, priority=Priority.PRIMARY)\n\n    assert pool.repository(\"explicit\") is explicit\n    assert pool.repository(\"primary\") is primary\n    assert pool.repositories == [primary]\n    assert pool.all_repositories == [primary, explicit]\n\n\ndef test_remove_non_existing_repository_raises_indexerror() -> None:\n    pool = RepositoryPool()\n\n    with pytest.raises(IndexError):\n        pool.remove_repository(\"foo\")\n\n\ndef test_remove_existing_repository_successful() -> None:\n    repo1 = LegacyRepository(\"foo\", \"https://foo.bar\")\n    repo2 = LegacyRepository(\"bar\", \"https://bar.baz\")\n    repo3 = LegacyRepository(\"baz\", \"https://baz.quux\")\n\n    pool = RepositoryPool()\n    pool.add_repository(repo1)\n    pool.add_repository(repo2)\n    pool.add_repository(repo3)\n    pool.remove_repository(\"bar\")\n\n    assert pool.repository(\"foo\") is repo1\n    assert not pool.has_repository(\"bar\")\n    assert pool.repository(\"baz\") is repo3\n\n\ndef test_repository_ordering() -> None:\n    primary1 = LegacyRepository(\"primary1\", \"https://primary1.com\")\n    primary2 = LegacyRepository(\"primary2\", \"https://primary2.com\")\n    primary3 = LegacyRepository(\"primary3\", \"https://primary3.com\")\n    supplemental = LegacyRepository(\"supplemental\", \"https://supplemental.com\")\n\n    pool = RepositoryPool()\n    pool.add_repository(supplemental, priority=Priority.SUPPLEMENTAL)\n    pool.add_repository(primary1)\n    pool.add_repository(primary2)\n\n    pool.remove_repository(\"primary2\")\n\n    pool.add_repository(primary3)\n\n    assert pool.repositories == [\n        primary1,\n        primary3,\n        supplemental,\n    ]\n\n\ndef test_pool_get_package_in_any_repository() -> None:\n    package1 = get_package(\"foo\", \"1.0.0\")\n    repo1 = Repository(\"repo1\", [package1])\n    package2 = get_package(\"bar\", \"1.0.0\")\n    repo2 = Repository(\"repo2\", [package1, package2])\n    pool = RepositoryPool([repo1, repo2])\n\n    returned_package1 = pool.package(\"foo\", Version.parse(\"1.0.0\"))\n    returned_package2 = pool.package(\"bar\", Version.parse(\"1.0.0\"))\n\n    assert returned_package1 == package1\n    assert returned_package2 == package2\n\n\ndef test_pool_find_packages_only_considers_supplemental_when_needed() -> None:\n    package1 = get_package(\"foo\", \"1.1.1\")\n    package2 = get_package(\"foo\", \"1.2.3\")\n    package3 = get_package(\"foo\", \"2.0.0\")\n    repo1 = Repository(\"repo1\", [package1, package3])\n    repo2 = Repository(\"repo2\", [package1, package2])\n    pool = RepositoryPool([repo1]).add_repository(repo2, priority=Priority.SUPPLEMENTAL)\n\n    dependency_in_nonsupplemental = get_dependency(\"foo\", \"^1.0.0\")\n    returned_packages_in_nonsupplemental = pool.find_packages(\n        dependency_in_nonsupplemental\n    )\n    dependency_needs_supplemental = get_dependency(\"foo\", \"1.2.3\")\n    returned_packages_needs_supplemental = pool.find_packages(\n        dependency_needs_supplemental\n    )\n\n    assert returned_packages_in_nonsupplemental == [package1]\n    assert returned_packages_needs_supplemental == [package2]\n\n\ndef test_pool_get_package_in_specified_repository() -> None:\n    package = get_package(\"foo\", \"1.0.0\")\n    repo1 = Repository(\"repo1\", [package])\n    repo2 = Repository(\"repo2\", [package])\n    pool = RepositoryPool([repo1]).add_repository(repo2, priority=Priority.SUPPLEMENTAL)\n\n    returned_package = pool.package(\n        \"foo\", Version.parse(\"1.0.0\"), repository_name=\"repo2\"\n    )\n\n    assert returned_package == package\n\n\ndef test_pool_no_package_from_any_repository_raises_package_not_found() -> None:\n    pool = RepositoryPool()\n    pool.add_repository(Repository(\"repo\"))\n\n    with pytest.raises(PackageNotFoundError):\n        pool.package(\"foo\", Version.parse(\"1.0.0\"))\n\n\ndef test_pool_no_package_from_specified_repository_raises_package_not_found() -> None:\n    package = get_package(\"foo\", \"1.0.0\")\n    repo1 = Repository(\"repo1\")\n    repo2 = Repository(\"repo2\", [package])\n    pool = RepositoryPool([repo1, repo2])\n\n    with pytest.raises(PackageNotFoundError):\n        pool.package(\"foo\", Version.parse(\"1.0.0\"), repository_name=\"repo1\")\n\n\ndef test_pool_find_packages_in_any_repository() -> None:\n    package1 = get_package(\"foo\", \"1.1.1\")\n    package2 = get_package(\"foo\", \"1.2.3\")\n    package3 = get_package(\"foo\", \"2.0.0\")\n    package4 = get_package(\"bar\", \"1.2.3\")\n    repo1 = Repository(\"repo1\", [package1, package3])\n    repo2 = Repository(\"repo2\", [package1, package2, package4])\n    pool = RepositoryPool([repo1, repo2])\n\n    available_dependency = get_dependency(\"foo\", \"^1.0.0\")\n    returned_packages_available = pool.find_packages(available_dependency)\n    unavailable_dependency = get_dependency(\"foo\", \"999.9.9\")\n    returned_packages_unavailable = pool.find_packages(unavailable_dependency)\n\n    assert returned_packages_available == [package1, package1, package2]\n    assert returned_packages_unavailable == []\n\n\ndef test_pool_find_packages_in_specified_repository() -> None:\n    package_foo1 = get_package(\"foo\", \"1.1.1\")\n    package_foo2 = get_package(\"foo\", \"1.2.3\")\n    package_foo3 = get_package(\"foo\", \"2.0.0\")\n    package_bar = get_package(\"bar\", \"1.2.3\")\n    repo1 = Repository(\"repo1\", [package_foo1, package_foo3])\n    repo2 = Repository(\"repo2\", [package_foo1, package_foo2, package_bar])\n    pool = RepositoryPool([repo1, repo2])\n\n    available_dependency = get_dependency(\"foo\", \"^1.0.0\")\n    available_dependency.source_name = \"repo2\"\n    returned_packages_available = pool.find_packages(available_dependency)\n    unavailable_dependency = get_dependency(\"foo\", \"999.9.9\")\n    unavailable_dependency.source_name = \"repo2\"\n    returned_packages_unavailable = pool.find_packages(unavailable_dependency)\n\n    assert returned_packages_available == [package_foo1, package_foo2]\n    assert returned_packages_unavailable == []\n\n\ndef test_search_no_legacy_repositories() -> None:\n    package_foo1 = get_package(\"foo\", \"1.0.0\")\n    package_foo2 = get_package(\"foo\", \"2.0.0\")\n    package_foobar = get_package(\"foobar\", \"1.0.0\")\n    repo1 = Repository(\"repo1\", [package_foo1, package_foo2])\n    repo2 = Repository(\"repo2\", [package_foo1, package_foobar])\n    pool = RepositoryPool([repo1, repo2])\n\n    assert pool.search(\"foo\") == [\n        package_foo1,\n        package_foo2,\n        package_foo1,\n        package_foobar,\n    ]\n    assert pool.search(\"bar\") == [package_foobar]\n    assert pool.search(\"nothing\") == []\n\n\ndef test_search_legacy_repositories_are_not_skipped(\n    legacy_repository: LegacyRepository,\n) -> None:\n    foo_package = get_package(\"foo\", \"1.0.0\")\n    demo_package = get_package(\"demo\", \"0.1.0\")\n\n    repo1 = Repository(\"repo1\", [foo_package])\n    repo2 = legacy_repository\n    pool = RepositoryPool([repo1, repo2])\n\n    assert pool.search(\"foo\") == [foo_package]\n\n    assert repo1.search(\"demo\") == []\n    assert repo2.search(\"demo\") == pool.search(\"demo\") == [demo_package]\n"
  },
  {
    "path": "tests/repositories/test_single_page_repository.py",
    "content": "from __future__ import annotations\n\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.packages.dependency import Dependency\n\nfrom poetry.repositories.exceptions import PackageNotFoundError\nfrom poetry.repositories.link_sources.html import HTMLPage\nfrom poetry.repositories.single_page_repository import SinglePageRepository\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n\n\nclass MockSinglePageRepository(SinglePageRepository):\n    FIXTURES = Path(__file__).parent / \"fixtures\" / \"single-page\"\n\n    def __init__(self, page: str) -> None:\n        super().__init__(\n            \"single-page\",\n            url=f\"http://single-page.foo.bar/single/page/repo/{page}.html\",\n            disable_cache=True,\n        )\n        self._lazy_wheel = False\n\n    def _get_page(self, name: NormalizedName) -> HTMLPage:\n        fixture = self.FIXTURES / self.url.rsplit(\"/\", 1)[-1]\n        if not fixture.exists():\n            raise PackageNotFoundError(f\"Package [{name}] not found.\")\n\n        with fixture.open(encoding=\"utf-8\") as f:\n            return HTMLPage(self._url, f.read())\n\n    def _download(\n        self, url: str, dest: Path, *, raise_accepts_ranges: bool = False\n    ) -> None:\n        raise RuntimeError(\"Tests are not configured for downloads\")\n\n\ndef test_single_page_repository_get_page() -> None:\n    repo = MockSinglePageRepository(\"jax_releases\")\n\n    page = repo.get_page(\"/ignored\")\n    links = list(page.links)\n\n    assert len(links) == 21\n\n    for link in links:\n        assert re.match(r\"^(jax|jaxlib)-0\\.3\\.\\d.*\\.(whl|tar\\.gz)$\", link.filename)\n        assert link.netloc == \"storage.googleapis.com\"\n        assert link.path.startswith(\"/jax-releases/\")\n\n\ndef test_single_page_repository_find_packages() -> None:\n    repo = MockSinglePageRepository(\"jax_releases\")\n\n    dep = Dependency(\"jaxlib\", \"0.3.7\")\n\n    packages = repo.find_packages(dep)\n\n    assert len(packages) == 1\n\n    package = packages[0]\n    assert package.name == dep.name\n    assert package.to_dependency().to_pep_508() == dep.to_pep_508()\n\n\ndef test_single_page_repository_get_page_with_relative_links() -> None:\n    repo = MockSinglePageRepository(\"mmcv_torch_releases\")\n\n    base_path = Path(\"/single/page/torch1.12.0\")\n    page = repo.get_page(\"mmcv\")\n    for link in page.links:\n        path = Path(link.path)\n        assert path.parent == base_path\n"
  },
  {
    "path": "tests/test_conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom packaging.utils import canonicalize_name\n\nfrom poetry.factory import Factory\nfrom poetry.repositories import Repository\n\n\nif TYPE_CHECKING:\n    from tests.types import PackageFactory\n\n\n@pytest.fixture\ndef repo() -> Repository:\n    return Repository(\"repo\")\n\n\ndef test_conftest_create_package(\n    create_package: PackageFactory, repo: Repository\n) -> None:\n    dependency = Factory.create_dependency(\n        \"dependency\", {\"version\": \"*\", \"extras\": [\"download\", \"install\"]}\n    )\n    package = create_package(\n        \"A\",\n        \"1.0\",\n        dependencies=[dependency],\n        extras={\n            \"download\": [\"download-package\"],\n            \"install\": [\"install-package\"],\n            \"py38\": [\"py38-package ; python_version == '3.8'\"],\n            \"py310\": [\"py310-package ; python_version > '3.8'\"],\n            \"all\": [\"a[download,install]\"],\n            \"py\": [\"a[py38,py310]\"],\n            \"nested\": [\"a[all]\"],\n        },\n    )\n\n    expected_extras = {\"download\", \"install\", \"py38\", \"py310\", \"all\", \"py\", \"nested\"}\n\n    # test returned package instance\n    assert package.name == \"a\"\n    assert str(package.version) == \"1.0\"\n    assert set(package.extras.keys()) == expected_extras\n\n    # test package was correctly added to the repo\n    assert repo.has_package(package)\n\n    repo_package = repo.package(package.name, package.version)\n    assert repo_package.name == \"a\"\n    assert set(package.extras.keys()) == expected_extras\n\n    assert repo.has_package(create_package(\"download-package\", \"1.0\"))\n    assert repo.has_package(create_package(\"install-package\", \"1.0\"))\n    assert repo.has_package(create_package(\"py38-package\", \"1.0\"))\n    assert repo.has_package(create_package(\"py310-package\", \"1.0\"))\n\n    # verify dependencies were correctly added\n    requirements = {requirement.to_pep_508() for requirement in repo_package.requires}\n\n    assert requirements == {\n        dependency.to_pep_508(),\n        'download-package (>=1.0,<2.0) ; extra == \"download\"',\n        'install-package (>=1.0,<2.0) ; extra == \"install\"',\n        'py310-package (>=1.0,<2.0) ; python_version > \"3.8\" and extra == \"py310\"',\n        'py38-package (>=1.0,<2.0) ; python_version == \"3.8\" and extra == \"py38\"',\n    }\n\n    # verify self-referencing extras\n    assert repo_package.extras[canonicalize_name(\"all\")] == [\n        Factory.create_dependency(\n            \"a\", {\"version\": \"*\", \"extras\": [\"download\", \"install\"]}\n        )\n    ]\n    assert repo_package.extras[canonicalize_name(\"nested\")] == [\n        Factory.create_dependency(\"a\", {\"version\": \"*\", \"extras\": [\"all\"]})\n    ]\n"
  },
  {
    "path": "tests/test_factory.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\n\nfrom cleo.io.buffered_io import BufferedIO\nfrom deepdiff.diff import DeepDiff\nfrom packaging.utils import canonicalize_name\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import parse_constraint\nfrom poetry.core.packages.dependency import Dependency\nfrom poetry.core.packages.package import Package\nfrom poetry.core.packages.vcs_dependency import VCSDependency\n\nfrom poetry.__version__ import __version__\nfrom poetry.exceptions import PoetryError\nfrom poetry.factory import Factory\nfrom poetry.plugins.plugin import Plugin\nfrom poetry.repositories.exceptions import InvalidSourceError\nfrom poetry.repositories.legacy_repository import LegacyRepository\nfrom poetry.repositories.pypi_repository import PyPiRepository\nfrom poetry.repositories.repository_pool import Priority\nfrom poetry.toml.file import TOMLFile\nfrom tests.helpers import mock_metadata_entry_points\n\n\nif TYPE_CHECKING:\n    from cleo.io.io import IO\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from poetry.poetry import Poetry\n    from tests.types import FixtureDirGetter\n\n\nclass MyPlugin(Plugin):\n    def activate(self, poetry: Poetry, io: IO) -> None:\n        io.write_line(\"Setting readmes\")\n        poetry.package.readmes = (Path(\"README.md\"),)\n\n\ndef test_create_poetry(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n\n    package = poetry.package\n\n    assert package.name == \"sample-project\"\n    assert package.version.text == \"1.2.3\"\n    assert package.description == \"Some description.\"\n    assert package.authors == [\"Sébastien Eustace <sebastien@eustace.io>\"]\n    assert package.license is not None\n    assert package.license.id == \"MIT\"\n\n    for readme in package.readmes:\n        assert (\n            readme.relative_to(fixture_dir(\"sample_project\")).as_posix() == \"README.rst\"\n        )\n\n    assert package.homepage == \"https://python-poetry.org\"\n    assert package.repository_url == \"https://github.com/python-poetry/poetry\"\n    assert package.keywords == [\"packaging\", \"dependency\", \"poetry\"]\n\n    assert package.python_versions == \"~2.7 || ^3.6\"\n    assert str(package.python_constraint) == \">=2.7,<2.8 || >=3.6,<4.0\"\n\n    dependencies = {}\n    for dep in package.requires:\n        dependencies[dep.name] = dep\n\n    cleo = dependencies[canonicalize_name(\"cleo\")]\n    assert cleo.pretty_constraint == \"^0.6\"\n    assert not cleo.is_optional()\n\n    pendulum = dependencies[canonicalize_name(\"pendulum\")]\n    assert pendulum.pretty_constraint == \"branch 2.0\"\n    assert pendulum.is_vcs()\n    assert isinstance(pendulum, VCSDependency)\n    assert pendulum.vcs == \"git\"\n    assert pendulum.branch == \"2.0\"\n    assert pendulum.source == \"https://github.com/sdispater/pendulum.git\"\n    assert pendulum.allows_prereleases()\n\n    requests = dependencies[canonicalize_name(\"requests\")]\n    assert requests.pretty_constraint == \"^2.18\"\n    assert not requests.is_vcs()\n    assert not requests.allows_prereleases()\n    assert requests.is_optional()\n    assert requests.extras == frozenset([\"security\"])\n\n    pathlib2 = dependencies[canonicalize_name(\"pathlib2\")]\n    assert pathlib2.pretty_constraint == \"^2.2\"\n    assert parse_constraint(pathlib2.python_versions) == parse_constraint(\"~2.7\")\n    assert not pathlib2.is_optional()\n\n    demo = dependencies[canonicalize_name(\"demo\")]\n    assert demo.is_file()\n    assert not demo.is_vcs()\n    assert demo.name == \"demo\"\n    assert demo.pretty_constraint == \"*\"\n\n    demo = dependencies[canonicalize_name(\"my-package\")]\n    assert not demo.is_file()\n    assert demo.is_directory()\n    assert not demo.is_vcs()\n    assert demo.name == \"my-package\"\n    assert demo.pretty_constraint == \"*\"\n\n    simple_project = dependencies[canonicalize_name(\"simple-project\")]\n    assert not simple_project.is_file()\n    assert simple_project.is_directory()\n    assert not simple_project.is_vcs()\n    assert simple_project.name == \"simple-project\"\n    assert simple_project.pretty_constraint == \"*\"\n\n    functools32 = dependencies[canonicalize_name(\"functools32\")]\n    assert functools32.name == \"functools32\"\n    assert functools32.pretty_constraint == \"^3.2.3\"\n    assert (\n        str(functools32.marker)\n        == 'python_version ~= \"2.7\" and sys_platform == \"win32\" or python_version in'\n        ' \"3.4 3.5\"'\n    )\n\n    assert \"db\" in package.extras\n\n    classifiers = package.classifiers\n\n    assert classifiers == [\n        \"Topic :: Software Development :: Build Tools\",\n        \"Topic :: Software Development :: Libraries :: Python Modules\",\n    ]\n\n    assert package.all_classifiers == [\n        \"License :: OSI Approved :: MIT License\",\n        *(\n            f\"Programming Language :: Python :: {version}\"\n            for version in sorted(\n                Package.AVAILABLE_PYTHONS,\n                key=lambda x: tuple(map(int, x.split(\".\"))),\n            )\n            if package.python_constraint.allows_any(\n                parse_constraint(version + \".*\")\n                if len(version) == 1\n                else Version.parse(version)\n            )\n        ),\n        \"Topic :: Software Development :: Build Tools\",\n        \"Topic :: Software Development :: Libraries :: Python Modules\",\n    ]\n\n\n@pytest.mark.parametrize(\n    (\"project\",),\n    [\n        (\"simple_project_legacy\",),\n        (\"project_with_extras\",),\n    ],\n)\ndef test_create_pyproject_from_package(\n    project: str, fixture_dir: FixtureDirGetter\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(project))\n    package = poetry.package\n\n    pyproject: dict[str, Any] = Factory.create_legacy_pyproject_from_package(package)\n\n    result = pyproject[\"tool\"][\"poetry\"]\n    expected = poetry.pyproject.poetry_config\n\n    # Extras are normalized as they are read.\n    extras = expected.pop(\"extras\", None)\n    if extras is not None:\n        normalized_extras = {\n            canonicalize_name(extra): dependencies\n            for extra, dependencies in extras.items()\n        }\n        expected[\"extras\"] = normalized_extras\n\n    # packages do not support this at present\n    expected.pop(\"scripts\", None)\n\n    # remove any empty sections\n    sections = list(expected.keys())\n    for section in sections:\n        if not expected[section]:\n            expected.pop(section)\n\n    assert not DeepDiff(expected, result)\n\n\ndef test_create_poetry_with_packages_and_includes(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"with-include\"))\n\n    package = poetry.package\n\n    assert package.packages == [\n        {\"include\": \"extra_dir/**/*.py\", \"format\": [\"sdist\", \"wheel\"]},\n        {\"include\": \"extra_dir/**/*.py\", \"format\": [\"sdist\", \"wheel\"]},\n        {\"include\": \"my_module.py\", \"format\": [\"sdist\", \"wheel\"]},\n        {\"include\": \"package_with_include\", \"format\": [\"sdist\", \"wheel\"]},\n        {\"include\": \"tests\", \"format\": [\"sdist\"]},\n        {\"include\": \"for_wheel_only\", \"format\": [\"wheel\"]},\n        {\"include\": \"src_package\", \"from\": \"src\", \"format\": [\"sdist\", \"wheel\"]},\n    ]\n\n    assert package.include in (\n        # with https://github.com/python-poetry/poetry-core/pull/773\n        [\n            {\"path\": \"extra_dir/vcs_excluded.txt\", \"format\": [\"sdist\", \"wheel\"]},\n            {\"path\": \"notes.txt\", \"format\": [\"sdist\"]},\n        ],\n        # without https://github.com/python-poetry/poetry-core/pull/773\n        [\n            {\"path\": \"extra_dir/vcs_excluded.txt\", \"format\": [\"sdist\"]},\n            {\"path\": \"notes.txt\", \"format\": [\"sdist\"]},\n        ],\n    )\n\n\ndef test_create_poetry_with_multi_constraints_dependency(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    poetry = Factory().create_poetry(\n        fixture_dir(\"project_with_multi_constraints_dependency\")\n    )\n\n    package = poetry.package\n\n    assert len(package.requires) == 2\n\n\ndef test_create_poetry_non_package_mode(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"non_package_mode\"))\n\n    assert not poetry.is_package_mode\n\n\ndef test_create_poetry_version_ok(fixture_dir: FixtureDirGetter) -> None:\n    io = BufferedIO()\n    Factory().create_poetry(fixture_dir(\"self_version_ok\"), io=io)\n\n    assert io.fetch_output() == \"\"\n    assert io.fetch_error() == \"\"\n\n\ndef test_create_poetry_version_not_ok(fixture_dir: FixtureDirGetter) -> None:\n    with pytest.raises(PoetryError) as e:\n        Factory().create_poetry(fixture_dir(\"self_version_not_ok\"))\n    assert (\n        str(e.value)\n        == f\"This project requires Poetry <1.2, but you are using Poetry {__version__}\"\n    )\n\n\ndef test_create_poetry_check_version_before_validation(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    with pytest.raises(PoetryError) as e:\n        Factory().create_poetry(fixture_dir(\"self_version_not_ok_invalid_config\"))\n    assert (\n        str(e.value)\n        == f\"This project requires Poetry <1.2, but you are using Poetry {__version__}\"\n    )\n\n\n@pytest.mark.parametrize(\n    \"project\",\n    (\"with_primary_source_implicit\", \"with_primary_source_explicit\"),\n)\ndef test_poetry_with_primary_source(\n    project: str, fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    io = BufferedIO()\n    poetry = Factory().create_poetry(fixture_dir(project), io=io)\n\n    assert not poetry.pool.has_repository(\"PyPI\")\n    assert poetry.pool.has_repository(\"foo\")\n    assert poetry.pool.get_priority(\"foo\") is Priority.PRIMARY\n    assert isinstance(poetry.pool.repository(\"foo\"), LegacyRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"foo\"}\n\n\ndef test_poetry_with_multiple_supplemental_sources(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"with_multiple_supplemental_sources\"))\n\n    assert poetry.pool.has_repository(\"PyPI\")\n    assert isinstance(poetry.pool.repository(\"PyPI\"), PyPiRepository)\n    assert poetry.pool.get_priority(\"PyPI\") is Priority.PRIMARY\n    assert poetry.pool.has_repository(\"foo\")\n    assert isinstance(poetry.pool.repository(\"foo\"), LegacyRepository)\n    assert poetry.pool.has_repository(\"bar\")\n    assert isinstance(poetry.pool.repository(\"bar\"), LegacyRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"PyPI\", \"foo\", \"bar\"}\n\n\ndef test_poetry_with_multiple_sources(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"with_multiple_sources\"))\n\n    assert not poetry.pool.has_repository(\"PyPI\")\n    assert poetry.pool.has_repository(\"bar\")\n    assert isinstance(poetry.pool.repository(\"bar\"), LegacyRepository)\n    assert poetry.pool.has_repository(\"foo\")\n    assert isinstance(poetry.pool.repository(\"foo\"), LegacyRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"bar\", \"foo\"}\n\n\ndef test_poetry_with_multiple_sources_pypi(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    io = BufferedIO()\n    poetry = Factory().create_poetry(fixture_dir(\"with_multiple_sources_pypi\"), io=io)\n\n    assert len(poetry.pool.repositories) == 4\n    assert poetry.pool.has_repository(\"PyPI\")\n    assert isinstance(poetry.pool.repository(\"PyPI\"), PyPiRepository)\n    assert poetry.pool.get_priority(\"PyPI\") is Priority.PRIMARY\n    # PyPI must be between bar and baz!\n    expected = [\"bar\", \"PyPI\", \"baz\", \"foo\"]\n    assert [repo.name for repo in poetry.pool.repositories] == expected\n\n\ndef test_poetry_with_no_default_source(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n\n    assert poetry.pool.has_repository(\"PyPI\")\n    assert poetry.pool.get_priority(\"PyPI\") is Priority.PRIMARY\n    assert isinstance(poetry.pool.repository(\"PyPI\"), PyPiRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"PyPI\"}\n\n\ndef test_poetry_with_supplemental_source(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    io = BufferedIO()\n    poetry = Factory().create_poetry(fixture_dir(\"with_supplemental_source\"), io=io)\n\n    assert poetry.pool.has_repository(\"PyPI\")\n    assert poetry.pool.get_priority(\"PyPI\") is Priority.PRIMARY\n    assert isinstance(poetry.pool.repository(\"PyPI\"), PyPiRepository)\n    assert poetry.pool.has_repository(\"supplemental\")\n    assert poetry.pool.get_priority(\"supplemental\") is Priority.SUPPLEMENTAL\n    assert isinstance(poetry.pool.repository(\"supplemental\"), LegacyRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"PyPI\", \"supplemental\"}\n    assert io.fetch_error() == \"\"\n\n\ndef test_poetry_with_explicit_source(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    io = BufferedIO()\n    poetry = Factory().create_poetry(fixture_dir(\"with_explicit_source\"), io=io)\n\n    assert len(poetry.pool.repositories) == 1\n    assert len(poetry.pool.all_repositories) == 2\n    assert poetry.pool.has_repository(\"PyPI\")\n    assert poetry.pool.get_priority(\"PyPI\") is Priority.PRIMARY\n    assert isinstance(poetry.pool.repository(\"PyPI\"), PyPiRepository)\n    assert poetry.pool.has_repository(\"explicit\")\n    assert isinstance(poetry.pool.repository(\"explicit\"), LegacyRepository)\n    assert {repo.name for repo in poetry.pool.repositories} == {\"PyPI\"}\n    assert io.fetch_error() == \"\"\n\n\ndef test_poetry_with_explicit_pypi_and_other(\n    fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    io = BufferedIO()\n    poetry = Factory().create_poetry(fixture_dir(\"with_explicit_pypi_and_other\"), io=io)\n\n    assert len(poetry.pool.repositories) == 1\n    assert len(poetry.pool.all_repositories) == 2\n    error = io.fetch_error()\n    assert error == \"\"\n\n\n@pytest.mark.parametrize(\n    \"project\", [\"with_explicit_pypi_no_other\", \"with_explicit_pypi_and_other_explicit\"]\n)\ndef test_poetry_with_pypi_explicit_only(\n    project: str, fixture_dir: FixtureDirGetter, with_simple_keyring: None\n) -> None:\n    with pytest.raises(PoetryError) as e:\n        Factory().create_poetry(fixture_dir(project))\n    assert str(e.value) == \"At least one source must not be configured as 'explicit'.\"\n\n\ndef test_poetry_with_build_constraints(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"build_constraints\"))\n    assert set(poetry.build_constraints) == {\n        \"legacy-lib\",\n        \"no-constraints\",\n        \"c-ext-lib\",\n    }\n    assert poetry.build_constraints[canonicalize_name(\"legacy-lib\")] == [\n        Dependency(\"setuptools\", \"<75\")\n    ]\n    assert poetry.build_constraints[canonicalize_name(\"no-constraints\")] == []\n    assert poetry.build_constraints[canonicalize_name(\"c-ext-lib\")] == [\n        Dependency(\"Cython\", \"<3.1\"),\n        Dependency(\"setuptools\", \">=60,<75\"),\n        Dependency(\"setuptools\", \">=75\"),\n    ]\n\n\ndef test_poetry_with_empty_build_constraints(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"build_constraints_empty\"))\n    assert set(poetry.build_constraints) == set()\n\n\ndef test_validate(fixture_dir: FixtureDirGetter) -> None:\n    complete = TOMLFile(fixture_dir(\"complete.toml\"))\n    pyproject: dict[str, Any] = complete.read()\n\n    assert Factory.validate(pyproject) == {\"errors\": [], \"warnings\": []}\n\n\ndef test_validate_fails(fixture_dir: FixtureDirGetter) -> None:\n    complete = TOMLFile(fixture_dir(\"complete.toml\"))\n    pyproject: dict[str, Any] = complete.read()\n    pyproject[\"tool\"][\"poetry\"][\"this key is not in the schema\"] = \"\"\n    pyproject[\"tool\"][\"poetry\"][\"source\"] = {}\n\n    expected = [\n        \"tool.poetry.source must be array\",\n        (\n            \"Additional properties are not allowed \"\n            \"('this key is not in the schema' was unexpected)\"\n        ),\n    ]\n\n    assert Factory.validate(pyproject) == {\"errors\": expected, \"warnings\": []}\n\n\ndef test_create_poetry_fails_on_invalid_configuration(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    with pytest.raises(RuntimeError) as e:\n        Factory().create_poetry(fixture_dir(\"invalid_pyproject_dep_name\"))\n\n    expected = \"\"\"\\\nThe Poetry configuration is invalid:\n  - Project name (invalid) is same as one of its dependencies\n\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_create_poetry_fails_on_nameless_project(\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    with pytest.raises(RuntimeError) as e:\n        Factory().create_poetry(fixture_dir(\"nameless_pyproject\"))\n\n    expected = \"\"\"\\\nThe Poetry configuration is invalid:\n  - Either [project.name] or [tool.poetry.name] is required in package mode.\n\"\"\"\n\n    assert str(e.value) == expected\n\n\ndef test_create_poetry_with_local_config(fixture_dir: FixtureDirGetter) -> None:\n    poetry = Factory().create_poetry(fixture_dir(\"with_local_config\"))\n\n    assert not poetry.config.get(\"virtualenvs.in-project\")\n    assert not poetry.config.get(\"virtualenvs.create\")\n    assert not poetry.config.get(\"virtualenvs.options.always-copy\")\n    assert not poetry.config.get(\"virtualenvs.options.no-pip\")\n    assert not poetry.config.get(\"virtualenvs.options.system-site-packages\")\n\n\ndef test_create_poetry_with_plugins(\n    mocker: MockerFixture, fixture_dir: FixtureDirGetter\n) -> None:\n    mock_metadata_entry_points(mocker, MyPlugin)\n\n    poetry = Factory().create_poetry(fixture_dir(\"sample_project\"))\n\n    assert poetry.package.readmes == (Path(\"README.md\"),)\n\n\n@pytest.mark.parametrize(\n    (\"source\", \"expected\"),\n    [\n        ({}, \"Missing [name] in source.\"),\n        ({\"name\": \"foo\"}, \"Missing [url] in source 'foo'.\"),\n        (\n            {\"name\": \"PyPI\", \"url\": \"https://example.com\"},\n            \"The PyPI repository cannot be configured with a custom url.\",\n        ),\n    ],\n)\ndef test_create_package_source_invalid(\n    source: dict[str, str],\n    expected: str,\n    config: Config,\n    fixture_dir: FixtureDirGetter,\n) -> None:\n    with pytest.raises(InvalidSourceError) as e:\n        Factory.create_package_source(source, config=config)\n        Factory().create_poetry(fixture_dir(\"with_source_pypi_url\"))\n\n    assert str(e.value) == expected\n"
  },
  {
    "path": "tests/test_helpers.py",
    "content": "from __future__ import annotations\n\nimport os\n\nfrom tests.helpers import flatten_dict\nfrom tests.helpers import isolated_environment\n\n\ndef test_flatten_dict() -> None:\n    orig_dict = {\n        \"a\": 1,\n        \"b\": 2,\n        \"c\": {\n            \"x\": 8,\n            \"y\": 9,\n        },\n    }\n\n    flattened_dict = {\n        \"a\": 1,\n        \"b\": 2,\n        \"c:x\": 8,\n        \"c:y\": 9,\n    }\n\n    assert flattened_dict == flatten_dict(orig_dict, delimiter=\":\")\n\n\ndef test_isolated_environment_restores_original_environ() -> None:\n    original_environ = dict(os.environ)\n    with isolated_environment():\n        os.environ[\"TEST_VAR\"] = \"test\"\n    assert os.environ == original_environ\n\n\ndef test_isolated_environment_clears_environ() -> None:\n    os.environ[\"TEST_VAR\"] = \"test\"\n    with isolated_environment(clear=True):\n        assert \"TEST_VAR\" not in os.environ\n    assert \"TEST_VAR\" in os.environ\n\n\ndef test_isolated_environment_updates_environ() -> None:\n    with isolated_environment(environ={\"NEW_VAR\": \"new_value\"}):\n        assert os.environ[\"NEW_VAR\"] == \"new_value\"\n    assert \"NEW_VAR\" not in os.environ\n"
  },
  {
    "path": "tests/types.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\nfrom typing import Any\nfrom typing import Protocol\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from contextlib import AbstractContextManager\n    from pathlib import Path\n\n    from cleo.io.inputs.argument import Argument\n    from cleo.io.inputs.option import Option\n    from cleo.io.io import IO\n    from cleo.testers.command_tester import CommandTester\n    from packaging.utils import NormalizedName\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.package import Package\n    from requests import PreparedRequest\n\n    from poetry.config.config import Config\n    from poetry.config.source import Source\n    from poetry.console.commands.command import Command\n    from poetry.installation import Installer\n    from poetry.installation.executor import Executor\n    from poetry.poetry import Poetry\n    from poetry.repositories.legacy_repository import LegacyRepository\n    from poetry.utils.env import Env\n    from poetry.utils.env.python import Python\n    from tests.repositories.fixtures.distribution_hashes import DistributionHash\n\n    HttpResponse = tuple[int, dict[str, str], bytes | str]  # status code, headers, body\n    HttpRequestCallback = Callable[[PreparedRequest], HttpResponse]\n    HttpRequestCallbackWrapper = Callable[[HttpRequestCallback], HttpRequestCallback]\n\n\nclass CommandTesterFactory(Protocol):\n    def __call__(\n        self,\n        command: str,\n        poetry: Poetry | None = None,\n        installer: Installer | None = None,\n        executor: Executor | None = None,\n        environment: Env | None = None,\n    ) -> CommandTester: ...\n\n\nclass SourcesFactory(Protocol):\n    def __call__(\n        self, poetry: Poetry, sources: Source, config: Config, io: IO\n    ) -> None: ...\n\n\nclass ProjectFactory(Protocol):\n    def __call__(\n        self,\n        name: str | None = None,\n        dependencies: dict[str, str] | None = None,\n        dev_dependencies: dict[str, str] | None = None,\n        pyproject_content: str | None = None,\n        poetry_lock_content: str | None = None,\n        install_deps: bool = True,\n        source: Path | None = None,\n        locker_config: dict[str, Any] | None = None,\n        use_test_locker: bool = True,\n    ) -> Poetry: ...\n\n\nclass PackageFactory(Protocol):\n    def __call__(\n        self,\n        name: str,\n        version: str | None = None,\n        dependencies: list[Dependency] | None = None,\n        extras: dict[str, list[str]] | None = None,\n        merge_extras: bool = False,\n    ) -> Package: ...\n\n\nclass CommandFactory(Protocol):\n    def __call__(\n        self,\n        command_name: str,\n        command_arguments: list[Argument] | None = None,\n        command_options: list[Option] | None = None,\n        command_description: str = \"\",\n        command_help: str = \"\",\n        command_handler: Callable[[Command], int] | str | None = None,\n    ) -> Command: ...\n\n\nclass FixtureDirGetter(Protocol):\n    def __call__(self, name: str) -> Path: ...\n\n\nclass FixtureCopier(Protocol):\n    def __call__(self, relative_path: str, target: Path | None = None) -> Path: ...\n\n\nclass HTMLPageGetter(Protocol):\n    def __call__(self, content: str, base_url: str | None = None) -> str: ...\n\n\nclass NormalizedNameTransformer(Protocol):\n    def __call__(self, name: str) -> NormalizedName: ...\n\n\nclass SpecializedLegacyRepositoryMocker(Protocol):\n    def __call__(\n        self,\n        transformer_or_suffix: NormalizedNameTransformer | str,\n        repository_name: str = \"special\",\n        repository_url: str = \"https://legacy.foo.bar\",\n    ) -> LegacyRepository: ...\n\n\nclass PythonHostedFileMocker(Protocol):\n    def __call__(\n        self,\n        distribution_locations: list[Path],\n        metadata_locations: list[Path],\n    ) -> None: ...\n\n\nclass PackageDistributionLookup(Protocol):\n    def __call__(self, name: str) -> Path | None: ...\n\n\nclass DistributionHashGetter(Protocol):\n    def __call__(self, name: str) -> DistributionHash: ...\n\n\nclass SetProjectContext(Protocol):\n    def __call__(\n        self, project: str | Path, in_place: bool = False\n    ) -> AbstractContextManager[Path]: ...\n\n\nclass MockedPythonRegister(Protocol):\n    def __call__(\n        self,\n        version: str,\n        executable_name: str | Path | None = None,\n        implementation: str | None = None,\n        free_threaded: bool = False,\n        parent: str | Path | None = None,\n        make_system: bool = False,\n    ) -> Python: ...\n\n\nclass MockedPoetryPythonRegister(Protocol):\n    def __call__(\n        self,\n        version: str,\n        implementation: str,\n        free_threaded: bool = False,\n        with_install_dir: bool = False,\n    ) -> Path: ...\n"
  },
  {
    "path": "tests/utils/__init__.py",
    "content": ""
  },
  {
    "path": "tests/utils/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\n\nif TYPE_CHECKING:\n    from poetry.poetry import Poetry\n    from poetry.utils.env import EnvManager\n\n\n@pytest.fixture\ndef venv_name(\n    manager: EnvManager,\n    poetry: Poetry,\n) -> str:\n    return manager.generate_env_name(\n        poetry.package.name,\n        str(poetry.file.path.parent),\n    )\n"
  },
  {
    "path": "tests/utils/env/__init__.py",
    "content": ""
  },
  {
    "path": "tests/utils/env/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.env import EnvManager\n\n\nif TYPE_CHECKING:\n    from poetry.poetry import Poetry\n    from tests.types import FixtureDirGetter\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture\ndef poetry(project_factory: ProjectFactory, fixture_dir: FixtureDirGetter) -> Poetry:\n    return project_factory(\"simple\", source=fixture_dir(\"simple_project\"))\n\n\n@pytest.fixture\ndef manager(poetry: Poetry) -> EnvManager:\n    return EnvManager(poetry)\n"
  },
  {
    "path": "tests/utils/env/python/__init__.py",
    "content": ""
  },
  {
    "path": "tests/utils/env/python/test_manager.py",
    "content": "from __future__ import annotations\n\nimport platform\nimport sys\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\nfrom poetry.core.constraints.version import parse_constraint\n\nfrom poetry.utils.env.python import Python\nfrom tests.helpers import pbs_installer_supported_arch\n\n\nif TYPE_CHECKING:\n    from tests.types import MockedPoetryPythonRegister\n    from tests.types import MockedPythonRegister\n\n\ndef test_find_all(without_mocked_findpython: None) -> None:\n    assert len(list(Python.find_all())) > 1\n\n\ndef test_find_all_with_poetry_managed(\n    without_mocked_findpython: None,\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n) -> None:\n    cpython_path = mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    pypy_path = mocked_poetry_managed_python_register(\"3.10.8\", \"pypy\")\n    found_pythons = list(Python.find_all())\n    assert len(found_pythons) > 3\n    for poetry_python in (cpython_path, pypy_path):\n        assert any(p.executable.parent == poetry_python for p in found_pythons)\n\n\ndef test_find_poetry_managed_pythons_none() -> None:\n    assert list(Python.find_poetry_managed_pythons()) == []\n\n\ndef test_find_poetry_managed_pythons(\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n) -> None:\n    mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    mocked_poetry_managed_python_register(\"3.10.8\", \"pypy\")\n\n    assert len(list(Python.find_poetry_managed_pythons())) == 3\n\n\n@pytest.mark.parametrize(\n    (\"constraint\", \"implementation\", \"free_threaded\", \"expected\"),\n    [\n        (None, None, None, 5),\n        (None, \"CPython\", None, 4),\n        (None, \"cpython\", None, 4),\n        (None, \"pypy\", None, 1),\n        (\"~3.9\", None, None, 2),\n        (\"~3.9\", \"cpython\", None, 2),\n        (\"~3.9\", \"pypy\", None, 0),\n        (\">=3.9.2\", None, None, 4),\n        (\">=3.9.2\", \"cpython\", None, 3),\n        (\">=3.9.2\", \"pypy\", None, 1),\n        (\">=3.10\", None, None, 3),\n        (\">=3.10\", None, False, 2),\n        (\">=3.10\", None, True, 1),\n        (\"~3.11\", None, None, 0),\n    ],\n)\ndef test_find_all_versions(\n    mocked_python_register: MockedPythonRegister,\n    constraint: str | None,\n    implementation: str | None,\n    free_threaded: bool | None,\n    expected: int,\n) -> None:\n    mocked_python_register(\"3.9.1\", implementation=\"CPython\", parent=\"a\")\n    mocked_python_register(\"3.9.3\", implementation=\"CPython\", parent=\"b\")\n    mocked_python_register(\"3.10.4\", implementation=\"PyPy\", parent=\"c\")\n    mocked_python_register(\"3.14.0\", implementation=\"CPython\", parent=\"d\")\n    mocked_python_register(\n        \"3.14.0\", implementation=\"CPython\", free_threaded=True, parent=\"e\"\n    )\n\n    versions = list(Python.find_all_versions(constraint, implementation, free_threaded))\n    assert len(versions) == expected\n\n\n@pytest.mark.parametrize(\"constraint\", [None, \"~3.9\", \">=3.10\"])\ndef test_find_downloadable_versions(constraint: str | None) -> None:\n    versions = list(Python.find_downloadable_versions(constraint))\n    if platform.system() == \"FreeBSD\" or not pbs_installer_supported_arch(\n        platform.machine()\n    ):\n        assert len(versions) == 0\n    else:\n        assert len(versions) > 0\n        if constraint:\n            parsed_constraint = parse_constraint(constraint)\n            assert all(\n                parsed_constraint.allows(\n                    Version.parse(f\"{v.major}.{v.minor}.{v.patch}\")\n                )\n                for v in versions\n            )\n        else:\n            assert len({v.free_threaded for v in versions}) == 2\n            assert len({v.implementation for v in versions}) >= 2\n\n\ndef find_downloadable_versions_include_incompatible() -> None:\n    assert len(\n        list(Python.find_downloadable_versions(include_incompatible=True))\n    ) > len(list(Python.find_downloadable_versions()))\n\n\n@pytest.mark.parametrize(\n    (\"name\", \"expected_minor\"),\n    [\n        (\"3.9\", 9),\n        (\"3.10\", 10),\n        (\"3.11\", None),\n    ],\n)\ndef test_get_by_name_version(\n    mocked_python_register: MockedPythonRegister, name: str, expected_minor: int | None\n) -> None:\n    mocked_python_register(\"3.9.1\", implementation=\"CPython\", parent=\"a\")\n    mocked_python_register(\"3.10.3\", implementation=\"CPython\", parent=\"b\")\n\n    python = Python.get_by_name(name)\n    if expected_minor is None:\n        assert python is None\n    else:\n        assert python is not None\n        assert python.minor == expected_minor\n\n\ndef test_get_by_name_python(without_mocked_findpython: None) -> None:\n    python = Python.get_by_name(\"python\")\n    assert python is not None\n    assert python.version.major == 3\n    assert python.version.minor == sys.version_info.minor\n\n\ndef test_get_by_name_path(without_mocked_findpython: None) -> None:\n    python = Python.get_by_name(sys.executable)\n    assert python is not None\n    assert python.version.major == 3\n    assert python.version.minor == sys.version_info.minor\n"
  },
  {
    "path": "tests/utils/env/python/test_python_installer.py",
    "content": "from __future__ import annotations\n\nfrom subprocess import CalledProcessError\nfrom typing import TYPE_CHECKING\nfrom typing import Literal\nfrom typing import cast\n\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.env.python.installer import PythonDownloadNotFoundError\nfrom poetry.utils.env.python.installer import PythonInstallationError\nfrom poetry.utils.env.python.installer import PythonInstaller\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n    from unittest.mock import MagicMock\n\n    from pytest_mock import MockerFixture\n\n\n@pytest.fixture(autouse=True)\ndef mock_get_download_link(mocker: MockerFixture) -> MagicMock:\n    return mocker.patch(\n        \"pbs_installer.get_download_link\",\n        return_value=(mocker.Mock(major=3, minor=9, micro=1), None),\n    )\n\n\ndef test_python_installer_version() -> None:\n    installer = PythonInstaller(request=\"3.9.1\")\n    assert installer.version == Version.from_parts(3, 9, 1)\n\n\ndef test_python_installer_version_not_found(mock_get_download_link: MagicMock) -> None:\n    mock_get_download_link.return_value = []\n    installer = PythonInstaller(request=\"3.9.1\")\n    with pytest.raises(PythonDownloadNotFoundError):\n        _ = installer.version\n\n\ndef test_python_installer_exists(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"poetry.utils.env.python.Python.find_poetry_managed_pythons\",\n        return_value=[\n            mocker.Mock(\n                implementation=\"cpython\",\n                free_threaded=False,\n                version=Version.from_parts(3, 9, 1),\n            )\n        ],\n    )\n    installer = PythonInstaller(request=\"3.9.1\")\n    assert installer.exists()\n\n\ndef test_python_installer_does_not_exist(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"poetry.utils.env.python.Python.find_poetry_managed_pythons\", return_value=[]\n    )\n    installer = PythonInstaller(request=\"3.9.1\")\n    assert not installer.exists()\n\n\ndef test_python_installer_exists_with_bad_executables(mocker: MockerFixture) -> None:\n    class BadPython:\n        @property\n        def implementation(self) -> str:\n            return \"cpython\"\n\n        @property\n        def free_threaded(self) -> bool:\n            return False\n\n        @property\n        def executable(self) -> Path:\n            return cast(\"Path\", mocker.Mock(as_posix=lambda: \"/path/to/bad/python\"))\n\n        @property\n        def version(self) -> None:\n            raise CalledProcessError(1, \"cmd\")\n\n    mocker.patch(\n        \"poetry.utils.env.python.Python.find_poetry_managed_pythons\",\n        return_value=[BadPython()],\n    )\n\n    installer = PythonInstaller(request=\"3.9.1\")\n    with pytest.raises(PoetryRuntimeError):\n        assert not installer.exists()\n\n\n@pytest.mark.parametrize(\n    (\"implementation\", \"requested\", \"expected\"),\n    [\n        (\"cpython\", \"pypy\", False),\n        (\"pypy\", \"pypy\", True),\n    ],\n)\ndef test_python_installer_exists_implementation(\n    mocker: MockerFixture,\n    implementation: Literal[\"cpython\", \"pypy\"],\n    requested: Literal[\"cpython\", \"pypy\"],\n    expected: bool,\n) -> None:\n    mocker.patch(\n        \"poetry.utils.env.python.Python.find_poetry_managed_pythons\",\n        return_value=[\n            mocker.Mock(\n                implementation=implementation,\n                free_threaded=False,\n                version=Version.from_parts(3, 9, 1),\n            )\n        ],\n    )\n    installer = PythonInstaller(request=\"3.9.1\", implementation=requested)\n    assert installer.exists() is expected\n\n\n@pytest.mark.parametrize(\n    (\"free_threaded\", \"requested\", \"expected\"),\n    [\n        (\"cpython\", \"pypy\", False),\n        (\"pypy\", \"pypy\", True),\n    ],\n)\ndef test_python_installer_exists_free_threaded(\n    mocker: MockerFixture, free_threaded: bool, requested: bool, expected: bool\n) -> None:\n    mocker.patch(\n        \"poetry.utils.env.python.Python.find_poetry_managed_pythons\",\n        return_value=[\n            mocker.Mock(\n                implementation=\"cpython\",\n                free_threaded=free_threaded,\n                version=Version.from_parts(3, 9, 1),\n            )\n        ],\n    )\n    installer = PythonInstaller(request=\"3.9.1\", free_threaded=requested)\n    assert installer.exists() is expected\n\n\ndef test_python_installer_install(mocker: MockerFixture) -> None:\n    mocker.patch(\n        \"pbs_installer.get_download_link\",\n        return_value=(mocker.Mock(major=3, minor=9, micro=1), None),\n    )\n    install = mocker.patch(\"pbs_installer.install\")\n    installer = PythonInstaller(request=\"3.9.1\")\n    installer.install()\n    install.assert_called_once_with(\n        \"3.9.1\",\n        installer.installation_directory,\n        version_dir=True,\n        implementation=\"cpython\",\n        free_threaded=False,\n    )\n\n\ndef test_python_installer_install_error(mocker: MockerFixture) -> None:\n    mocker.patch(\"pbs_installer.install\", side_effect=ValueError)\n    installer = PythonInstaller(request=\"3.9.1\")\n    with pytest.raises(PythonInstallationError):\n        installer.install()\n"
  },
  {
    "path": "tests/utils/env/python/test_python_providers.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom typing import TYPE_CHECKING\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.utils.env.python.providers import PoetryPythonPathProvider\nfrom poetry.utils.env.python.providers import ShutilWhichPythonProvider\n\n\nif TYPE_CHECKING:\n    from tests.types import MockedPoetryPythonRegister\n\n\ndef test_shutil_which_python_provider() -> None:\n    provider = ShutilWhichPythonProvider.create()\n    assert provider\n    pythons = list(provider.find_pythons())\n    assert len(pythons) == 1\n    assert pythons[0].minor == sys.version_info.minor\n\n\ndef test_poetry_python_path_provider_no_pythons() -> None:\n    provider = PoetryPythonPathProvider.create()\n    assert provider\n    assert not provider.paths\n\n\ndef test_poetry_python_path_provider(\n    mocked_poetry_managed_python_register: MockedPoetryPythonRegister,\n) -> None:\n    cpython_path = mocked_poetry_managed_python_register(\"3.9.1\", \"cpython\")\n    pypy_path = mocked_poetry_managed_python_register(\"3.10.8\", \"pypy\")\n    free_threaded_path = mocked_poetry_managed_python_register(\n        \"3.13.2\", \"cpython\", free_threaded=True, with_install_dir=True\n    )\n\n    provider = PoetryPythonPathProvider.create()\n\n    assert provider\n\n    assert set(provider.paths) == {cpython_path, pypy_path, free_threaded_path}\n    assert len(list(provider.find_pythons())) == 4\n\n    assert provider.installation_bin_paths(Version.parse(\"3.9.1\"), \"cpython\") == [\n        cpython_path\n    ]\n    assert provider.installation_bin_paths(Version.parse(\"3.9.2\"), \"cpython\") == []\n    assert provider.installation_bin_paths(Version.parse(\"3.10.8\"), \"pypy\") == [\n        pypy_path\n    ]\n    assert provider.installation_bin_paths(Version.parse(\"3.10.8\"), \"cpython\") == []\n    assert provider.installation_bin_paths(\n        Version.parse(\"3.13.2\"), \"cpython\", free_threaded=True\n    ) == [free_threaded_path]\n"
  },
  {
    "path": "tests/utils/env/test_env.py",
    "content": "from __future__ import annotations\n\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom importlib import metadata\nfrom pathlib import Path\nfrom threading import Thread\nfrom typing import TYPE_CHECKING\n\nimport packaging.tags\nimport pytest\n\nfrom deepdiff.diff import DeepDiff\nfrom installer.utils import SCHEME_NAMES\n\nfrom poetry.factory import Factory\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.utils._compat import WINDOWS\nfrom poetry.utils.env import EnvCommandError\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import GenericEnv\nfrom poetry.utils.env import MockEnv\nfrom poetry.utils.env import SystemEnv\nfrom poetry.utils.env import VirtualEnv\nfrom poetry.utils.env import build_environment\nfrom poetry.utils.env import ephemeral_environment\nfrom poetry.utils.helpers import is_dir_writable\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.types import FixtureDirGetter\n    from tests.types import SetProjectContext\n\nMINIMAL_SCRIPT = \"\"\"\\\n\nprint(\"Minimal Output\"),\n\"\"\"\n\n# Script expected to fail.\nERRORING_SCRIPT = \"\"\"\\\nimport nullpackage\n\nprint(\"nullpackage loaded\"),\n\"\"\"\n\n\nclass MockVirtualEnv(VirtualEnv):\n    def __init__(\n        self,\n        path: Path,\n        base: Path | None = None,\n        sys_path: list[str] | None = None,\n    ) -> None:\n        super().__init__(path, base=base)\n\n        self._sys_path = sys_path\n\n    @property\n    def sys_path(self) -> list[str]:\n        if self._sys_path is not None:\n            return self._sys_path\n\n        return super().sys_path\n\n\ndef test_virtualenvs_with_spaces_in_their_path_work_as_expected(\n    tmp_path: Path, manager: EnvManager\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n\n    manager.build_venv(venv_path)\n\n    venv = VirtualEnv(venv_path)\n\n    assert venv.run(\"python\", \"-V\").startswith(\"Python\")\n\n\ndef test_env_commands_with_spaces_in_their_arg_work_as_expected(\n    tmp_path: Path, manager: EnvManager\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    manager.build_venv(venv_path)\n    venv = VirtualEnv(venv_path)\n    output = venv.run(\"python\", str(venv.pip), \"--version\")\n    assert re.match(r\"pip \\S+ from\", output)\n\n\n@pytest.mark.parametrize(\"differing_platform\", [True, False])\ndef test_env_get_supported_tags_matches_inside_virtualenv(\n    tmp_path: Path, manager: EnvManager, mocker: MockerFixture, differing_platform: bool\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    manager.build_venv(venv_path)\n    venv = VirtualEnv(venv_path)\n\n    run_python_script_spy = mocker.spy(venv, \"run_python_script\")\n\n    # determine expected tags before patching sysconfig!\n    expected_tags = list(packaging.tags.sys_tags())\n\n    if differing_platform:\n        mocker.patch(\"sysconfig.get_platform\", return_value=\"some_other_platform\")\n        expected_call_count = 2\n    else:\n        expected_call_count = 1\n\n    assert venv.get_supported_tags() == expected_tags\n    assert run_python_script_spy.call_count == expected_call_count\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"free threading is only relevant for CPython\",\n)\ndef test_env_get_supported_tags_free_threading(\n    tmp_path: Path, manager: EnvManager\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    manager.build_venv(venv_path)\n    venv = VirtualEnv(venv_path)\n\n    if venv.marker_env[\"free_threading\"]:\n        assert venv.get_supported_tags() == list(packaging.tags.sys_tags())\n    else:\n        assert not any(t.abi.endswith(\"t\") for t in venv.get_supported_tags())\n        venv.marker_env[\"free_threading\"] = True\n        assert any(t.abi.endswith(\"t\") for t in venv.get_supported_tags())\n\n\n@pytest.mark.skipif(os.name == \"nt\", reason=\"Symlinks are not support for Windows\")\ndef test_env_has_symlinks_on_nix(tmp_path: Path, tmp_venv: VirtualEnv) -> None:\n    assert os.path.islink(tmp_venv.python)\n\n\ndef test_run_with_keyboard_interrupt(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\"subprocess.check_output\", side_effect=KeyboardInterrupt())\n    with pytest.raises(KeyboardInterrupt):\n        tmp_venv.run(\"python\", \"-c\", MINIMAL_SCRIPT)\n    subprocess.check_output.assert_called_once()  # type: ignore[attr-defined]\n\n\ndef test_call_with_keyboard_interrupt(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\"subprocess.check_call\", side_effect=KeyboardInterrupt())\n    kwargs = {\"call\": True}\n    with pytest.raises(KeyboardInterrupt):\n        tmp_venv.run(\"python\", \"-\", **kwargs)\n    subprocess.check_call.assert_called_once()  # type: ignore[attr-defined]\n\n\ndef test_run_with_called_process_error(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=subprocess.CalledProcessError(\n            42, \"some_command\", \"some output\", \"some error\"\n        ),\n    )\n    with pytest.raises(EnvCommandError) as error:\n        tmp_venv.run(\"python\", \"-c\", MINIMAL_SCRIPT)\n    subprocess.check_output.assert_called_once()  # type: ignore[attr-defined]\n    assert \"some output\" in str(error.value)\n    assert \"some error\" in str(error.value)\n\n\ndef test_call_no_input_with_called_process_error(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"subprocess.check_call\",\n        side_effect=subprocess.CalledProcessError(\n            42, \"some_command\", \"some output\", \"some error\"\n        ),\n    )\n    kwargs = {\"call\": True}\n    with pytest.raises(EnvCommandError) as error:\n        tmp_venv.run(\"python\", \"-\", **kwargs)\n    subprocess.check_call.assert_called_once()  # type: ignore[attr-defined]\n    assert \"some output\" in str(error.value)\n    assert \"some error\" in str(error.value)\n\n\ndef test_check_output_with_called_process_error(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=subprocess.CalledProcessError(\n            42, \"some_command\", \"some output\", \"some error\"\n        ),\n    )\n    with pytest.raises(EnvCommandError) as error:\n        tmp_venv.run(\"python\", \"-\")\n    subprocess.check_output.assert_called_once()  # type: ignore[attr-defined]\n    assert \"some output\" in str(error.value)\n    assert \"some error\" in str(error.value)\n\n\n@pytest.mark.parametrize(\"out\", [\"sys.stdout\", \"sys.stderr\"])\ndef test_call_does_not_block_on_full_pipe(\n    tmp_path: Path, tmp_venv: VirtualEnv, out: str\n) -> None:\n    \"\"\"see https://github.com/python-poetry/poetry/issues/7698\"\"\"\n    script = tmp_path / \"script.py\"\n    script.write_text(\n        f\"\"\"\\\nimport sys\nfor i in range(10000):\n    print('just print a lot of text to fill the buffer', file={out})\n\"\"\",\n        encoding=\"utf-8\",\n    )\n\n    def target(result: list[int]) -> None:\n        tmp_venv.run(\"python\", str(script), call=True)\n        result.append(0)\n\n    results: list[int] = []\n    # use a separate thread, so that the test does not block in case of error\n    thread = Thread(target=target, args=(results,))\n    thread.start()\n    thread.join(1)  # must not block\n    assert results and results[0] == 0\n\n\ndef test_run_python_script_called_process_error(\n    tmp_path: Path, tmp_venv: VirtualEnv, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"subprocess.run\",\n        side_effect=subprocess.CalledProcessError(\n            42, \"some_command\", \"some output\", \"some error\"\n        ),\n    )\n    with pytest.raises(EnvCommandError) as error:\n        tmp_venv.run_python_script(MINIMAL_SCRIPT)\n    assert \"some output\" in str(error.value)\n    assert \"some error\" in str(error.value)\n\n\ndef test_run_python_script_only_stdout(tmp_path: Path, tmp_venv: VirtualEnv) -> None:\n    output = tmp_venv.run_python_script(\n        \"import sys; print('some warning', file=sys.stderr); print('some output')\"\n    )\n    assert \"some output\" in output\n    assert \"some warning\" not in output\n\n\ndef test_system_env_has_correct_paths() -> None:\n    env = SystemEnv(Path(sys.prefix))\n\n    paths = env.paths\n\n    assert paths.get(\"purelib\") is not None\n    assert paths.get(\"platlib\") is not None\n    assert paths.get(\"scripts\") is not None\n    assert env.site_packages.path == Path(paths[\"purelib\"])\n    assert paths[\"include\"] is not None\n\n\n@pytest.mark.parametrize(\n    \"enabled\",\n    [True, False],\n)\ndef test_system_env_usersite(mocker: MockerFixture, enabled: bool) -> None:\n    mocker.patch(\"site.check_enableusersite\", return_value=enabled)\n    env = SystemEnv(Path(sys.prefix))\n    assert (enabled and env.usersite is not None) or (\n        not enabled and env.usersite is None\n    )\n\n\ndef test_venv_has_correct_paths(tmp_venv: VirtualEnv) -> None:\n    paths = tmp_venv.paths\n\n    assert paths.get(\"purelib\") is not None\n    assert paths.get(\"platlib\") is not None\n    assert paths.get(\"scripts\") is not None\n    assert tmp_venv.site_packages.path == Path(paths[\"purelib\"])\n    assert paths[\"include\"] == str(\n        tmp_venv.path.joinpath(\n            f\"include/site/python{tmp_venv.version_info[0]}.{tmp_venv.version_info[1]}\"\n        )\n    )\n\n\n@pytest.mark.parametrize(\"with_system_site_packages\", [True, False])\ndef test_env_system_packages(\n    tmp_path: Path, poetry: Poetry, with_system_site_packages: bool\n) -> None:\n    venv_path = tmp_path / \"venv\"\n    pyvenv_cfg = venv_path / \"pyvenv.cfg\"\n\n    EnvManager(poetry).build_venv(\n        path=venv_path, flags={\"system-site-packages\": with_system_site_packages}\n    )\n    env = VirtualEnv(venv_path)\n\n    assert (\n        f\"include-system-site-packages = {str(with_system_site_packages).lower()}\"\n        in pyvenv_cfg.read_text(encoding=\"utf-8\")\n    )\n    assert env.includes_system_site_packages is with_system_site_packages\n\n\ndef test_generic_env_system_packages(poetry: Poetry) -> None:\n    \"\"\"https://github.com/python-poetry/poetry/issues/8646\"\"\"\n    env = GenericEnv(Path(sys.base_prefix))\n    assert not env.includes_system_site_packages\n\n\n@pytest.mark.parametrize(\"with_system_site_packages\", [True, False])\ndef test_env_system_packages_are_relative_to_lib(\n    tmp_path: Path, poetry: Poetry, with_system_site_packages: bool\n) -> None:\n    venv_path = tmp_path / \"venv\"\n    EnvManager(poetry).build_venv(\n        path=venv_path, flags={\"system-site-packages\": with_system_site_packages}\n    )\n    env = VirtualEnv(venv_path)\n\n    # These are Poetry's own dependencies.\n    # They should not be relative to the virtualenv's lib directory.\n    for dist in metadata.distributions():\n        assert not env.is_path_relative_to_lib(\n            Path(str(dist._path))  # type: ignore[attr-defined]\n        )\n        # Checking one package is sufficient\n        break\n    else:\n        pytest.fail(\"No distributions found in Poetry's own environment\")\n\n    # These are the virtual environments' base env packages,\n    # in this case the system site packages.\n    for dist in env.parent_env.site_packages.distributions():\n        assert (\n            env.is_path_relative_to_lib(\n                Path(str(dist._path))  # type: ignore[attr-defined]\n            )\n            is with_system_site_packages\n        )\n        # Checking one package is sufficient\n        break\n    else:\n        pytest.fail(\"No distributions found in the base environment of the virtualenv\")\n\n\n@pytest.mark.parametrize(\n    (\"flags\", \"packages\"),\n    [\n        ({\"no-pip\": False}, {\"pip\"}),\n        ({\"no-pip\": True}, set()),\n        ({}, set()),\n    ],\n)\ndef test_env_no_pip(\n    tmp_path: Path, poetry: Poetry, flags: dict[str, str | bool], packages: set[str]\n) -> None:\n    venv_path = tmp_path / \"venv\"\n    EnvManager(poetry).build_venv(path=venv_path, flags=flags)\n    env = VirtualEnv(venv_path)\n    installed_repository = InstalledRepository.load(env=env, with_dependencies=True)\n    installed_packages = {\n        package.name\n        for package in installed_repository.packages\n        # workaround for BSD test environments\n        if package.name != \"sqlite3\"\n    }\n\n    assert installed_packages == packages\n\n\ndef test_env_finds_the_correct_executables(tmp_path: Path, manager: EnvManager) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    manager.build_venv(venv_path, with_pip=True)\n    venv = VirtualEnv(venv_path)\n\n    default_executable = expected_executable = f\"python{'.exe' if WINDOWS else ''}\"\n    default_pip_executable = expected_pip_executable = f\"pip{'.exe' if WINDOWS else ''}\"\n    major_executable = f\"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}\"\n    major_pip_executable = f\"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''}\"\n\n    if (\n        venv._bin_dir.joinpath(default_executable).exists()\n        and venv._bin_dir.joinpath(major_executable).exists()\n    ):\n        venv._bin_dir.joinpath(default_executable).unlink()\n        expected_executable = major_executable\n\n    if (\n        venv._bin_dir.joinpath(default_pip_executable).exists()\n        and venv._bin_dir.joinpath(major_pip_executable).exists()\n    ):\n        venv._bin_dir.joinpath(default_pip_executable).unlink()\n        expected_pip_executable = major_pip_executable\n\n    venv = VirtualEnv(venv_path)\n\n    assert Path(venv.python).name == expected_executable\n    assert Path(venv.pip).name.startswith(expected_pip_executable.split(\".\")[0])\n\n\ndef test_env_finds_the_correct_executables_for_generic_env(\n    tmp_path: Path, manager: EnvManager\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    child_venv_path = tmp_path / \"Child Virtual Env\"\n    manager.build_venv(venv_path, with_pip=True)\n    parent_venv = VirtualEnv(venv_path)\n    manager.build_venv(child_venv_path, executable=parent_venv.python, with_pip=True)\n    venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))\n\n    expected_executable = (\n        f\"python{sys.version_info[0]}.exe\"\n        if WINDOWS\n        else f\"python{sys.version_info[0]}.{sys.version_info[1]}\"\n    )\n    expected_pip_executable = (\n        f\"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}\"\n    )\n\n    assert Path(venv.python).name == expected_executable\n    assert Path(venv.pip).name == expected_pip_executable\n\n\n@pytest.mark.parametrize(\"fallback\", [\"major\", \"default\"])\ndef test_env_finds_fallback_executables_for_generic_env(\n    tmp_path: Path, manager: EnvManager, fallback: str\n) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    child_venv_path = tmp_path / \"Child Virtual Env\"\n    manager.build_venv(venv_path, with_pip=True)\n    parent_venv = VirtualEnv(venv_path)\n    manager.build_venv(child_venv_path, executable=parent_venv.python, with_pip=True)\n    venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))\n\n    default_executable = f\"python{'.exe' if WINDOWS else ''}\"\n    default_pip_executable = f\"pip{'.exe' if WINDOWS else ''}\"\n    major_executable = f\"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}\"\n    major_pip_executable = f\"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''}\"\n    minor_executable = (\n        f\"python{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}\"\n    )\n    minor_pip_executable = (\n        f\"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}\"\n    )\n\n    venv._bin_dir.joinpath(minor_executable).unlink(missing_ok=True)\n    venv._bin_dir.joinpath(minor_pip_executable).unlink(missing_ok=True)\n\n    if fallback == \"default\":\n        venv._bin_dir.joinpath(major_executable).unlink(missing_ok=True)\n        venv._bin_dir.joinpath(major_pip_executable).unlink(missing_ok=True)\n\n        expected_executable = default_executable\n        expected_pip_executable = default_pip_executable\n\n    else:\n        expected_executable = major_executable\n        expected_pip_executable = major_pip_executable\n\n    venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path))\n\n    assert Path(venv.python).name == expected_executable\n    assert Path(venv.pip).name == expected_pip_executable\n\n\n@pytest.fixture\ndef extended_without_setup_poetry(\n    fixture_dir: FixtureDirGetter, set_project_context: SetProjectContext\n) -> Iterator[Poetry]:\n    with set_project_context(\"extended_project_without_setup\") as cwd:\n        yield Factory().create_poetry(cwd)\n\n\ndef test_build_environment_called_build_script_specified(\n    mocker: MockerFixture,\n    extended_without_setup_poetry: Poetry,\n) -> None:\n    patched_install = mocker.patch(\"poetry.utils.isolated_build.IsolatedEnv.install\")\n\n    with ephemeral_environment() as project_env:\n        import poetry.utils.env\n\n        spy = mocker.spy(poetry.utils.env, \"ephemeral_environment\")\n\n        with build_environment(extended_without_setup_poetry, project_env):\n            assert patched_install.call_count == 1\n            assert patched_install.call_args == mocker.call([\"poetry-core\", \"cython\"])\n\n        assert spy.call_count == 1\n\n\ndef test_build_environment_not_called_without_build_script_specified(\n    mocker: MockerFixture, poetry: Poetry, tmp_path: Path\n) -> None:\n    project_env = MockEnv(path=tmp_path / \"project\")\n    ephemeral_env = MockEnv(path=tmp_path / \"ephemeral\")\n\n    mocker.patch(\n        \"poetry.utils.env.ephemeral_environment\"\n    ).return_value.__enter__.return_value = ephemeral_env\n\n    with build_environment(poetry, project_env) as env:\n        assert env == project_env\n        assert not env.executed  # type: ignore[attr-defined]\n\n\ndef test_command_from_bin_preserves_relative_path(manager: EnvManager) -> None:\n    # https://github.com/python-poetry/poetry/issues/7959\n    env = manager.get()\n    command = env.get_command_from_bin(\"./foo.py\")\n    assert command == [\"./foo.py\"]\n\n\n@pytest.fixture\ndef system_env_read_only(system_env: SystemEnv, mocker: MockerFixture) -> SystemEnv:\n    original_is_dir_writable = is_dir_writable\n\n    read_only_paths = {system_env.paths[key] for key in SCHEME_NAMES}\n\n    def mock_is_dir_writable(path: Path, create: bool = False) -> bool:\n        if str(path) in read_only_paths:\n            return False\n        return original_is_dir_writable(path, create)\n\n    mocker.patch(\"poetry.utils.env.base_env.is_dir_writable\", new=mock_is_dir_writable)\n\n    return system_env\n\n\ndef test_env_scheme_dict_returns_original_when_writable(system_env: SystemEnv) -> None:\n    assert not DeepDiff(system_env.scheme_dict, system_env.paths, ignore_order=True)\n\n\ndef test_env_scheme_dict_returns_modified_when_read_only(\n    system_env_read_only: SystemEnv,\n) -> None:\n    scheme_dict = system_env_read_only.scheme_dict\n    assert DeepDiff(scheme_dict, system_env_read_only.paths, ignore_order=True)\n\n    paths = system_env_read_only.paths\n    assert all(\n        Path(scheme_dict[scheme]).exists()\n        and scheme_dict[scheme].startswith(paths[\"userbase\"])\n        for scheme in SCHEME_NAMES\n    )\n\n\ndef test_marker_env_is_equal_for_all_envs(tmp_path: Path, manager: EnvManager) -> None:\n    venv_path = tmp_path / \"Virtual Env\"\n    manager.build_venv(venv_path)\n    venv = VirtualEnv(venv_path)\n    generic_env = GenericEnv(venv.path)\n    system_env = SystemEnv(Path(sys.prefix))\n\n    venv_marker_env = venv.marker_env\n    generic_marker_env = generic_env.marker_env\n    system_marker_env = system_env.marker_env\n\n    assert venv_marker_env == generic_marker_env\n    assert venv_marker_env == system_marker_env\n"
  },
  {
    "path": "tests/utils/env/test_env_manager.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport os\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\nimport tomlkit\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.config.config import Config\nfrom poetry.console.exceptions import PoetryConsoleError\nfrom poetry.toml.file import TOMLFile\nfrom poetry.utils.env import GET_BASE_PREFIX\nfrom poetry.utils.env import GET_PYTHON_VERSION_ONELINER\nfrom poetry.utils.env import EnvManager\nfrom poetry.utils.env import IncorrectEnvError\nfrom poetry.utils.env.env_manager import EnvsFile\nfrom poetry.utils.env.python.exceptions import InvalidCurrentPythonVersionError\nfrom poetry.utils.env.python.exceptions import NoCompatiblePythonVersionFoundError\nfrom poetry.utils.env.python.exceptions import PythonVersionNotFoundError\nfrom poetry.utils.helpers import remove_directory\n\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n    from collections.abc import Iterator\n    from unittest.mock import MagicMock\n\n    from pytest import LogCaptureFixture\n    from pytest_mock import MockerFixture\n\n    from poetry.poetry import Poetry\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n    from tests.types import MockedPythonRegister\n    from tests.types import ProjectFactory\n\nVERSION_3_7_1 = Version.parse(\"3.7.1\")\n\n\ndef build_venv(path: Path | str, **__: Any) -> None:\n    os.mkdir(str(path))\n\n\ndef check_output_wrapper(\n    version: Version = VERSION_3_7_1,\n) -> Callable[[list[str], Any, Any], str]:\n    def check_output(cmd: list[str], *args: Any, **kwargs: Any) -> str:\n        # cmd is a list, like [\"python\", \"-c\", \"do stuff\"]\n        python_cmd = cmd[-1]\n        if \"print(json.dumps(env))\" in python_cmd:\n            return (\n                f'{{\"version_info\": [{version.major}, {version.minor},'\n                f\" {version.patch}]}}\"\n            )\n\n        if \"sys.version_info[:3]\" in python_cmd:\n            return version.text\n\n        if \"sys.version_info[:2]\" in python_cmd:\n            return f\"{version.major}.{version.minor}\"\n\n        if \"import sys; print(sys.executable)\" in python_cmd:\n            executable = cmd[0]\n            basename = os.path.basename(executable)\n            return f\"/usr/bin/{basename}\"\n\n        if \"print(sys.base_prefix)\" in python_cmd:\n            return sys.base_prefix\n\n        assert \"import sys; print(sys.prefix)\" in python_cmd\n        return \"/prefix\"\n\n    return check_output\n\n\n@pytest.fixture\ndef in_project_venv_dir(poetry: Poetry) -> Iterator[Path]:\n    os.environ.pop(\"VIRTUAL_ENV\", None)\n    venv_dir = poetry.file.path.parent.joinpath(\".venv\")\n    venv_dir.mkdir()\n    try:\n        yield venv_dir\n    finally:\n        venv_dir.rmdir()\n\n\n@pytest.mark.parametrize(\n    (\"section\", \"version\", \"expected\"),\n    [\n        (\"foo\", None, \"3.10\"),\n        (\"bar\", None, \"3.11\"),\n        (\"baz\", None, \"3.12\"),\n        (\"bar\", \"3.11\", \"3.11\"),\n        (\"bar\", \"3.10\", None),\n    ],\n)\ndef test_envs_file_remove_section(\n    tmp_path: Path, section: str, version: str | None, expected: str | None\n) -> None:\n    envs_file_path = tmp_path / \"envs.toml\"\n\n    envs_file = TOMLFile(envs_file_path)\n    doc = tomlkit.document()\n    doc[\"foo\"] = {\"minor\": \"3.10\", \"patch\": \"3.10.13\"}\n    doc[\"bar\"] = {\"minor\": \"3.11\", \"patch\": \"3.11.7\"}\n    doc[\"baz\"] = {\"minor\": \"3.12\", \"patch\": \"3.12.1\"}\n    envs_file.write(doc)\n\n    minor = EnvsFile(envs_file_path).remove_section(section, version)\n\n    assert minor == expected\n\n    envs = TOMLFile(envs_file_path).read()\n    if expected is None:\n        assert section in envs\n    else:\n        assert section not in envs\n    for other_section in {\"foo\", \"bar\", \"baz\"} - {section}:\n        assert other_section in envs\n\n\ndef test_activate_in_project_venv_no_explicit_config(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    mocker: MockerFixture,\n    venv_name: str,\n    in_project_venv_dir: Path,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    mocked_python_register(\"3.7.1\")\n    m = mocker.patch(\"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv)\n\n    env = manager.activate(\"python3.7\")\n\n    assert env.path == tmp_path / \"poetry-fixture-simple\" / \".venv\"\n    assert env.base == Path(sys.base_prefix)\n\n    m.assert_called_with(\n        tmp_path / \"poetry-fixture-simple\" / \".venv\",\n        executable=Path(\"/usr/bin/python3.7\"),\n        flags={\n            \"always-copy\": False,\n            \"system-site-packages\": False,\n            \"no-pip\": False,\n        },\n        prompt=\"simple-project-py3.7\",\n    )\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    assert not envs_file.exists()\n\n\ndef test_activate_activates_non_existing_virtualenv_no_envs_file(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.7.1\")\n    m = mocker.patch(\"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv)\n\n    env = manager.activate(\"python3.7\")\n\n    m.assert_called_with(\n        tmp_path / f\"{venv_name}-py3.7\",\n        executable=Path(\"/usr/bin/python3.7\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.7\",\n    )\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.7\"\n    assert envs[venv_name][\"patch\"] == \"3.7.1\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.7\"\n    assert env.base == Path(sys.base_prefix)\n\n\ndef test_activate_fails_when_python_cannot_be_found(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"2.7.1\")\n\n    with pytest.raises(PythonVersionNotFoundError) as e:\n        manager.activate(\"python3.7\")\n\n    expected_message = \"Could not find the python executable python3.7\"\n    assert str(e.value) == expected_message\n\n\ndef test_activate_activates_existing_virtualenv_no_envs_file(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.7.1\")\n    m = mocker.patch(\"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv)\n\n    env = manager.activate(\"python3.7\")\n\n    m.assert_not_called()\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.7\"\n    assert envs[venv_name][\"patch\"] == \"3.7.1\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.7\"\n    assert env.base == Path(sys.base_prefix)\n\n\ndef test_activate_activates_same_virtualenv_with_envs_file(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.1\"}\n    envs_file.write(doc)\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.7.1\")\n    m = mocker.patch(\"poetry.utils.env.EnvManager.create_venv\")\n\n    env = manager.activate(\"python3.7\")\n\n    m.assert_not_called()\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.7\"\n    assert envs[venv_name][\"patch\"] == \"3.7.1\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.7\"\n    assert env.base == Path(sys.base_prefix)\n\n\ndef test_activate_activates_different_virtualenv_with_envs_file(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.1\"}\n    envs_file.write(doc)\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.6.6\")\n    mocked_python_register(\"3.7.1\")\n\n    m = mocker.patch(\"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv)\n\n    env = manager.activate(\"python3.6\")\n\n    m.assert_called_with(\n        tmp_path / f\"{venv_name}-py3.6\",\n        executable=Path(\"/usr/bin/python3.6\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.6\",\n    )\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.6\"\n    assert envs[venv_name][\"patch\"] == \"3.6.6\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.6\"\n    assert env.base == Path(sys.base_prefix)\n\n\ndef test_activate_activates_recreates_for_different_patch(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    envs_file.write(doc)\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.7.1\")\n    build_venv_m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv\n    )\n    remove_venv_m = mocker.patch(\n        \"poetry.utils.env.EnvManager.remove_venv\", side_effect=EnvManager.remove_venv\n    )\n\n    env = manager.activate(\"python3.7\")\n\n    build_venv_m.assert_called_with(\n        tmp_path / f\"{venv_name}-py3.7\",\n        executable=Path(\"/usr/bin/python3.7\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.7\",\n    )\n    remove_venv_m.assert_called_with(tmp_path / f\"{venv_name}-py3.7\")\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.7\"\n    assert envs[venv_name][\"patch\"] == \"3.7.1\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.7\"\n    assert env.base == Path(sys.base_prefix)\n    assert (tmp_path / f\"{venv_name}-py3.7\").exists()\n\n\ndef test_activate_does_not_recreate_when_switching_minor(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    envs_file.write(doc)\n\n    os.mkdir(tmp_path / f\"{venv_name}-py3.7\")\n    os.mkdir(tmp_path / f\"{venv_name}-py3.6\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocked_python_register(\"3.7.1\")\n    mocked_python_register(\"3.6.6\")\n\n    build_venv_m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=build_venv\n    )\n    remove_venv_m = mocker.patch(\n        \"poetry.utils.env.EnvManager.remove_venv\", side_effect=EnvManager.remove_venv\n    )\n\n    env = manager.activate(\"python3.6\")\n\n    build_venv_m.assert_not_called()\n    remove_venv_m.assert_not_called()\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert envs[venv_name][\"minor\"] == \"3.6\"\n    assert envs[venv_name][\"patch\"] == \"3.6.6\"\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.6\"\n    assert env.base == Path(sys.base_prefix)\n    assert (tmp_path / f\"{venv_name}-py3.6\").exists()\n\n\ndef test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    tmp_path: Path,\n    mocker: MockerFixture,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    config.merge(\n        {\n            \"virtualenvs\": {\n                \"path\": str(tmp_path / \"virtualenvs\"),\n                \"in-project\": True,\n            }\n        }\n    )\n\n    mocked_python_register(\"3.7.1\")\n    m = mocker.patch(\"poetry.utils.env.EnvManager.build_venv\")\n\n    manager.activate(\"python3.7\")\n\n    m.assert_called_with(\n        poetry.file.path.parent / \".venv\",\n        executable=Path(\"/usr/bin/python3.7\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.7\",\n    )\n\n    envs_file = TOMLFile(tmp_path / \"virtualenvs\" / \"envs.toml\")\n    assert not envs_file.exists()\n\n\ndef test_deactivate_non_activated_but_existing(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    venv_name: str,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    python = \".\".join(str(c) for c in sys.version_info[:2])\n    (tmp_path / f\"{venv_name}-py{python}\").mkdir()\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    manager.deactivate()\n    env = manager.get()\n\n    assert env.path == tmp_path / f\"{venv_name}-py{python}\"\n\n\ndef test_deactivate_activated(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    version = Version.from_parts(*sys.version_info[:3])\n    other_version = Version.parse(\"3.4\") if version.major == 2 else version.next_minor()\n    (tmp_path / f\"{venv_name}-py{version.major}.{version.minor}\").mkdir()\n    (tmp_path / f\"{venv_name}-py{other_version.major}.{other_version.minor}\").mkdir()\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\n        \"minor\": f\"{other_version.major}.{other_version.minor}\",\n        \"patch\": other_version.text,\n    }\n    envs_file.write(doc)\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(),\n    )\n\n    manager.deactivate()\n    env = manager.get()\n\n    assert env.path == tmp_path / f\"{venv_name}-py{version.major}.{version.minor}\"\n\n    envs = envs_file.read()\n    assert len(envs) == 0\n\n\n@pytest.mark.parametrize(\"in_project\", [True, False, None])\ndef test_get_venv_with_venv_folder_present(\n    manager: EnvManager,\n    poetry: Poetry,\n    in_project_venv_dir: Path,\n    in_project: bool | None,\n) -> None:\n    poetry.config.config[\"virtualenvs\"][\"in-project\"] = in_project\n    venv = manager.get()\n    if in_project is False:\n        assert venv.path != in_project_venv_dir\n    else:\n        assert venv.path == in_project_venv_dir\n\n\ndef test_get_prefers_explicitly_activated_virtualenvs_over_env_var(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    os.environ[\"VIRTUAL_ENV\"] = \"/environment/prefix\"\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    envs_file.write(doc)\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(),\n    )\n\n    env = manager.get()\n\n    assert env.path == tmp_path / f\"{venv_name}-py3.7\"\n    assert env.base == Path(sys.base_prefix)\n\n\ndef test_list(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    venvs = manager.list()\n\n    assert len(venvs) == 2\n    assert venvs[0].path == tmp_path / f\"{venv_name}-py3.6\"\n    assert venvs[1].path == tmp_path / f\"{venv_name}-py3.7\"\n\n\ndef test_remove_by_python_version(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    venv = manager.remove(\"3.6\")\n\n    expected_venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    assert venv.path == expected_venv_path\n    assert not expected_venv_path.exists()\n\n\ndef test_remove_by_name(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    venv = manager.remove(f\"{venv_name}-py3.6\")\n\n    expected_venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    assert venv.path == expected_venv_path\n    assert not expected_venv_path.exists()\n\n\ndef test_remove_by_string_with_python_and_version(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    venv = manager.remove(\"python3.6\")\n\n    expected_venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    assert venv.path == expected_venv_path\n    assert not expected_venv_path.exists()\n\n\ndef test_remove_by_full_path_to_python(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    expected_venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    python_path = expected_venv_path / \"bin\" / \"python\"\n\n    venv = manager.remove(str(python_path))\n\n    assert venv.path == expected_venv_path\n    assert not expected_venv_path.exists()\n\n\ndef test_remove_raises_if_acting_on_different_project_by_full_path(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    different_venv_name = \"different-project\"\n    different_venv_path = tmp_path / f\"{different_venv_name}-py3.6\"\n    different_venv_bin_path = different_venv_path / \"bin\"\n    different_venv_bin_path.mkdir(parents=True)\n\n    python_path = different_venv_bin_path / \"python\"\n    python_path.touch(exist_ok=True)\n\n    # Patch initial call where python env path is extracted\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=lambda *args, **kwargs: str(different_venv_path),\n    )\n\n    with pytest.raises(IncorrectEnvError):\n        manager.remove(str(python_path))\n\n\ndef test_remove_raises_if_acting_on_different_project_by_name(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    different_venv_name = (\n        EnvManager.generate_env_name(\n            \"different-project\",\n            str(poetry.file.path.parent),\n        )\n        + \"-py3.6\"\n    )\n    different_venv_path = tmp_path / different_venv_name\n    different_venv_bin_path = different_venv_path / \"bin\"\n    different_venv_bin_path.mkdir(parents=True)\n\n    python_path = different_venv_bin_path / \"python\"\n    python_path.touch(exist_ok=True)\n\n    with pytest.raises(IncorrectEnvError):\n        manager.remove(different_venv_name)\n\n\ndef test_raises_when_passing_old_env_after_dir_rename(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    venv_name: str,\n) -> None:\n    # Make sure that poetry raises when trying to remove old venv after you've renamed\n    # root directory of the project, which will create another venv with new name.\n    # This is not ideal as you still \"can't\" remove it by name, but it at least doesn't\n    # cause any unwanted side effects\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    previous_venv_name = EnvManager.generate_env_name(\n        poetry.package.name,\n        \"previous_dir_name\",\n    )\n    venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    venv_path.mkdir()\n\n    previous_venv_name = f\"{previous_venv_name}-py3.6\"\n    previous_venv_path = tmp_path / previous_venv_name\n    previous_venv_path.mkdir()\n\n    with pytest.raises(IncorrectEnvError):\n        manager.remove(previous_venv_name)\n\n\ndef test_remove_also_deactivates(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    (tmp_path / f\"{venv_name}-py3.7\").mkdir()\n    (tmp_path / f\"{venv_name}-py3.6\").mkdir()\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    doc[venv_name] = {\"minor\": \"3.6\", \"patch\": \"3.6.6\"}\n    envs_file.write(doc)\n\n    venv = manager.remove(\"python3.6\")\n\n    expected_venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    assert venv.path == expected_venv_path\n    assert not expected_venv_path.exists()\n\n    envs = envs_file.read()\n    assert venv_name not in envs\n\n\ndef test_remove_keeps_dir_if_not_deleteable(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n) -> None:\n    # Ensure we empty rather than delete folder if its is an active mount point.\n    # See https://github.com/python-poetry/poetry/pull/2064\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    venv_path = tmp_path / f\"{venv_name}-py3.6\"\n    venv_path.mkdir()\n\n    folder1_path = venv_path / \"folder1\"\n    folder1_path.mkdir()\n\n    file1_path = folder1_path / \"file1\"\n    file1_path.touch(exist_ok=False)\n\n    file2_path = venv_path / \"file2\"\n    file2_path.touch(exist_ok=False)\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=check_output_wrapper(Version.parse(\"3.6.6\")),\n    )\n\n    def err_on_rm_venv_only(path: Path, *args: Any, **kwargs: Any) -> None:\n        if path.resolve() == venv_path.resolve():\n            raise OSError(16, \"Test error\")  # ERRNO 16: Device or resource busy\n        else:\n            remove_directory(path)\n\n    m = mocker.patch(\n        \"poetry.utils.env.env_manager.remove_directory\", side_effect=err_on_rm_venv_only\n    )\n\n    venv = manager.remove(f\"{venv_name}-py3.6\")\n\n    m.assert_any_call(venv_path)\n\n    assert venv_path == venv.path\n    assert venv_path.exists()\n\n    assert not folder1_path.exists()\n    assert not file1_path.exists()\n    assert not file2_path.exists()\n\n    m.side_effect = remove_directory  # Avoid teardown using `err_on_rm_venv_only`\n\n\ndef test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ones_first(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"^3.6\"\n\n    mocked_python_register(\"2.7.16\", make_system=True)\n    mocked_python_register(\"3.7.16\", \"python3\")\n\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py3.7\",\n        executable=Path(\"/usr/bin/python3\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.7\",\n    )\n\n\ndef test_create_venv_finds_no_python_executable(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"^999\"\n\n    with pytest.raises(NoCompatiblePythonVersionFoundError) as e:\n        manager.create_venv()\n\n    expected_message = (\n        \"Poetry was unable to find a compatible version. \"\n        \"If you have one, you can explicitly use it \"\n        'via the \"env use\" command.'\n    )\n\n    assert str(e.value) == expected_message\n\n\ndef test_create_venv_tries_to_find_a_compatible_python_executable_using_specific_ones(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"^3.6\"\n\n    mocked_python_register(\"3.5.3\")\n    mocked_python_register(\"3.9.0\")\n\n    mocker.patch(\n        \"poetry.utils.env.python.Python.get_system_python\",\n        return_value=mocked_python_register(\"2.7.16\", make_system=True),\n    )\n    mocked_python_register(\"3.5.3\")\n    mocked_python_register(\"3.9.0\")\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py3.9\",\n        executable=Path(\"/usr/bin/python3.9\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.9\",\n    )\n\n\ndef test_create_venv_fails_if_no_compatible_python_version_could_be_found(\n    manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"^4.8\"\n\n    mocker.patch(\n        \"subprocess.check_output\",\n        side_effect=[sys.base_prefix, \"/usr/bin/python\", \"3.9.0\"],\n    )\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    with pytest.raises(NoCompatiblePythonVersionFoundError) as e:\n        manager.create_venv()\n\n    expected_message = (\n        \"Poetry was unable to find a compatible version. \"\n        \"If you have one, you can explicitly use it \"\n        'via the \"env use\" command.'\n    )\n\n    assert str(e.value) == expected_message\n    assert m.call_count == 0\n\n\n@pytest.mark.parametrize(\"use_poetry_python\", [True, False])\ndef test_create_venv_does_not_try_to_find_compatible_versions_with_executable(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    mocked_python_register: MockedPythonRegister,\n    use_poetry_python: bool,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = use_poetry_python\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"^4.8\"\n\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    with pytest.raises(NoCompatiblePythonVersionFoundError) as e:\n        manager.create_venv(python=mocked_python_register(\"3.8.0\"))\n\n    expected_message = (\n        \"The specified Python version (3.8.0) is not supported by the project (^4.8).\\n\"\n        \"Please choose a compatible version or loosen the python constraint \"\n        \"specified in the pyproject.toml file.\"\n    )\n\n    assert str(e.value) == expected_message\n    assert m.call_count == 0\n\n\ndef test_create_venv_uses_patch_version_to_detect_compatibility(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    version = Version.from_parts(*sys.version_info[:3])\n    poetry.package.python_versions = \"^\" + \".\".join(\n        str(c) for c in sys.version_info[:3]\n    )\n\n    assert version.patch is not None\n    python = mocked_python_register(\n        f\"{version.major}.{version.minor}.{version.patch + 1}\"\n    )\n    mocker.patch(\n        \"poetry.utils.env.python.Python.get_system_python\",\n        return_value=python,\n    )\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py{version.major}.{version.minor}\",\n        executable=python.executable,\n        flags=venv_flags_default,\n        prompt=f\"simple-project-py{version.major}.{version.minor}\",\n    )\n\n\ndef test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n    venv_flags_default: dict[str, bool],\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    version = Version.from_parts(*sys.version_info[:3])\n    assert version.minor is not None\n    poetry.package.python_versions = \"~3.6.0\"\n    venv_name = manager.generate_env_name(\n        \"simple-project\", str(poetry.file.path.parent)\n    )\n\n    mocked_python_register(\"3.6.0\")\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv(python=mocked_python_register(\"3.6.0\"))\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py3.6\",\n        executable=Path(\"/usr/bin/python3.6\"),\n        flags=venv_flags_default,\n        prompt=\"simple-project-py3.6\",\n    )\n\n\ndef test_create_venv_fails_if_current_python_version_is_not_supported(\n    manager: EnvManager, poetry: Poetry\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    manager.create_venv()\n\n    current_version = Version.parse(\".\".join(str(c) for c in sys.version_info[:3]))\n    assert current_version.minor is not None\n    next_version = \".\".join(\n        str(c) for c in (current_version.major, current_version.minor + 1, 0)\n    )\n    package_version = \"~\" + next_version\n    poetry.package.python_versions = package_version\n\n    with pytest.raises(InvalidCurrentPythonVersionError) as e:\n        manager.create_venv()\n\n    expected_message = (\n        f\"Current Python version ({current_version}) is not allowed by the project\"\n        f' ({package_version}).\\nPlease change python executable via the \"env use\"'\n        \" command.\"\n    )\n\n    assert expected_message == str(e.value)\n\n\ndef test_create_venv_project_name_empty_sets_correct_prompt(\n    fixture_dir: FixtureDirGetter,\n    project_factory: ProjectFactory,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = True\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry = project_factory(\"no\", source=fixture_dir(\"no_name_project\"))\n    manager = EnvManager(poetry)\n\n    poetry.package.python_versions = \"^3.7\"\n    venv_name = manager.generate_env_name(\n        \"non-package-mode\", str(poetry.file.path.parent)\n    )\n\n    mocked_python_register(\"2.7.16\", make_system=True)\n    mocked_python_register(\"3.7.1\", \"python3\")\n\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py3.7\",\n        executable=Path(\"/usr/bin/python3\"),\n        flags={\n            \"always-copy\": False,\n            \"system-site-packages\": False,\n            \"no-pip\": False,\n        },\n        prompt=\"non-package-mode-py3.7\",\n    )\n\n\ndef test_create_venv_accepts_fallback_version_w_nonzero_patchlevel(\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    config_virtualenvs_path: Path,\n    venv_name: str,\n    mocked_python_register: MockedPythonRegister,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    poetry.package.python_versions = \"~3.5.1\"\n\n    def mock_check_output(cmd: str, *args: Any, **kwargs: Any) -> str:\n        if GET_PYTHON_VERSION_ONELINER in cmd:\n            executable = cmd[0]\n            if \"python3.5\" in str(executable):\n                return \"3.5.12\"\n            return \"3.7.1\"\n\n        if GET_BASE_PREFIX in cmd:\n            return sys.base_prefix\n\n        return \"/usr/bin/python3.5\"\n\n    mocked_python_register(\"3.5.12\")\n\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called_with(\n        config_virtualenvs_path / f\"{venv_name}-py3.5\",\n        executable=Path(\"/usr/bin/python3.5\"),\n        flags={\n            \"always-copy\": False,\n            \"system-site-packages\": False,\n            \"no-pip\": False,\n        },\n        prompt=\"simple-project-py3.5\",\n    )\n\n\n@pytest.mark.parametrize(\"is_inconsistent_entry\", [False, True])\ndef test_create_venv_does_not_keep_inconsistent_envs_entry(\n    tmp_path: Path,\n    manager: EnvManager,\n    poetry: Poetry,\n    config: Config,\n    mocker: MockerFixture,\n    venv_name: str,\n    is_inconsistent_entry: bool,\n    mocked_python_register: MockedPythonRegister,\n    with_no_active_python: MagicMock,\n) -> None:\n    if \"VIRTUAL_ENV\" in os.environ:\n        del os.environ[\"VIRTUAL_ENV\"]\n\n    # There is an entry in the envs.toml file but the venv does not exist\n    envs_file = TOMLFile(tmp_path / \"envs.toml\")\n    doc = tomlkit.document()\n    if is_inconsistent_entry:\n        doc[venv_name] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    doc[\"other\"] = {\"minor\": \"3.7\", \"patch\": \"3.7.0\"}\n    envs_file.write(doc)\n\n    mocked_python_register(\"3.7.0\")\n\n    config.merge({\"virtualenvs\": {\"path\": str(tmp_path)}})\n\n    m = mocker.patch(\n        \"poetry.utils.env.EnvManager.build_venv\", side_effect=lambda *args, **kwargs: \"\"\n    )\n\n    manager.create_venv()\n\n    m.assert_called()\n\n    assert envs_file.exists()\n    envs: dict[str, Any] = envs_file.read()\n    assert venv_name not in envs\n    assert envs[\"other\"][\"minor\"] == \"3.7\"\n    assert envs[\"other\"][\"patch\"] == \"3.7.0\"\n\n\ndef test_build_venv_does_not_change_loglevel(\n    tmp_path: Path, manager: EnvManager, caplog: LogCaptureFixture\n) -> None:\n    # see https://github.com/python-poetry/poetry/pull/8760\n    venv_path = tmp_path / \"venv\"\n    caplog.set_level(logging.DEBUG)\n    manager.build_venv(venv_path)\n    assert logging.root.level == logging.DEBUG\n\n\n@pytest.mark.skipif(sys.platform != \"darwin\", reason=\"requires darwin\")\ndef test_venv_backup_exclusion(tmp_path: Path, manager: EnvManager) -> None:\n    import xattr\n\n    venv_path = tmp_path / \"Virtual Env\"\n\n    manager.build_venv(venv_path)\n\n    value = (\n        b\"bplist00_\\x10\\x11com.apple.backupd\"\n        b\"\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n        b\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1c\"\n    )\n    assert (\n        xattr.getxattr(\n            str(venv_path), \"com.apple.metadata:com_apple_backup_excludeItem\"\n        )\n        == value\n    )\n\n\ndef test_generate_env_name_ignores_case_for_case_insensitive_fs(\n    poetry: Poetry,\n    tmp_path: Path,\n) -> None:\n    venv_name1 = EnvManager.generate_env_name(poetry.package.name, \"MyDiR\")\n    venv_name2 = EnvManager.generate_env_name(poetry.package.name, \"mYdIr\")\n    if sys.platform == \"win32\":\n        assert venv_name1 == venv_name2\n    else:\n        assert venv_name1 != venv_name2\n\n\ndef test_generate_env_name_uses_real_path(\n    tmp_path: Path, mocker: MockerFixture\n) -> None:\n    mocker.patch(\"os.path.realpath\", return_value=\"the_real_dir\")\n    venv_name1 = EnvManager.generate_env_name(\"simple-project\", \"the_real_dir\")\n    venv_name2 = EnvManager.generate_env_name(\"simple-project\", \"linked_dir\")\n    assert venv_name1 == venv_name2\n\n\ndef test_create_venv_invalid_prompt_template_variable(\n    manager: EnvManager, poetry: Poetry, config: Config\n) -> None:\n    config.merge({\"virtualenvs\": {\"prompt\": \"{project_name}-{invalid_var}\"}})\n\n    with pytest.raises(PoetryConsoleError) as exc_info:\n        manager.create_venv()\n\n    assert \"Invalid template variable 'invalid_var'\" in str(exc_info.value)\n    assert \"Valid variables are: {project_name}, {python_version}\" in str(\n        exc_info.value\n    )\n\n\ndef test_create_venv_malformed_prompt_template(\n    manager: EnvManager, poetry: Poetry, config: Config\n) -> None:\n    config.merge({\"virtualenvs\": {\"prompt\": \"{project_name\"}})  # Missing closing brace\n\n    with pytest.raises(PoetryConsoleError) as exc_info:\n        manager.create_venv()\n\n    assert \"Invalid template string in 'virtualenvs.prompt' setting\" in str(\n        exc_info.value\n    )\n"
  },
  {
    "path": "tests/utils/env/test_env_site_packages.py",
    "content": "from __future__ import annotations\n\nimport uuid\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom poetry.utils.env import SitePackages\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n\ndef test_env_site_simple(tmp_path: Path, mocker: MockerFixture) -> None:\n    # emulate permission error when creating directory\n    mocker.patch(\"pathlib.Path.mkdir\", side_effect=OSError())\n    site_packages = SitePackages(Path(\"/non-existent\"), fallbacks=[tmp_path])\n    candidates = site_packages.make_candidates(Path(\"hello.txt\"), writable_only=True)\n    hello = tmp_path / \"hello.txt\"\n\n    assert len(candidates) == 1\n    assert candidates[0].as_posix() == hello.as_posix()\n\n    content = str(uuid.uuid4())\n    site_packages.write_text(Path(\"hello.txt\"), content, encoding=\"utf-8\")\n\n    assert hello.read_text(encoding=\"utf-8\") == content\n\n    assert not (site_packages.path / \"hello.txt\").exists()\n\n\ndef test_env_site_select_first(tmp_path: Path) -> None:\n    fallback = tmp_path / \"fallback\"\n    fallback.mkdir(parents=True)\n\n    site_packages = SitePackages(tmp_path, fallbacks=[fallback])\n    candidates = site_packages.make_candidates(Path(\"hello.txt\"), writable_only=True)\n\n    assert len(candidates) == 2\n    assert len(site_packages.find(Path(\"hello.txt\"))) == 0\n\n    content = str(uuid.uuid4())\n    site_packages.write_text(Path(\"hello.txt\"), content, encoding=\"utf-8\")\n\n    assert (site_packages.path / \"hello.txt\").exists()\n    assert not (fallback / \"hello.txt\").exists()\n\n    assert len(site_packages.find(Path(\"hello.txt\"))) == 1\n"
  },
  {
    "path": "tests/utils/env/test_system_env.py",
    "content": "from __future__ import annotations\n\nimport sys\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom poetry.utils.env import SystemEnv\n\n\nif TYPE_CHECKING:\n    from pytest_mock import MockerFixture\n\n\ndef test_get_marker_env_untagged_cpython(mocker: MockerFixture) -> None:\n    mocker.patch(\"platform.python_version\", return_value=\"3.11.9+\")\n    env = SystemEnv(Path(sys.prefix))\n    marker_env = env.get_marker_env()\n    assert marker_env[\"python_full_version\"] == \"3.11.9\"\n"
  },
  {
    "path": "tests/utils/fixtures/pyproject.toml",
    "content": "[tool.poetry]\nname = \"poetry\"\nversion = \"0.2.0\"\ndescription = \"Python dependency management and packaging made easy.\"\nauthors = [\n    \"Sébastien Eustace <sebastien@eustace.io>\"\n]\nlicense = \"MIT\"\n\nreadme = \"README.md\"\n\nhomepage = \"https://python-poetry.org/\"\nrepository = \"https://github.com/python-poetry/poetry\"\ndocumentation = \"https://python-poetry.org/docs\"\n\nkeywords = [\"packaging\", \"dependency\", \"poetry\"]\n\n# Requirements\n[tool.poetry.dependencies]\npython = \"^3.6\"\ncleo = \"^0.6\"\nrequests = \"^2.18\"\ntoml = \"^0.9\"\ncachy = \"^0.1.0\"\npip-tools = \"^1.11\"\n\n[tool.poetry.group.dev.dependencies]\npytest = \"~3.4\"\n"
  },
  {
    "path": "tests/utils/test_authenticator.py",
    "content": "from __future__ import annotations\n\nimport base64\nimport logging\nimport re\nimport uuid\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import NoReturn\n\nimport pytest\nimport requests\nimport responses\n\nfrom cleo.io.null_io import NullIO\nfrom keyring.credentials import SimpleCredential\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.utils.authenticator import Authenticator\nfrom poetry.utils.authenticator import RepositoryCertificateConfig\nfrom poetry.utils.password_manager import PoetryKeyring\n\n\nif TYPE_CHECKING:\n    from pytest import LogCaptureFixture\n    from pytest import MonkeyPatch\n    from pytest_mock import MockerFixture\n\n    from tests.conftest import Config\n    from tests.conftest import DummyBackend\n    from tests.types import HttpResponse\n\n\n@pytest.fixture()\ndef mock_remote(http: responses.RequestsMock) -> None:\n    http.get(\n        re.compile(r\"^https?://(?:[^@]+@)?foo\\.bar/(.+?)$\"),\n        body=\"\",\n    )\n\n\n@pytest.fixture()\ndef repo() -> dict[str, dict[str, str]]:\n    return {\"foo\": {\"url\": \"https://foo.bar/simple/\"}}\n\n\n@pytest.fixture\ndef mock_config(config: Config, repo: dict[str, dict[str, str]]) -> Config:\n    config.merge(\n        {\n            \"repositories\": repo,\n            \"http-basic\": {\"foo\": {\"username\": \"bar\", \"password\": \"baz\"}},\n        }\n    )\n\n    return config\n\n\ndef test_authenticator_uses_url_provided_credentials(\n    mock_config: Config, mock_remote: None, http: responses.RequestsMock\n) -> None:\n    authenticator = Authenticator(mock_config, NullIO())\n    authenticator.request(\"get\", \"https://foo001:bar002@foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"foo001:bar002\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_uses_credentials_from_config_if_not_provided(\n    mock_config: Config, mock_remote: None, http: responses.RequestsMock\n) -> None:\n    authenticator = Authenticator(mock_config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"bar:baz\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_uses_username_only_credentials(\n    mock_config: Config,\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n) -> None:\n    authenticator = Authenticator(mock_config, NullIO())\n    authenticator.request(\"get\", \"https://foo001@foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"foo001:\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_ignores_locked_keyring(\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_locked_keyring: None,\n    caplog: LogCaptureFixture,\n    mocker: MockerFixture,\n) -> None:\n    caplog.set_level(logging.DEBUG, logger=\"poetry.utils.password_manager\")\n    spy_get_credential = mocker.spy(PoetryKeyring, \"get_credential\")\n    spy_get_password = mocker.spy(PoetryKeyring, \"get_password\")\n    authenticator = Authenticator()\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    assert \"Authorization\" not in request.headers\n    assert \"Accessing keyring failed during availability check\" in caplog.messages\n    assert \"Using keyring backend 'conftest LockedBackend'\" in caplog.messages\n    assert spy_get_credential.call_count == spy_get_password.call_count == 0\n\n\ndef test_authenticator_ignores_failing_keyring(\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_erroneous_keyring: None,\n    caplog: LogCaptureFixture,\n    mocker: MockerFixture,\n) -> None:\n    caplog.set_level(logging.DEBUG, logger=\"poetry.utils.password_manager\")\n    spy_get_credential = mocker.spy(PoetryKeyring, \"get_credential\")\n    spy_get_password = mocker.spy(PoetryKeyring, \"get_password\")\n    authenticator = Authenticator()\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    assert \"Authorization\" not in request.headers\n\n    assert \"Using keyring backend 'conftest ErroneousBackend'\" in caplog.messages\n    assert \"Accessing keyring failed during availability check\" in caplog.messages\n    assert spy_get_credential.call_count == spy_get_password.call_count == 0\n\n\ndef test_authenticator_uses_password_only_credentials(\n    mock_config: Config, mock_remote: None, http: responses.RequestsMock\n) -> None:\n    authenticator = Authenticator(mock_config, NullIO())\n    authenticator.request(\"get\", \"https://:bar002@foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\":bar002\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_uses_empty_strings_as_default_password(\n    config: Config,\n    mock_remote: None,\n    repo: dict[str, dict[str, str]],\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": repo,\n            \"http-basic\": {\"foo\": {\"username\": \"bar\"}},\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"bar:\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_does_not_ignore_empty_strings_as_default_username(\n    config: Config,\n    mock_remote: None,\n    repo: dict[str, dict[str, str]],\n    http: responses.RequestsMock,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": repo,\n            \"http-basic\": {\"foo\": {\"username\": None, \"password\": \"bar\"}},\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\":bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_falls_back_to_keyring_url(\n    config: Config,\n    mock_remote: None,\n    repo: dict[str, dict[str, str]],\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": repo,\n        }\n    )\n\n    dummy_keyring.set_default_service_credential(\n        \"https://foo.bar/simple/\",\n        SimpleCredential(\"foo\", \"bar\"),\n    )\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_falls_back_to_keyring_netloc(\n    config: Config,\n    mock_remote: None,\n    repo: dict[str, dict[str, str]],\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n    poetry_keyring: PoetryKeyring,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": repo,\n        }\n    )\n\n    dummy_keyring.set_default_service_credential(\n        \"foo.bar\",\n        SimpleCredential(\"foo\", \"bar\"),\n    )\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\n@pytest.mark.filterwarnings(\"ignore::pytest.PytestUnhandledThreadExceptionWarning\")\ndef test_authenticator_request_retries_on_exception(\n    mocker: MockerFixture, config: Config, http: responses.RequestsMock\n) -> None:\n    sleep = mocker.patch(\"time.sleep\")\n    sdist_uri = f\"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz\"\n    content = str(uuid.uuid4())\n    seen: list[str] = []\n\n    def callback(request: requests.PreparedRequest) -> HttpResponse:\n        assert request.url\n        if seen.count(request.url) < 2:\n            seen.append(request.url)\n            raise requests.exceptions.ConnectionError(\"Disconnected\")\n        return 200, {}, content\n\n    http.add_callback(responses.GET, sdist_uri, callback=callback)\n\n    authenticator = Authenticator(config, NullIO())\n    response = authenticator.request(\"get\", sdist_uri)\n    assert response.text == content\n    assert sleep.call_count == 2\n\n\n@pytest.mark.filterwarnings(\"ignore::pytest.PytestUnhandledThreadExceptionWarning\")\ndef test_authenticator_request_raises_exception_when_attempts_exhausted(\n    mocker: MockerFixture, config: Config, http: responses.RequestsMock\n) -> None:\n    sleep = mocker.patch(\"time.sleep\")\n    sdist_uri = f\"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz\"\n\n    def callback(request: requests.PreparedRequest) -> NoReturn:\n        raise requests.exceptions.ConnectionError(str(uuid.uuid4()))\n\n    http.add_callback(responses.GET, sdist_uri, callback=callback)\n    authenticator = Authenticator(config, NullIO())\n\n    with pytest.raises(PoetryRuntimeError) as e:\n        authenticator.request(\"get\", sdist_uri)\n\n    assert str(e.value) == \"All attempts to connect to foo.bar failed.\"\n    assert sleep.call_count == 5\n\n\ndef test_authenticator_request_respects_retry_header(\n    mocker: MockerFixture,\n    config: Config,\n    http: responses.RequestsMock,\n) -> None:\n    sleep = mocker.patch(\"time.sleep\")\n    sdist_uri = f\"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz\"\n    content = str(uuid.uuid4())\n    seen: list[str] = []\n\n    def callback(request: requests.PreparedRequest) -> HttpResponse:\n        assert request.url\n        if not seen.count(request.url):\n            seen.append(request.url)\n            return 429, {\"Retry-After\": \"42\"}, \"Retry later\"\n\n        return 200, {}, content\n\n    http.add_callback(responses.GET, sdist_uri, callback=callback)\n    authenticator = Authenticator(config, NullIO())\n\n    response = authenticator.request(\"get\", sdist_uri)\n    assert sleep.call_args[0] == (42.0,)\n    assert response.text == content\n\n\n@pytest.mark.parametrize(\n    [\"status\", \"attempts\"],\n    [\n        (400, 0),\n        (401, 0),\n        (403, 0),\n        (404, 0),\n        (429, 5),\n        (500, 5),\n        (501, 5),\n        (502, 5),\n        (503, 5),\n        (504, 5),\n    ],\n)\ndef test_authenticator_request_retries_on_status_code(\n    mocker: MockerFixture,\n    config: Config,\n    http: responses.RequestsMock,\n    status: int,\n    attempts: int,\n) -> None:\n    sleep = mocker.patch(\"time.sleep\")\n    sdist_uri = f\"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz\"\n    content = str(uuid.uuid4())\n\n    def callback(request: requests.PreparedRequest) -> HttpResponse:\n        return status, {}, content\n\n    http.add_callback(responses.GET, sdist_uri, callback=callback)\n    authenticator = Authenticator(config, NullIO())\n\n    with pytest.raises(requests.exceptions.HTTPError) as excinfo:\n        authenticator.request(\"get\", sdist_uri)\n\n    assert excinfo.value.response is not None\n    assert excinfo.value.response.status_code == status\n    assert excinfo.value.response.text == content\n\n    assert sleep.call_count == attempts\n\n\ndef test_authenticator_uses_env_provided_credentials(\n    config: Config,\n    repo: dict[str, dict[str, str]],\n    environ: None,\n    mock_remote: responses.RequestsMock,\n    http: responses.RequestsMock,\n    monkeypatch: MonkeyPatch,\n) -> None:\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_USERNAME\", \"bar\")\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_PASSWORD\", \"baz\")\n\n    config.merge({\"repositories\": repo})\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/files/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"bar:baz\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\n@pytest.mark.parametrize(\n    \"cert,client_cert\",\n    [\n        (None, None),\n        (None, \"path/to/provided/client-cert\"),\n        (\"/path/to/provided/cert\", None),\n        (\"/path/to/provided/cert\", \"path/to/provided/client-cert\"),\n    ],\n)\ndef test_authenticator_uses_certs_from_config_if_not_provided(\n    config: Config,\n    mock_remote: responses.RequestsMock,\n    mock_config: Config,\n    http: responses.RequestsMock,\n    mocker: MockerFixture,\n    cert: str | None,\n    client_cert: str | None,\n) -> None:\n    configured_cert = \"/path/to/cert\"\n    configured_client_cert = \"/path/to/client-cert\"\n\n    mock_config.merge(\n        {\n            \"certificates\": {\n                \"foo\": {\"cert\": configured_cert, \"client-cert\": configured_client_cert}\n            },\n        }\n    )\n\n    authenticator = Authenticator(mock_config, NullIO())\n    url = \"https://foo.bar/files/foo-0.1.0.tar.gz\"\n    session = authenticator.get_session(url)\n    session_send = mocker.patch.object(session, \"send\")\n    authenticator.request(\n        \"get\",\n        url,\n        verify=cert,\n        cert=client_cert,\n    )\n    kwargs = session_send.call_args[1]\n\n    assert Path(kwargs[\"verify\"]) == Path(cert or configured_cert)\n    assert Path(kwargs[\"cert\"]) == Path(client_cert or configured_client_cert)\n\n\ndef test_authenticator_uses_credentials_from_config_matched_by_url_path(\n    config: Config, mock_remote: None, http: responses.RequestsMock\n) -> None:\n    config.merge(\n        {\n            \"repositories\": {\n                \"foo-alpha\": {\"url\": \"https://foo.bar/alpha/files/simple/\"},\n                \"foo-beta\": {\"url\": \"https://foo.bar/beta/files/simple/\"},\n            },\n            \"http-basic\": {\n                \"foo-alpha\": {\"username\": \"bar\", \"password\": \"alpha\"},\n                \"foo-beta\": {\"username\": \"baz\", \"password\": \"beta\"},\n            },\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/alpha/files/simple/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"bar:alpha\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n    # Make request on second repository with the same netloc but different credentials\n    authenticator.request(\"get\", \"https://foo.bar/beta/files/simple/foo-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"baz:beta\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_uses_credentials_from_config_with_at_sign_in_path(\n    config: Config, mock_remote: None, http: responses.RequestsMock\n) -> None:\n    config.merge(\n        {\n            \"repositories\": {\n                \"foo\": {\"url\": \"https://foo.bar/beta/files/simple/\"},\n            },\n            \"http-basic\": {\n                \"foo\": {\"username\": \"bar\", \"password\": \"baz\"},\n            },\n        }\n    )\n    authenticator = Authenticator(config, NullIO())\n    authenticator.request(\"get\", \"https://foo.bar/beta/files/simple/f@@-0.1.0.tar.gz\")\n\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"bar:baz\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_falls_back_to_keyring_url_matched_by_path(\n    config: Config,\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": {\n                \"foo-alpha\": {\"url\": \"https://foo.bar/alpha/files/simple/\"},\n                \"foo-beta\": {\"url\": \"https://foo.bar/beta/files/simple/\"},\n            }\n        }\n    )\n\n    dummy_keyring.set_default_service_credential(\n        \"https://foo.bar/alpha/files/simple/\",\n        SimpleCredential(\"foo\", \"bar\"),\n    )\n    dummy_keyring.set_default_service_credential(\n        \"https://foo.bar/beta/files/simple/\",\n        SimpleCredential(\"foo\", \"baz\"),\n    )\n\n    authenticator = Authenticator(config, NullIO())\n\n    authenticator.request(\"get\", \"https://foo.bar/alpha/files/simple/foo-0.1.0.tar.gz\")\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n    authenticator.request(\"get\", \"https://foo.bar/beta/files/simple/foo-0.1.0.tar.gz\")\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"foo:baz\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_uses_env_provided_credentials_matched_by_url_path(\n    config: Config,\n    environ: None,\n    mock_remote: responses.RequestsMock,\n    http: responses.RequestsMock,\n    monkeypatch: MonkeyPatch,\n) -> None:\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_ALPHA_USERNAME\", \"bar\")\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_ALPHA_PASSWORD\", \"alpha\")\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_BETA_USERNAME\", \"baz\")\n    monkeypatch.setenv(\"POETRY_HTTP_BASIC_FOO_BETA_PASSWORD\", \"beta\")\n\n    config.merge(\n        {\n            \"repositories\": {\n                \"foo-alpha\": {\"url\": \"https://foo.bar/alpha/files/simple/\"},\n                \"foo-beta\": {\"url\": \"https://foo.bar/beta/files/simple/\"},\n            }\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n\n    authenticator.request(\"get\", \"https://foo.bar/alpha/files/simple/foo-0.1.0.tar.gz\")\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"bar:alpha\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n    authenticator.request(\"get\", \"https://foo.bar/beta/files/simple/foo-0.1.0.tar.gz\")\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"baz:beta\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_azure_feed_guid_credentials(\n    config: Config,\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": {\n                \"alpha\": {\n                    \"url\": \"https://foo.bar/org-alpha/_packaging/feed/pypi/simple/\"\n                },\n                \"beta\": {\n                    \"url\": \"https://foo.bar/org-beta/_packaging/feed/pypi/simple/\"\n                },\n            },\n            \"http-basic\": {\n                \"alpha\": {\"username\": \"foo\", \"password\": \"bar\"},\n                \"beta\": {\"username\": \"baz\", \"password\": \"qux\"},\n            },\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n\n    authenticator.request(\n        \"get\",\n        \"https://foo.bar/org-alpha/_packaging/GUID/pypi/simple/a/1.0.0/a-1.0.0.whl\",\n    )\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n    authenticator.request(\n        \"get\",\n        \"https://foo.bar/org-beta/_packaging/GUID/pypi/simple/b/1.0.0/a-1.0.0.whl\",\n    )\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"baz:qux\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_add_repository(\n    config: Config,\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    config.merge(\n        {\n            \"http-basic\": {\n                \"source\": {\"username\": \"foo\", \"password\": \"bar\"},\n            },\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n\n    authenticator.request(\n        \"get\",\n        \"https://foo.bar/simple/a/1.0.0/a-1.0.0.whl\",\n    )\n    request = http.calls[-1].request\n    assert \"Authorization\" not in request.headers\n\n    authenticator.add_repository(\"source\", \"https://foo.bar/simple/\")\n\n    authenticator.request(\n        \"get\",\n        \"https://foo.bar/simple/a/1.0.0/a-1.0.0.whl\",\n    )\n    request = http.calls[-1].request\n\n    basic_auth = base64.b64encode(b\"foo:bar\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_authenticator_git_repositories(\n    config: Config,\n    mock_remote: None,\n    http: responses.RequestsMock,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    config.merge(\n        {\n            \"repositories\": {\n                \"one\": {\"url\": \"https://foo.bar/org/one.git\"},\n                \"two\": {\"url\": \"https://foo.bar/org/two.git\"},\n            },\n            \"http-basic\": {\n                \"one\": {\"username\": \"foo\", \"password\": \"bar\"},\n                \"two\": {\"username\": \"baz\", \"password\": \"qux\"},\n            },\n        }\n    )\n\n    authenticator = Authenticator(config, NullIO())\n\n    one = authenticator.get_credentials_for_git_url(\"https://foo.bar/org/one.git\")\n    assert one.username == \"foo\"\n    assert one.password == \"bar\"\n\n    two = authenticator.get_credentials_for_git_url(\"https://foo.bar/org/two.git\")\n    assert two.username == \"baz\"\n    assert two.password == \"qux\"\n\n    two_ssh = authenticator.get_credentials_for_git_url(\"ssh://git@foo.bar/org/two.git\")\n    assert not two_ssh.username\n    assert not two_ssh.password\n\n    three = authenticator.get_credentials_for_git_url(\"https://foo.bar/org/three.git\")\n    assert not three.username\n    assert not three.password\n\n\n@pytest.mark.parametrize(\n    (\"ca_cert\", \"client_cert\", \"result\"),\n    [\n        (None, None, RepositoryCertificateConfig()),\n        (\n            \"path/to/ca.pem\",\n            \"path/to/client.pem\",\n            RepositoryCertificateConfig(\n                Path(\"path/to/ca.pem\"), Path(\"path/to/client.pem\")\n            ),\n        ),\n        (\n            None,\n            \"path/to/client.pem\",\n            RepositoryCertificateConfig(None, Path(\"path/to/client.pem\")),\n        ),\n        (\n            \"path/to/ca.pem\",\n            None,\n            RepositoryCertificateConfig(Path(\"path/to/ca.pem\"), None),\n        ),\n        (True, None, RepositoryCertificateConfig()),\n        (False, None, RepositoryCertificateConfig(verify=False)),\n        (\n            False,\n            \"path/to/client.pem\",\n            RepositoryCertificateConfig(None, Path(\"path/to/client.pem\"), verify=False),\n        ),\n    ],\n)\ndef test_repository_certificate_configuration_create(\n    ca_cert: str | bool | None,\n    client_cert: str | None,\n    result: RepositoryCertificateConfig,\n    config: Config,\n) -> None:\n    cert_config = {}\n\n    if ca_cert is not None:\n        cert_config[\"cert\"] = ca_cert\n\n    if client_cert is not None:\n        cert_config[\"client-cert\"] = client_cert\n\n    config.merge({\"certificates\": {\"foo\": cert_config}})\n\n    assert RepositoryCertificateConfig.create(\"foo\", config) == result\n"
  },
  {
    "path": "tests/utils/test_cache.py",
    "content": "from __future__ import annotations\n\nimport concurrent.futures\nimport shutil\nimport traceback\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import TypeVar\n\nimport pytest\n\nfrom packaging.tags import Tag\nfrom poetry.core.packages.utils.link import Link\n\nfrom poetry.utils.cache import ArtifactCache\nfrom poetry.utils.cache import FileCache\nfrom poetry.utils.env import MockEnv\n\n\nif TYPE_CHECKING:\n    from typing import Any\n\n    from pytest_mock import MockerFixture\n\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n\n\nT = TypeVar(\"T\")\n\n\n@pytest.fixture\ndef repository_cache_dir(config: Config) -> Path:\n    return config.repository_cache_directory\n\n\n@pytest.fixture\ndef poetry_file_cache(repository_cache_dir: Path) -> FileCache[Any]:\n    return FileCache(repository_cache_dir / \"cache\")\n\n\ndef test_cache_validates(repository_cache_dir: Path) -> None:\n    with pytest.raises(ValueError) as e:\n        FileCache(repository_cache_dir / \"cache\", hash_type=\"unknown\")\n    assert str(e.value) == \"FileCache.hash_type is unknown value: 'unknown'.\"\n\n\ndef test_cache_get_put_has(repository_cache_dir: Path) -> None:\n    cache: FileCache[Any] = FileCache(repository_cache_dir / \"cache\")\n    cache.put(\"key1\", \"value\")\n    cache.put(\"key2\", {\"a\": [\"json-encoded\", \"value\"]})\n\n    assert cache.get(\"key1\") == \"value\"\n    assert cache.get(\"key2\") == {\"a\": [\"json-encoded\", \"value\"]}\n    assert cache.has(\"key1\")\n    assert cache.has(\"key2\")\n    assert not cache.has(\"key3\")\n\n\ndef test_cache_forget(repository_cache_dir: Path) -> None:\n    cache: FileCache[Any] = FileCache(repository_cache_dir / \"cache\")\n    cache.put(\"key1\", \"value\")\n    cache.put(\"key2\", \"value\")\n\n    assert cache.has(\"key1\")\n    assert cache.has(\"key2\")\n\n    cache.forget(\"key1\")\n\n    assert not cache.has(\"key1\")\n    assert cache.has(\"key2\")\n\n\ndef test_cache_flush(repository_cache_dir: Path) -> None:\n    cache: FileCache[Any] = FileCache(repository_cache_dir / \"cache\")\n    cache.put(\"key1\", \"value\")\n    cache.put(\"key2\", \"value\")\n\n    assert cache.has(\"key1\")\n    assert cache.has(\"key2\")\n\n    cache.flush()\n\n    assert not cache.has(\"key1\")\n    assert not cache.has(\"key2\")\n\n\ndef test_cache_remember(repository_cache_dir: Path, mocker: MockerFixture) -> None:\n    cache: FileCache[Any] = FileCache(repository_cache_dir / \"cache\")\n\n    method = mocker.Mock(return_value=\"value2\")\n    cache.put(\"key1\", \"value1\")\n    assert cache.remember(\"key1\", method) == \"value1\"\n    method.assert_not_called()\n\n    assert cache.remember(\"key2\", method) == \"value2\"\n    method.assert_called()\n\n\ndef test_cache_get_limited_minutes(\n    repository_cache_dir: Path, mocker: MockerFixture\n) -> None:\n    cache: FileCache[Any] = FileCache(repository_cache_dir / \"cache\")\n\n    start_time = 1111111111\n\n    mocker.patch(\"time.time\", return_value=start_time)\n    cache.put(\"key1\", \"value\", minutes=5)\n    cache.put(\"key2\", \"value\", minutes=5)\n\n    assert cache.get(\"key1\") is not None\n    assert cache.get(\"key2\") is not None\n\n    mocker.patch(\"time.time\", return_value=start_time + 5 * 60 + 1)\n    # check to make sure that the cache deletes for has() and get()\n    assert not cache.has(\"key1\")\n    assert cache.get(\"key2\") is None\n\n\ndef test_missing_cache_file(poetry_file_cache: FileCache[Any]) -> None:\n    poetry_file_cache.put(\"key1\", \"value\")\n\n    key1_path = (\n        poetry_file_cache.path\n        / \"81/74/09/96/87/a2/66/21/8174099687a26621f4e2cdd7cc03b3dacedb3fb962255b1aafd033cabe831530\"\n    )\n    assert key1_path.exists()\n    key1_path.unlink()  # corrupt cache by removing a key file\n\n    assert poetry_file_cache.get(\"key1\") is None\n\n\ndef test_missing_cache_path(poetry_file_cache: FileCache[Any]) -> None:\n    poetry_file_cache.put(\"key1\", \"value\")\n\n    key1_partial_path = poetry_file_cache.path / \"81/74/09/96/87/a2/\"\n    assert key1_partial_path.exists()\n    shutil.rmtree(\n        key1_partial_path\n    )  # corrupt cache by removing a subdirectory containing a key file\n\n    assert poetry_file_cache.get(\"key1\") is None\n\n\n@pytest.mark.parametrize(\n    \"corrupt_payload\",\n    [\n        \"\",  # empty file\n        b\"\\x00\",  # null\n        \"99999999\",  # truncated file\n        '999999a999\"value\"',  # corrupt lifetime\n        b'9999999999\"va\\xd8\\x00\"',  # invalid unicode\n        \"fil3systemFa!led\",  # garbage file\n    ],\n)\ndef test_detect_corrupted_cache_key_file(\n    corrupt_payload: str | bytes, poetry_file_cache: FileCache[Any]\n) -> None:\n    poetry_file_cache.put(\"key1\", \"value\")\n\n    key1_path = (\n        poetry_file_cache.path\n        / \"81/74/09/96/87/a2/66/21/8174099687a26621f4e2cdd7cc03b3dacedb3fb962255b1aafd033cabe831530\"\n    )\n    assert key1_path.exists()\n\n    # original content: 9999999999\"value\"\n\n    if isinstance(corrupt_payload, str):\n        with open(key1_path, \"w\", encoding=\"utf-8\") as f:\n            f.write(corrupt_payload)  # write corrupt data\n    else:\n        with open(key1_path, \"wb\") as f:\n            f.write(corrupt_payload)  # write corrupt data\n\n    assert poetry_file_cache.get(\"key1\") is None\n\n\ndef test_get_cache_directory_for_link(tmp_path: Path) -> None:\n    cache = ArtifactCache(cache_dir=tmp_path)\n    directory = cache.get_cache_directory_for_link(\n        Link(\"https://files.pythonhosted.org/poetry-1.1.0.tar.gz\")\n    )\n\n    expected = Path(\n        f\"{tmp_path.as_posix()}/41/9c/6e/\"\n        \"ef83f08fcf4dac7cd78d843e7974d601a19c90e4bb90bb76b4a7a61548\"\n    )\n\n    assert directory == expected\n\n\n@pytest.mark.parametrize(\"subdirectory\", [None, \"subdir\"])\ndef test_get_cache_directory_for_git(tmp_path: Path, subdirectory: str | None) -> None:\n    cache = ArtifactCache(cache_dir=tmp_path)\n    directory = cache.get_cache_directory_for_git(\n        url=\"https://github.com/demo/demo.git\", ref=\"123456\", subdirectory=subdirectory\n    )\n\n    if subdirectory:\n        expected = Path(\n            f\"{tmp_path.as_posix()}/53/08/33/\"\n            \"7851e5806669aa15ab0c555b13bd5523978057323c6a23a9cee18ec51c\"\n        )\n    else:\n        expected = Path(\n            f\"{tmp_path.as_posix()}/61/14/30/\"\n            \"7c57f8fd71e4eee40b18893b9b586cba45177f15e300f4fb8b14ccc933\"\n        )\n\n    assert directory == expected\n\n\ndef test_get_cached_archives(fixture_dir: FixtureDirGetter) -> None:\n    distributions = fixture_dir(\"distributions\")\n    cache = ArtifactCache(cache_dir=Path())\n\n    archives = cache._get_cached_archives(distributions)\n\n    assert archives\n    assert set(archives) == set(distributions.glob(\"*.whl\")) | set(\n        distributions.glob(\"*.tar.gz\")\n    )\n\n\n@pytest.mark.parametrize(\n    (\"link\", \"strict\", \"available_packages\"),\n    [\n        (\n            \"https://files.pythonhosted.org/demo-0.1.0.tar.gz\",\n            True,\n            [\n                Path(\"/cache/demo-0.1.0-py2.py3-none-any\"),\n                Path(\"/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\"),\n                Path(\"/cache/demo-0.1.0-cp37-cp37-macosx_10_15_x86_64.whl\"),\n            ],\n        ),\n        (\n            \"https://example.com/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            False,\n            [],\n        ),\n    ],\n)\ndef test_get_not_found_cached_archive_for_link(\n    mocker: MockerFixture,\n    link: str,\n    strict: bool,\n    available_packages: list[Path],\n) -> None:\n    env = MockEnv(\n        version_info=(3, 8, 3),\n        marker_env={\"interpreter_name\": \"cpython\", \"interpreter_version\": \"3.8.3\"},\n        supported_tags=[\n            Tag(\"cp38\", \"cp38\", \"macosx_10_15_x86_64\"),\n            Tag(\"py3\", \"none\", \"any\"),\n        ],\n    )\n    cache = ArtifactCache(cache_dir=Path())\n\n    mocker.patch.object(\n        cache,\n        \"_get_cached_archives\",\n        return_value=available_packages,\n    )\n\n    archive = cache.get_cached_archive_for_link(Link(link), strict=strict, env=env)\n\n    assert archive is None\n\n\n@pytest.mark.parametrize(\n    (\"link\", \"cached\", \"strict\"),\n    [\n        (\n            \"https://files.pythonhosted.org/demo-0.1.0.tar.gz\",\n            \"/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            False,\n        ),\n        (\n            \"https://example.com/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            \"/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            False,\n        ),\n        (\n            \"https://files.pythonhosted.org/demo-0.1.0.tar.gz\",\n            \"/cache/demo-0.1.0.tar.gz\",\n            True,\n        ),\n        (\n            \"https://example.com/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            \"/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\",\n            True,\n        ),\n    ],\n)\ndef test_get_found_cached_archive_for_link(\n    mocker: MockerFixture,\n    link: str,\n    cached: str,\n    strict: bool,\n) -> None:\n    env = MockEnv(\n        version_info=(3, 8, 3),\n        marker_env={\"interpreter_name\": \"cpython\", \"interpreter_version\": \"3.8.3\"},\n        supported_tags=[\n            Tag(\"cp38\", \"cp38\", \"macosx_10_15_x86_64\"),\n            Tag(\"py3\", \"none\", \"any\"),\n        ],\n    )\n    cache = ArtifactCache(cache_dir=Path())\n\n    mocker.patch.object(\n        cache,\n        \"_get_cached_archives\",\n        return_value=[\n            Path(\"/cache/demo-0.1.0-py2.py3-none-any\"),\n            Path(\"/cache/demo-0.1.0.tar.gz\"),\n            Path(\"/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl\"),\n            Path(\"/cache/demo-0.1.0-cp37-cp37-macosx_10_15_x86_64.whl\"),\n        ],\n    )\n\n    archive = cache.get_cached_archive_for_link(Link(link), strict=strict, env=env)\n\n    assert Path(cached) == archive\n\n\ndef test_get_cached_archive_for_link_no_race_condition(\n    tmp_path: Path, mocker: MockerFixture\n) -> None:\n    cache = ArtifactCache(cache_dir=tmp_path)\n    link = Link(\"https://files.pythonhosted.org/demo-0.1.0.tar.gz\")\n\n    def replace_file(_: str, dest: Path) -> None:\n        dest.unlink(missing_ok=True)\n        # write some data (so it takes a while) to provoke possible race conditions\n        dest.write_text(\"a\" * 2**20, encoding=\"utf-8\")\n\n    download_mock = mocker.Mock(side_effect=replace_file)\n\n    def get_archive(link: Link) -> Path:\n        path: Path = cache.get_cached_archive_for_link(\n            link, strict=True, download_func=download_mock\n        )\n        return path\n\n    with concurrent.futures.ThreadPoolExecutor() as executor:\n        tasks = []\n        for _ in range(4):\n            tasks.append(executor.submit(get_archive, link))\n\n        concurrent.futures.wait(tasks)\n        results = set()\n        for task in tasks:\n            try:\n                results.add(task.result())\n            except Exception:\n                pytest.fail(traceback.format_exc())\n        assert results == {cache.get_cache_directory_for_link(link) / link.filename}\n        download_mock.assert_called_once()\n\n\ndef test_get_cached_archive_for_git() -> None:\n    \"\"\"Smoke test that checks that no assertion is raised.\"\"\"\n    cache = ArtifactCache(cache_dir=Path())\n    archive = cache.get_cached_archive_for_git(\"url\", \"ref\", \"subdirectory\", MockEnv())\n    assert archive is None\n"
  },
  {
    "path": "tests/utils/test_dependency_specification.py",
    "content": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom deepdiff.diff import DeepDiff\n\nfrom poetry.inspection.info import PackageInfo\nfrom poetry.utils.dependency_specification import RequirementsParser\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.cache import ArtifactCache\n    from poetry.utils.dependency_specification import DependencySpec\n\n\n@pytest.mark.parametrize(\n    (\"requirement\", \"expected_variants\"),\n    [\n        (\n            \"git+http://github.com/demo/demo.git\",\n            ({\"git\": \"http://github.com/demo/demo.git\", \"name\": \"demo\"},),\n        ),\n        (\n            \"git+https://github.com/demo/demo.git\",\n            ({\"git\": \"https://github.com/demo/demo.git\", \"name\": \"demo\"},),\n        ),\n        (\n            \"git+ssh://github.com/demo/demo.git\",\n            ({\"git\": \"ssh://github.com/demo/demo.git\", \"name\": \"demo\"},),\n        ),\n        (\n            \"git+https://github.com/demo/demo.git#main\",\n            (\n                {\n                    \"git\": \"https://github.com/demo/demo.git\",\n                    \"name\": \"demo\",\n                    \"rev\": \"main\",\n                },\n            ),\n        ),\n        (\n            \"git+https://github.com/demo/demo.git@main\",\n            (\n                {\n                    \"git\": \"https://github.com/demo/demo.git\",\n                    \"name\": \"demo\",\n                    \"rev\": \"main\",\n                },\n            ),\n        ),\n        (\n            \"git+https://github.com/demo/subdirectories.git@main#subdirectory=two\",\n            (\n                {\n                    \"git\": \"https://github.com/demo/subdirectories.git\",\n                    \"name\": \"two\",\n                    \"rev\": \"main\",\n                    \"subdirectory\": \"two\",\n                },\n            ),\n        ),\n        (\"demo\", ({\"name\": \"demo\"},)),\n        (\"demo@1.0.0\", ({\"name\": \"demo\", \"version\": \"1.0.0\"},)),\n        (\"demo@^1.0.0\", ({\"name\": \"demo\", \"version\": \"^1.0.0\"},)),\n        (\"demo@==1.0.0\", ({\"name\": \"demo\", \"version\": \"==1.0.0\"},)),\n        (\"demo@!=1.0.0\", ({\"name\": \"demo\", \"version\": \"!=1.0.0\"},)),\n        (\"demo@~1.0.0\", ({\"name\": \"demo\", \"version\": \"~1.0.0\"},)),\n        (\n            \"demo[a,b]@1.0.0\",\n            ({\"name\": \"demo\", \"version\": \"1.0.0\", \"extras\": [\"a\", \"b\"]},),\n        ),\n        (\"demo[a,b]\", ({\"name\": \"demo\", \"extras\": [\"a\", \"b\"]},)),\n        (\"../demo\", ({\"name\": \"demo\", \"path\": \"../demo\"},)),\n        (\"../demo/demo.whl\", ({\"name\": \"demo\", \"path\": \"../demo/demo.whl\"},)),\n        (\n            \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\",\n            (\n                {\n                    \"name\": \"demo\",\n                    \"url\": \"https://files.pythonhosted.org/distributions/demo-0.1.0.tar.gz\",\n                },\n            ),\n        ),\n        # PEP 508 inputs\n        (\n            \"poetry-core (>=1.0.7,<1.1.0)\",\n            ({\"name\": \"poetry-core\", \"version\": \">=1.0.7,<1.1.0\"},),\n        ),\n        (\n            'requests [security,tests] >= 2.8.1, == 2.8.* ; python_version < \"2.7\"',\n            (  # allow several equivalent versions to make test more robust\n                {\n                    \"name\": \"requests\",\n                    \"markers\": 'python_version < \"2.7\"',\n                    \"version\": \">=2.8.1,<2.9\",\n                    \"extras\": [\"security\", \"tests\"],\n                },\n                {\n                    \"name\": \"requests\",\n                    \"markers\": 'python_version < \"2.7\"',\n                    \"version\": \">=2.8.1,<2.9.0\",\n                    \"extras\": [\"security\", \"tests\"],\n                },\n                {\n                    \"name\": \"requests\",\n                    \"markers\": 'python_version < \"2.7\"',\n                    \"version\": \">=2.8.1,<2.9.dev0\",\n                    \"extras\": [\"security\", \"tests\"],\n                },\n                {\n                    \"name\": \"requests\",\n                    \"markers\": 'python_version < \"2.7\"',\n                    \"version\": \">=2.8.1,<2.9.0.dev0\",\n                    \"extras\": [\"security\", \"tests\"],\n                },\n                {\n                    \"name\": \"requests\",\n                    \"markers\": 'python_version < \"2.7\"',\n                    \"version\": \">=2.8.1,==2.8.*\",\n                    \"extras\": [\"security\", \"tests\"],\n                },\n            ),\n        ),\n        (\"name (>=3,<4)\", ({\"name\": \"name\", \"version\": \">=3,<4\"},)),\n        (\n            \"name@http://foo.com\",\n            ({\"name\": \"name\", \"url\": \"http://foo.com\"},),\n        ),\n        (\n            \"name [fred,bar] @ http://foo.com ; python_version=='2.7'\",\n            (\n                {\n                    \"name\": \"name\",\n                    \"markers\": 'python_version == \"2.7\"',\n                    \"url\": \"http://foo.com\",\n                    \"extras\": [\"fred\", \"bar\"],\n                },\n            ),\n        ),\n        (\n            (\n                'cachecontrol[filecache] (>=0.12.9,<0.13.0); python_version >= \"3.6\"'\n                ' and python_version < \"4.0\"'\n            ),\n            (\n                {\n                    \"version\": \">=0.12.9,<0.13.0\",\n                    \"markers\": 'python_version >= \"3.6\" and python_version < \"4.0\"',\n                    \"extras\": [\"filecache\"],\n                    \"name\": \"cachecontrol\",\n                },\n            ),\n        ),\n    ],\n)\ndef test_parse_dependency_specification(\n    requirement: str,\n    expected_variants: Collection[DependencySpec],\n    mocker: MockerFixture,\n    artifact_cache: ArtifactCache,\n) -> None:\n    original = Path.exists\n\n    # Parsing file and path dependencies reads metadata from the file or path in\n    # question: for these tests we mock that out.\n    def _mock(self: Path) -> bool:\n        if \"/\" in requirement and self == Path.cwd().joinpath(requirement):\n            return True\n        return original(self)\n\n    mocker.patch(\"pathlib.Path.exists\", _mock)\n\n    mocker.patch(\n        \"poetry.inspection.info.get_pep517_metadata\",\n        return_value=PackageInfo(name=\"demo\", version=\"0.1.2\"),\n    )\n\n    assert any(\n        not DeepDiff(\n            RequirementsParser(artifact_cache=artifact_cache).parse(requirement),\n            specification,\n            ignore_order=True,\n        )\n        for specification in expected_variants\n    )\n"
  },
  {
    "path": "tests/utils/test_extras.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.factory import Factory\nfrom poetry.utils.extras import get_extra_package_names\n\n\nif TYPE_CHECKING:\n    from packaging.utils import NormalizedName\n\n_PACKAGE_FOO = Package(\"foo\", \"0.1.0\")\n_PACKAGE_SPAM = Package(\"spam\", \"0.2.0\")\n_PACKAGE_BAR = Package(\"bar\", \"0.3.0\")\n_PACKAGE_BAR.add_dependency(Factory.create_dependency(\"foo\", \"*\"))\n\n# recursive dependency\n_PACKAGE_BAZ = Package(\"baz\", \"0.4.0\")\n_PACKAGE_BAZ.add_dependency(Factory.create_dependency(\"quix\", \"*\"))\n_PACKAGE_QUIX = Package(\"quix\", \"0.5.0\")\n_PACKAGE_QUIX.add_dependency(Factory.create_dependency(\"baz\", \"*\"))\n\n\n@pytest.mark.parametrize(\n    [\"packages\", \"extras\", \"extra_names\", \"expected_extra_package_names\"],\n    [\n        # Empty edge case\n        ([], {}, [], set()),\n        # Selecting no extras is fine\n        ([_PACKAGE_FOO], {}, [], set()),\n        # An empty extras group should return an empty list\n        ([_PACKAGE_FOO], {\"group0\": []}, [\"group0\"], set()),\n        # Selecting an extras group should return the contained packages\n        (\n            [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR],\n            {\"group0\": [\"foo\"]},\n            [\"group0\"],\n            {\"foo\"},\n        ),\n        # If a package has dependencies, we should also get their names\n        (\n            [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR],\n            {\"group0\": [\"bar\"], \"group1\": [\"spam\"]},\n            [\"group0\"],\n            {\"bar\", \"foo\"},\n        ),\n        # Selecting multiple extras should get us the union of all package names\n        (\n            [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR],\n            {\"group0\": [\"bar\"], \"group1\": [\"spam\"]},\n            [\"group0\", \"group1\"],\n            {\"bar\", \"foo\", \"spam\"},\n        ),\n        (\n            [_PACKAGE_BAZ, _PACKAGE_QUIX],\n            {\"group0\": [\"baz\"], \"group1\": [\"quix\"]},\n            [\"group0\", \"group1\"],\n            {\"baz\", \"quix\"},\n        ),\n    ],\n)\ndef test_get_extra_package_names(\n    packages: list[Package],\n    extras: dict[NormalizedName, list[NormalizedName]],\n    extra_names: list[NormalizedName],\n    expected_extra_package_names: set[str],\n) -> None:\n    assert (\n        get_extra_package_names(packages, extras, extra_names)\n        == expected_extra_package_names\n    )\n"
  },
  {
    "path": "tests/utils/test_helpers.py",
    "content": "from __future__ import annotations\n\nimport base64\nimport re\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Any\n\nimport pytest\nimport responses\n\nfrom poetry.core.utils.helpers import parse_requires\nfrom requests.exceptions import ChunkedEncodingError\n\nfrom poetry.utils.helpers import Downloader\nfrom poetry.utils.helpers import HTTPRangeRequestSupportedError\nfrom poetry.utils.helpers import download_file\nfrom poetry.utils.helpers import ensure_path\nfrom poetry.utils.helpers import get_file_hash\nfrom poetry.utils.helpers import get_highest_priority_hash_type\n\n\nif TYPE_CHECKING:\n    from requests import PreparedRequest\n\n    from tests.conftest import Config\n    from tests.types import FixtureDirGetter\n    from tests.types import HttpResponse\n\n\ndef test_parse_requires() -> None:\n    requires = \"\"\"\\\njsonschema>=2.6.0.0,<3.0.0.0\nlockfile>=0.12.0.0,<0.13.0.0\npip-tools>=1.11.0.0,<2.0.0.0\npkginfo>=1.4.0.0,<2.0.0.0\npyrsistent>=0.14.2.0,<0.15.0.0\ntoml>=0.9.0.0,<0.10.0.0\ncleo>=0.6.0.0,<0.7.0.0\ncachy>=0.1.1.0,<0.2.0.0\ncachecontrol>=0.12.4.0,<0.13.0.0\nrequests>=2.18.0.0,<3.0.0.0\nmsgpack-python>=0.5.0.0,<0.6.0.0\npyparsing>=2.2.0.0,<3.0.0.0\nrequests-toolbelt>=0.8.0.0,<0.9.0.0\n\n[:(python_version >= \"2.7.0.0\" and python_version < \"2.8.0.0\")\\\n or (python_version >= \"3.4.0.0\" and python_version < \"3.5.0.0\")]\ntyping>=3.6.0.0,<4.0.0.0\n\n[:python_version >= \"2.7.0.0\" and python_version < \"2.8.0.0\"]\nvirtualenv>=15.2.0.0,<16.0.0.0\npathlib2>=2.3.0.0,<3.0.0.0\n\n[:python_version >= \"3.4.0.0\" and python_version < \"3.6.0.0\"]\nzipfile36>=0.1.0.0,<0.2.0.0\n\n[dev]\nisort@ git+git://github.com/timothycrosley/isort.git@e63ae06ec7d70b06df9e528357650281a3d3ec22#egg=isort\n\"\"\"\n    result = parse_requires(requires)\n    # fmt: off\n    expected = [\n        \"jsonschema>=2.6.0.0,<3.0.0.0\",\n        \"lockfile>=0.12.0.0,<0.13.0.0\",\n        \"pip-tools>=1.11.0.0,<2.0.0.0\",\n        \"pkginfo>=1.4.0.0,<2.0.0.0\",\n        \"pyrsistent>=0.14.2.0,<0.15.0.0\",\n        \"toml>=0.9.0.0,<0.10.0.0\",\n        \"cleo>=0.6.0.0,<0.7.0.0\",\n        \"cachy>=0.1.1.0,<0.2.0.0\",\n        \"cachecontrol>=0.12.4.0,<0.13.0.0\",\n        \"requests>=2.18.0.0,<3.0.0.0\",\n        \"msgpack-python>=0.5.0.0,<0.6.0.0\",\n        \"pyparsing>=2.2.0.0,<3.0.0.0\",\n        \"requests-toolbelt>=0.8.0.0,<0.9.0.0\",\n        'typing>=3.6.0.0,<4.0.0.0 ; (python_version >= \"2.7.0.0\" and python_version < \"2.8.0.0\") or (python_version >= \"3.4.0.0\" and python_version < \"3.5.0.0\")',\n        'virtualenv>=15.2.0.0,<16.0.0.0 ; python_version >= \"2.7.0.0\" and python_version < \"2.8.0.0\"',\n        'pathlib2>=2.3.0.0,<3.0.0.0 ; python_version >= \"2.7.0.0\" and python_version < \"2.8.0.0\"',\n        'zipfile36>=0.1.0.0,<0.2.0.0 ; python_version >= \"3.4.0.0\" and python_version < \"3.6.0.0\"',\n        'isort@ git+git://github.com/timothycrosley/isort.git@e63ae06ec7d70b06df9e528357650281a3d3ec22#egg=isort ; extra == \"dev\"',\n    ]\n    # fmt: on\n    assert result == expected\n\n\ndef test_default_hash(fixture_dir: FixtureDirGetter) -> None:\n    sha_256 = \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n    assert get_file_hash(fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\") == sha_256\n\n\n@pytest.mark.parametrize(\n    \"hash_name,expected\",\n    [\n        (\"sha224\", \"d26bd24163fe91c16b4b0162e773514beab77b76114d9faf6a31e350\"),\n        (\n            \"sha3_512\",\n            \"196f4af9099185054ed72ca1d4c57707da5d724df0af7c3dfcc0fd018b0e0533908e790a291600c7d196fe4411b4f5f6db45213fe6e5cd5512bf18b2e9eff728\",\n        ),\n        (\n            \"blake2s\",\n            \"6dd9007d36c106defcf362cc637abeca41e8e93999928c8fcfaba515ed33bc93\",\n        ),\n        (\n            \"sha3_384\",\n            \"787264d7885a0c305d2ee4daecfff435d11818399ef96cacef7e7c6bb638ce475f630d39fdd2800ca187dcd0071dc410\",\n        ),\n        (\n            \"blake2b\",\n            \"077a34e8252c8f6776bddd0d34f321cc52762cb4c11a1c7aa9b6168023f1722caf53c9f029074a6eb990a8de341d415dd986293bc2a2fccddad428be5605696e\",\n        ),\n        (\n            \"sha256\",\n            \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\",\n        ),\n        (\n            \"sha512\",\n            \"766ecf369b6bdf801f6f7bbfe23923cc9793d633a55619472cd3d5763f9154711fbf57c8b6ca74e4a82fa9bd8380af831e7b8668e68e362669fc60b1d81d79ad\",\n        ),\n        (\n            \"sha384\",\n            \"c638f32460f318035e4600284ba64fb531630740aebd33885946e527002d742787ff09eb65fd81bc34ce5ff5ef11cfe8\",\n        ),\n        (\"sha3_224\", \"72980fc7bdf8c4d34268dc469442b09e1ccd2a8ff390954fc4d55a5a\"),\n        (\"sha1\", \"91b585bd38f72d7ceedb07d03f94911b772fdc4c\"),\n        (\n            \"sha3_256\",\n            \"7da5c08b416e6bcb339d6bedc0fe077c6e69af00607251ef4424c356ea061fcb\",\n        ),\n    ],\n)\ndef test_guaranteed_hash(\n    hash_name: str, expected: str, fixture_dir: FixtureDirGetter\n) -> None:\n    file_path = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    assert get_file_hash(file_path, hash_name) == expected\n\n\ndef test_download_file(\n    http: responses.RequestsMock, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    file_path = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    url = \"https://foo.com/demo-0.1.0.tar.gz\"\n    http.get(url, body=file_path.read_bytes())\n    dest = tmp_path / \"demo-0.1.0.tar.gz\"\n\n    download_file(url, dest)\n\n    expect_sha_256 = \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n    assert get_file_hash(dest) == expect_sha_256\n    assert http.calls[-1].request.headers[\"Accept-Encoding\"] == \"Identity\"\n\n\ndef test_download_file_recover_from_error(\n    http: responses.RequestsMock, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    file_path = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    file_body = file_path.read_bytes()\n    file_length = len(file_body)\n    url = \"https://foo.com/demo-0.1.0.tar.gz\"\n\n    def handle_request(request: PreparedRequest) -> HttpResponse:\n        if request.headers.get(\"Range\") is None:\n            response_headers = {\n                \"Content-Length\": str(file_length),\n                \"Accept-Ranges\": \"bytes\",\n            }\n            return 200, response_headers, file_body[: file_length // 2]\n        else:\n            start = int(\n                request.headers.get(\"Range\", \"bytes=0-\").split(\"=\")[1].split(\"-\")[0]\n            )\n            response_headers = {\"Content-Length\": str(len(file_body[start:]))}\n            return 206, response_headers, file_body[start:]\n\n    http.add_callback(responses.GET, url, callback=handle_request)\n    dest = tmp_path / \"demo-0.1.0.tar.gz\"\n\n    download_file(url, dest, chunk_size=file_length // 2, max_retries=1)\n\n    expect_sha_256 = \"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad\"\n    assert get_file_hash(dest) == expect_sha_256\n    assert http.calls[-1].request.headers[\"Accept-Encoding\"] == \"Identity\"\n    assert http.calls[-1].request.headers[\"Range\"] == f\"bytes={file_length // 2}-\"\n\n\ndef test_download_file_fail_when_no_range(\n    http: responses.RequestsMock, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    file_path = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    file_body = file_path.read_bytes()\n    file_length = len(file_body)\n    url = \"https://foo.com/demo-0.1.0.tar.gz\"\n\n    def handle_request(request: PreparedRequest) -> HttpResponse:\n        response_headers = {\"Content-Length\": str(file_length)}\n        return 200, response_headers, file_body[: file_length // 2]\n\n    http.add_callback(responses.GET, url, callback=handle_request)\n    dest = tmp_path / \"demo-0.1.0.tar.gz\"\n    with pytest.raises(ChunkedEncodingError):\n        download_file(url, dest, chunk_size=file_length // 2, max_retries=1)\n\n\ndef test_download_file_fail_when_first_chunk_failed(\n    http: responses.RequestsMock, fixture_dir: FixtureDirGetter, tmp_path: Path\n) -> None:\n    file_path = fixture_dir(\"distributions\") / \"demo-0.1.0.tar.gz\"\n    file_body = file_path.read_bytes()\n    file_length = len(file_body)\n    url = \"https://foo.com/demo-0.1.0.tar.gz\"\n\n    def handle_request(request: PreparedRequest) -> tuple[int, dict[str, Any], bytes]:\n        response_headers = {\n            \"Content-Length\": str(file_length),\n            \"Accept-Ranges\": \"bytes\",\n        }\n        return 200, response_headers, file_body[: file_length // 2]\n\n    http.add_callback(responses.GET, url, callback=handle_request)\n    dest = tmp_path / \"demo-0.1.0.tar.gz\"\n    with pytest.raises(ChunkedEncodingError):\n        download_file(url, dest, chunk_size=file_length, max_retries=1)\n\n\n@pytest.mark.parametrize(\n    \"hash_types,expected\",\n    [\n        ((\"sha512\", \"sha3_512\", \"md5\"), \"sha3_512\"),\n        (\"md5\", \"md5\"),\n        ((\"blah\", \"blah_blah\"), None),\n        ((), None),\n    ],\n)\ndef test_highest_priority_hash_type(hash_types: set[str], expected: str | None) -> None:\n    assert get_highest_priority_hash_type(hash_types, \"Blah\") == expected\n\n\n@pytest.mark.parametrize(\"accepts_ranges\", [False, True])\n@pytest.mark.parametrize(\"raise_accepts_ranges\", [False, True])\ndef test_download_file_raise_accepts_ranges(\n    http: responses.RequestsMock,\n    fixture_dir: FixtureDirGetter,\n    tmp_path: Path,\n    accepts_ranges: bool,\n    raise_accepts_ranges: bool,\n) -> None:\n    filename = \"demo-0.1.0-py2.py3-none-any.whl\"\n\n    def handle_request(request: PreparedRequest) -> tuple[int, dict[str, Any], bytes]:\n        file_path = fixture_dir(\"distributions\") / filename\n        response_headers = {}\n        if accepts_ranges:\n            response_headers[\"Accept-Ranges\"] = \"bytes\"\n        return 200, response_headers, file_path.read_bytes()\n\n    url = f\"https://foo.com/{filename}\"\n    http.add_callback(responses.GET, url, callback=handle_request)\n    dest = tmp_path / filename\n\n    if accepts_ranges and raise_accepts_ranges:\n        with pytest.raises(HTTPRangeRequestSupportedError):\n            download_file(url, dest, raise_accepts_ranges=raise_accepts_ranges)\n        assert not dest.exists()\n    else:\n        download_file(url, dest, raise_accepts_ranges=raise_accepts_ranges)\n        assert dest.is_file()\n\n\ndef test_downloader_uses_authenticator_by_default(\n    config: Config,\n    http: responses.RequestsMock,\n    tmp_working_directory: Path,\n) -> None:\n    import poetry.utils.authenticator\n\n    # force set default authenticator to None so that it is recreated using patched config\n    poetry.utils.authenticator._authenticator = None\n\n    config.merge(\n        {\n            \"repositories\": {\"foo\": {\"url\": \"https://foo.bar/files/\"}},\n            \"http-basic\": {\"foo\": {\"username\": \"bar\", \"password\": \"baz\"}},\n        }\n    )\n\n    http.get(\n        re.compile(\"^https?://foo.bar/(.+?)$\"),\n    )\n\n    Downloader(\n        \"https://foo.bar/files/foo-0.1.0.tar.gz\",\n        tmp_working_directory / \"foo-0.1.0.tar.gz\",\n    )\n\n    request = http.calls[-1].request\n    basic_auth = base64.b64encode(b\"bar:baz\").decode()\n    assert request.headers[\"Authorization\"] == f\"Basic {basic_auth}\"\n\n\ndef test_ensure_path_converts_string(tmp_path: Path) -> None:\n    assert tmp_path.exists()\n    assert ensure_path(path=tmp_path.as_posix(), is_directory=True) == tmp_path\n\n\ndef test_ensure_path_does_not_convert_path(tmp_path: Path) -> None:\n    assert tmp_path.exists()\n    assert Path(tmp_path.as_posix()) is not tmp_path\n\n    result = ensure_path(path=tmp_path, is_directory=True)\n\n    assert result == tmp_path\n    assert result is tmp_path\n\n\ndef test_ensure_path_is_directory_parameter(tmp_path: Path) -> None:\n    with pytest.raises(ValueError):\n        ensure_path(path=tmp_path, is_directory=False)\n\n    assert ensure_path(path=tmp_path, is_directory=True) is tmp_path\n\n\ndef test_ensure_path_file(tmp_path: Path) -> None:\n    path = tmp_path.joinpath(\"some_file.txt\")\n    assert not path.exists()\n\n    with pytest.raises(ValueError):\n        ensure_path(path=path, is_directory=False)\n\n    path.write_text(\"foobar\", encoding=\"utf-8\")\n    assert ensure_path(path=path, is_directory=False) is path\n\n\ndef test_ensure_path_directory(tmp_path: Path) -> None:\n    path = tmp_path.joinpath(\"foobar\")\n    assert not path.exists()\n\n    with pytest.raises(ValueError):\n        ensure_path(path=path, is_directory=True)\n\n    path.mkdir()\n    assert ensure_path(path=path, is_directory=True) is path\n"
  },
  {
    "path": "tests/utils/test_isolated_build.py",
    "content": "from __future__ import annotations\n\nimport shutil\nimport sys\nimport uuid\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.core.packages.dependency import Dependency\n\nfrom poetry.factory import Factory\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.puzzle.provider import IncompatibleConstraintsError\nfrom poetry.repositories import RepositoryPool\nfrom poetry.repositories.installed_repository import InstalledRepository\nfrom poetry.utils.env import ephemeral_environment\nfrom poetry.utils.isolated_build import CONSTRAINTS_GROUP_NAME\nfrom poetry.utils.isolated_build import IsolatedBuildInstallError\nfrom poetry.utils.isolated_build import IsolatedEnv\nfrom poetry.utils.isolated_build import isolated_builder\nfrom tests.helpers import get_dependency\n\n\nif TYPE_CHECKING:\n    from collections.abc import Collection\n\n    from pytest_mock import MockerFixture\n\n    from poetry.repositories.pypi_repository import PyPiRepository\n    from tests.types import FixtureDirGetter\n\n\n@pytest.fixture()\ndef pool(pypi_repository: PyPiRepository) -> RepositoryPool:\n    pool = RepositoryPool()\n\n    pool.add_repository(pypi_repository)\n\n    return pool\n\n\n@pytest.fixture(autouse=True)\ndef setup(mocker: MockerFixture, pool: RepositoryPool) -> None:\n    mocker.patch.object(Factory, \"create_pool\", return_value=pool)\n\n\ndef test_isolated_env_install_success(pool: RepositoryPool) -> None:\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        assert not InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n\n        env.install({\"poetry-core\"})\n        assert InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n\n\ndef test_isolated_env_install_with_constraints_success(pool: RepositoryPool) -> None:\n    constraints = [\n        Dependency(\"poetry-core\", \"<2\", groups=[CONSTRAINTS_GROUP_NAME]),\n        Dependency(\"attrs\", \">1\", groups=[CONSTRAINTS_GROUP_NAME]),\n    ]\n\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        assert not InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n        assert not InstalledRepository.load(venv).find_packages(get_dependency(\"attrs\"))\n\n        env.install({\"poetry-core\"}, constraints=constraints)\n        assert InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n        assert not InstalledRepository.load(venv).find_packages(get_dependency(\"attrs\"))\n\n\ndef test_isolated_env_install_discards_requirements_not_needed_by_env(\n    pool: RepositoryPool,\n) -> None:\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        assert not InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n\n        venv_python_version = venv.get_marker_env().get(\"python_version\")\n        package_one = uuid.uuid4().hex\n        package_two = uuid.uuid4().hex\n\n        env.install(\n            {\n                f\"poetry-core; python_version=='{venv_python_version}'\",\n                f\"{package_one}>=1.0.0; python_version=='0.0'\",\n                f\"{package_two}>=2.0.0; platform_system=='Mirrors'\",\n            }\n        )\n        assert InstalledRepository.load(venv).find_packages(\n            get_dependency(\"poetry-core\")\n        )\n        assert not InstalledRepository.load(venv).find_packages(\n            get_dependency(package_one)\n        )\n\n        assert not InstalledRepository.load(venv).find_packages(\n            get_dependency(package_two)\n        )\n\n\n@pytest.mark.parametrize(\n    (\"requirements\", \"exception\"),\n    [\n        ({\"poetry-core==1.5.0\", \"poetry-core==1.6.0\"}, IncompatibleConstraintsError),\n        ({\"black==19.10b0\", \"attrs==17.4.0\"}, SolverProblemError),\n    ],\n)\ndef test_isolated_env_install_error(\n    requirements: Collection[str], exception: type[Exception], pool: RepositoryPool\n) -> None:\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        with pytest.raises(exception):\n            env.install(requirements)\n\n\n@pytest.mark.parametrize(\n    (\"requirements\", \"constraints\", \"exception\"),\n    [\n        (\n            {\"poetry-core==1.5.0\"},\n            [(\"poetry-core\", \"1.6.0\")],\n            IncompatibleConstraintsError,\n        ),\n        ({\"black==19.10b0\"}, [(\"attrs\", \"17.4.0\")], SolverProblemError),\n    ],\n)\ndef test_isolated_env_install_with_constraints_error(\n    requirements: Collection[str],\n    constraints: list[tuple[str, str]],\n    exception: type[Exception],\n    pool: RepositoryPool,\n) -> None:\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        with pytest.raises(exception):\n            env.install(\n                requirements,\n                constraints=[\n                    Dependency(name, version, groups=[CONSTRAINTS_GROUP_NAME])\n                    for name, version in constraints\n                ],\n            )\n\n\ndef test_isolated_env_install_failure(\n    pool: RepositoryPool, mocker: MockerFixture\n) -> None:\n    mocker.patch(\"poetry.installation.installer.Installer.run\", return_value=1)\n    with ephemeral_environment(Path(sys.executable)) as venv:\n        env = IsolatedEnv(venv, pool)\n        with pytest.raises(IsolatedBuildInstallError) as e:\n            env.install({\"a\", \"b>1\"})\n        assert e.value.requirements == {\"a\", \"b>1\"}\n\n\ndef test_isolated_builder_outside_poetry_project_context(\n    tmp_working_directory: Path, fixture_dir: FixtureDirGetter\n) -> None:\n    source = tmp_working_directory / \"source\"\n    shutil.copytree(fixture_dir(\"project_with_setup\"), source)\n    destination = tmp_working_directory / \"dist\"\n\n    try:\n        with isolated_builder(source, \"wheel\") as builder:\n            builder.metadata_path(destination)\n    except RuntimeError:\n        pytest.fail(\"Isolated builder did not fallback to default repository pool\")\n"
  },
  {
    "path": "tests/utils/test_log_utils.py",
    "content": "from __future__ import annotations\n\nfrom poetry.core.packages.package import Package\n\nfrom poetry.utils.env.mock_env import MockEnv\nfrom poetry.utils.log_utils import format_build_wheel_log\n\n\ndef test_format_build_wheel_log() -> None:\n    env = MockEnv(version_info=(3, 13, 1), platform=\"win32\", platform_machine=\"AMD64\")\n    package = Package(name=\"demo\", version=\"1.2.3\")\n    result = format_build_wheel_log(package, env)\n    expected = (\n        \" <info>Building a wheel file for demo for Python 3.13.1 on win32-AMD64</info>\"\n    )\n    assert result == expected\n"
  },
  {
    "path": "tests/utils/test_password_manager.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport os\n\nfrom typing import TYPE_CHECKING\nfrom unittest.mock import MagicMock\n\nimport pytest\n\nfrom poetry.utils.password_manager import HTTPAuthCredential\nfrom poetry.utils.password_manager import PasswordManager\nfrom poetry.utils.password_manager import PoetryKeyring\nfrom poetry.utils.password_manager import PoetryKeyringError\n\n\nif TYPE_CHECKING:\n    from pytest import LogCaptureFixture\n    from pytest_mock import MockerFixture\n\n    from tests.conftest import Config\n    from tests.conftest import DummyBackend\n\n\ndef test_set_http_password(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    manager.set_http_password(\"foo\", \"bar\", \"baz\")\n\n    assert dummy_keyring.get_password(\"poetry-repository-foo\", \"bar\") == \"baz\"\n\n    auth = config.get(\"http-basic.foo\")\n    assert auth[\"username\"] == \"bar\"\n    assert \"password\" not in auth\n\n\n@pytest.mark.parametrize(\n    (\"username\", \"password\", \"is_valid\"),\n    [\n        (\"bar\", \"baz\", True),\n        (\"\", \"baz\", True),\n        (\"bar\", \"\", True),\n        (\"\", \"\", False),\n    ],\n)\ndef test_get_http_auth(\n    username: str,\n    password: str,\n    is_valid: bool,\n    config: Config,\n    with_simple_keyring: None,\n    poetry_keyring: PoetryKeyring,\n) -> None:\n    poetry_keyring.set_password(\"foo\", username, password)\n\n    config.auth_config_source.add_property(\"http-basic.foo\", {\"username\": username})\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    auth = manager.get_http_auth(\"foo\")\n\n    if is_valid:\n        assert auth is not None\n        assert auth.username == (username or None)\n        assert auth.password == (password or None)\n    else:\n        assert auth.username is auth.password is None\n\n\ndef test_delete_http_password(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    dummy_keyring.set_password(\"poetry-repository-foo\", \"bar\", \"baz\")\n    config.auth_config_source.add_property(\"http-basic.foo\", {\"username\": \"bar\"})\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    manager.delete_http_password(\"foo\")\n\n    assert dummy_keyring.get_password(\"poetry-repository-foo\", \"bar\") is None\n    assert config.get(\"http-basic.foo\") is None\n\n\ndef test_set_pypi_token(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    manager.set_pypi_token(\"foo\", \"baz\")\n\n    assert config.get(\"pypi-token.foo\") is None\n\n    assert dummy_keyring.get_password(\"poetry-repository-foo\", \"__token__\") == \"baz\"\n\n\ndef test_get_pypi_token(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    dummy_keyring.set_password(\"poetry-repository-foo\", \"__token__\", \"baz\")\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    assert manager.get_pypi_token(\"foo\") == \"baz\"\n\n\ndef test_delete_pypi_token(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    dummy_keyring.set_password(\"poetry-repository-foo\", \"__token__\", \"baz\")\n    manager = PasswordManager(config)\n\n    assert PoetryKeyring.is_available()\n    manager.delete_pypi_token(\"foo\")\n\n    assert dummy_keyring.get_password(\"poetry-repository-foo\", \"__token__\") is None\n\n\ndef test_set_http_password_with_unavailable_backend(\n    config: Config, with_fail_keyring: None\n) -> None:\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    manager.set_http_password(\"foo\", \"bar\", \"baz\")\n\n    auth = config.get(\"http-basic.foo\")\n    assert auth[\"username\"] == \"bar\"\n    assert auth[\"password\"] == \"baz\"\n\n\n@pytest.mark.parametrize(\n    (\"username\", \"password\", \"is_valid\"),\n    [\n        (\"bar\", \"baz\", True),\n        (\"\", \"baz\", True),\n        (\"bar\", \"\", True),\n        (\"\", \"\", False),\n    ],\n)\ndef test_get_http_auth_with_unavailable_backend(\n    username: str,\n    password: str,\n    is_valid: bool,\n    config: Config,\n    with_fail_keyring: None,\n) -> None:\n    config.auth_config_source.add_property(\n        \"http-basic.foo\", {\"username\": username, \"password\": password}\n    )\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    auth = manager.get_http_auth(\"foo\")\n\n    if is_valid:\n        assert auth is not None\n        assert auth.username == (username or None)\n        assert auth.password == (password or None)\n    else:\n        assert auth.username is auth.password is None\n\n\ndef test_delete_http_password_with_unavailable_backend(\n    config: Config, with_fail_keyring: None\n) -> None:\n    config.auth_config_source.add_property(\n        \"http-basic.foo\", {\"username\": \"bar\", \"password\": \"baz\"}\n    )\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    manager.delete_http_password(\"foo\")\n\n    assert config.get(\"http-basic.foo\") is None\n\n\ndef test_set_pypi_token_with_unavailable_backend(\n    config: Config, with_fail_keyring: None\n) -> None:\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    manager.set_pypi_token(\"foo\", \"baz\")\n\n    assert config.get(\"pypi-token.foo\") == \"baz\"\n\n\ndef test_get_pypi_token_with_unavailable_backend(\n    config: Config, with_fail_keyring: None\n) -> None:\n    config.auth_config_source.add_property(\"pypi-token.foo\", \"baz\")\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    assert manager.get_pypi_token(\"foo\") == \"baz\"\n\n\ndef test_delete_pypi_token_with_unavailable_backend(\n    config: Config, with_fail_keyring: None\n) -> None:\n    config.auth_config_source.add_property(\"pypi-token.foo\", \"baz\")\n    manager = PasswordManager(config)\n\n    assert not PoetryKeyring.is_available()\n    manager.delete_pypi_token(\"foo\")\n\n    assert config.get(\"pypi-token.foo\") is None\n\n\ndef test_keyring_raises_errors_on_keyring_errors(\n    mocker: MockerFixture, with_fail_keyring: None\n) -> None:\n    mocker.patch(\"poetry.utils.password_manager.PoetryKeyring.is_available\")\n\n    key_ring = PoetryKeyring(\"poetry\")\n    with pytest.raises(PoetryKeyringError):\n        key_ring.set_password(\"foo\", \"bar\", \"baz\")\n\n    with pytest.raises(PoetryKeyringError):\n        key_ring.get_password(\"foo\", \"bar\")\n\n    with pytest.raises(PoetryKeyringError):\n        key_ring.delete_password(\"foo\", \"bar\")\n\n\ndef test_keyring_returns_none_on_locked_keyring(\n    with_locked_keyring: None,\n    caplog: LogCaptureFixture,\n) -> None:\n    caplog.set_level(logging.DEBUG, logger=\"poetry.utils.password_manager\")\n    key_ring = PoetryKeyring(\"poetry\")\n\n    cred = key_ring.get_credential(\"foo\")\n\n    assert cred.password is None\n    assert \"Keyring foo is locked\" in caplog.messages\n\n\ndef test_keyring_returns_none_on_erroneous_keyring(\n    with_erroneous_keyring: None,\n    caplog: LogCaptureFixture,\n) -> None:\n    caplog.set_level(logging.DEBUG, logger=\"poetry.utils.password_manager\")\n    key_ring = PoetryKeyring(\"poetry\")\n\n    cred = key_ring.get_credential(\"foo\")\n\n    assert cred.password is None\n    assert \"Accessing keyring foo failed\" in caplog.messages\n\n\ndef test_keyring_with_chainer_backend_and_fail_keyring_should_be_unavailable(\n    with_chained_fail_keyring: None,\n) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_keyring_with_chainer_backend_and_null_keyring_should_be_unavailable(\n    with_chained_null_keyring: None,\n) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_null_keyring_should_be_unavailable(\n    with_null_keyring: None,\n) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_fail_keyring_should_be_unavailable(\n    with_fail_keyring: None,\n) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_locked_keyring_should_not_be_available(with_locked_keyring: None) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_erroneous_keyring_should_not_be_available(\n    with_erroneous_keyring: None,\n) -> None:\n    key_ring = PoetryKeyring(\"poetry\")\n\n    assert not key_ring.is_available()\n\n\ndef test_get_http_auth_from_environment_variables(\n    environ: None, config: Config\n) -> None:\n    os.environ[\"POETRY_HTTP_BASIC_FOO_USERNAME\"] = \"bar\"\n    os.environ[\"POETRY_HTTP_BASIC_FOO_PASSWORD\"] = \"baz\"\n\n    manager = PasswordManager(config)\n\n    auth = manager.get_http_auth(\"foo\")\n    assert auth == HTTPAuthCredential(username=\"bar\", password=\"baz\")\n\n\ndef test_get_http_auth_does_not_call_keyring_when_credentials_in_environment_variables(\n    environ: None, config: Config\n) -> None:\n    os.environ[\"POETRY_HTTP_BASIC_FOO_USERNAME\"] = \"bar\"\n    os.environ[\"POETRY_HTTP_BASIC_FOO_PASSWORD\"] = \"baz\"\n\n    manager = PasswordManager(config)\n    manager.keyring = MagicMock()\n\n    auth = manager.get_http_auth(\"foo\")\n    assert auth == HTTPAuthCredential(username=\"bar\", password=\"baz\")\n    manager.keyring.get_password.assert_not_called()\n\n\ndef test_get_http_auth_does_not_call_keyring_when_password_in_environment_variables(\n    environ: None, config: Config\n) -> None:\n    config.merge(\n        {\n            \"http-basic\": {\"foo\": {\"username\": \"bar\"}},\n        }\n    )\n    os.environ[\"POETRY_HTTP_BASIC_FOO_PASSWORD\"] = \"baz\"\n\n    manager = PasswordManager(config)\n    manager.keyring = MagicMock()\n\n    auth = manager.get_http_auth(\"foo\")\n    assert auth == HTTPAuthCredential(username=\"bar\", password=\"baz\")\n    manager.keyring.get_password.assert_not_called()\n\n\ndef test_get_pypi_token_with_env_var_positive(\n    mocker: MockerFixture,\n    config: Config,\n    with_simple_keyring: None,\n    dummy_keyring: DummyBackend,\n) -> None:\n    sample_token = \"sampletoken-1234\"\n    repo_name = \"foo\"\n    manager = PasswordManager(config)\n    mocker.patch.dict(\n        os.environ,\n        {f\"POETRY_PYPI_TOKEN_{repo_name.upper()}\": sample_token},\n    )\n\n    assert manager.get_pypi_token(repo_name) == sample_token\n\n\ndef test_get_pypi_token_with_env_var_not_available(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    repo_name = \"foo\"\n    manager = PasswordManager(config)\n\n    result_token = manager.get_pypi_token(repo_name)\n\n    assert result_token is None\n\n\ndef test_disabled_keyring_never_called(\n    config: Config, with_simple_keyring: None, dummy_keyring: DummyBackend\n) -> None:\n    config.config[\"keyring\"][\"enabled\"] = False\n    config.config[\"http-basic\"] = {\"onlyuser\": {\"username\": \"user\"}}\n\n    manager = PasswordManager(config)\n    num_public_functions = len([f for f in dir(manager) if not f.startswith(\"_\")])\n    if num_public_functions != 10:\n        pytest.fail(\n            f\"A function was added to or removed from the {PasswordManager.__name__} \"\n            \"class without reflecting this change in this test.\"\n        )\n\n    with pytest.raises(PoetryKeyringError) as e:\n        _ = manager.keyring\n\n    assert str(e.value) == \"Access to keyring was requested, but it is not available\"\n\n    # We made sure that accessing a disabled keyring raises an exception.\n    # Now we call the PasswordManager functions that do access the keyring to\n    # make sure that they never do so when the keyring is disabled.\n    manager.set_pypi_token(repo_name=\"exists\", token=\"token\")\n    manager.get_pypi_token(repo_name=\"exists\")\n    manager.get_pypi_token(repo_name=\"doesn't exist\")\n    manager.delete_pypi_token(repo_name=\"exists\")\n    manager.delete_pypi_token(repo_name=\"doesn't exist\")\n    manager.set_http_password(repo_name=\"exists\", username=\"user\", password=\"password\")\n    manager.get_http_auth(repo_name=\"exists\")\n    manager.get_http_auth(repo_name=\"doesn't exist\")\n    manager.get_http_auth(repo_name=\"onlyuser\")\n    manager.delete_http_password(repo_name=\"exits\")\n    manager.delete_http_password(repo_name=\"doesn't exist\")\n    manager.delete_http_password(repo_name=\"onlyuser\")\n    manager.get_credential(\"a\", \"b\", \"c\", username=\"user\")\n"
  },
  {
    "path": "tests/utils/test_patterns.py",
    "content": "from __future__ import annotations\n\nimport pytest\n\nfrom poetry.utils import patterns\n\n\n@pytest.mark.parametrize(\n    [\"filename\", \"expected\"],\n    [\n        (\n            \"markdown_captions-2-py3-none-any.whl\",\n            {\n                \"namever\": \"markdown_captions-2\",\n                \"name\": \"markdown_captions\",\n                \"ver\": \"2\",\n                \"build\": None,\n                \"pyver\": \"py3\",\n                \"abi\": \"none\",\n                \"plat\": \"any\",\n            },\n        ),\n        (\n            \"SQLAlchemy-1.3.20-cp27-cp27mu-manylinux2010_x86_64.whl\",\n            {\n                \"namever\": \"SQLAlchemy-1.3.20\",\n                \"name\": \"SQLAlchemy\",\n                \"ver\": \"1.3.20\",\n                \"build\": None,\n                \"pyver\": \"cp27\",\n                \"abi\": \"cp27mu\",\n                \"plat\": \"manylinux2010_x86_64\",\n            },\n        ),\n        (\n            \"isort-metadata-4.3.4-py2-none-any.whl\",\n            {\n                \"namever\": \"isort-metadata-4.3.4\",\n                \"name\": \"isort-metadata\",\n                \"ver\": \"4.3.4\",\n                \"build\": None,\n                \"pyver\": \"py2\",\n                \"abi\": \"none\",\n                \"plat\": \"any\",\n            },\n        ),\n    ],\n)\ndef test_wheel_file_re(filename: str, expected: dict[str, str | None]) -> None:\n    match = patterns.wheel_file_re.match(filename)\n    assert match is not None\n    groups = match.groupdict()\n\n    assert groups == expected\n\n\n@pytest.mark.parametrize(\n    [\"filename\", \"expected\"],\n    [\n        (\n            \"poetry_core-1.5.0.tar.gz\",\n            {\n                \"namever\": \"poetry_core-1.5.0\",\n                \"name\": \"poetry_core\",\n                \"ver\": \"1.5.0\",\n                \"format\": \"tar.gz\",\n            },\n        ),\n        (\n            \"flask-restful-swagger-2-0.35.tar.gz\",\n            {\n                \"namever\": \"flask-restful-swagger-2-0.35\",\n                \"name\": \"flask-restful-swagger-2\",\n                \"ver\": \"0.35\",\n                \"format\": \"tar.gz\",\n            },\n        ),\n    ],\n)\ndef test_sdist_file_re(filename: str, expected: dict[str, str | None]) -> None:\n    match = patterns.sdist_file_re.match(filename)\n    assert match is not None\n    groups = match.groupdict()\n\n    assert groups == expected\n"
  },
  {
    "path": "tests/utils/test_pip.py",
    "content": "from __future__ import annotations\n\nimport subprocess\n\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.pip import pip_install\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from pytest_mock import MockerFixture\n\n    from poetry.utils.env import VirtualEnv\n    from tests.types import FixtureDirGetter\n\n\ndef test_pip_install_successful(\n    tmp_path: Path, tmp_venv: VirtualEnv, fixture_dir: FixtureDirGetter\n) -> None:\n    file_path = fixture_dir(\"distributions/demo-0.1.0-py2.py3-none-any.whl\")\n    result = pip_install(file_path, tmp_venv)\n\n    assert \"Successfully installed demo-0.1.0\" in result\n\n\ndef test_pip_install_with_keyboard_interrupt(\n    tmp_path: Path,\n    tmp_venv: VirtualEnv,\n    fixture_dir: FixtureDirGetter,\n    mocker: MockerFixture,\n) -> None:\n    file_path = fixture_dir(\"distributions/demo-0.1.0-py2.py3-none-any.whl\")\n    mocker.patch(\"subprocess.run\", side_effect=KeyboardInterrupt())\n    with pytest.raises(KeyboardInterrupt):\n        pip_install(file_path, tmp_venv)\n    subprocess.run.assert_called_once()  # type: ignore[attr-defined]\n"
  },
  {
    "path": "tests/utils/test_python_manager.py",
    "content": "from __future__ import annotations\n\nimport os\nimport sys\nimport textwrap\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nimport findpython\nimport packaging.version\nimport pytest\n\nfrom poetry.core.constraints.version import Version\n\nfrom poetry.utils.env.python import Python\n\n\nif TYPE_CHECKING:\n    from unittest.mock import MagicMock\n\n    from pytest_mock import MockerFixture\n\n    from poetry.config.config import Config\n    from tests.types import MockedPythonRegister\n    from tests.types import ProjectFactory\n\n\n@pytest.fixture(scope=\"session\")\ndef python_version() -> Version:\n    version = sys.version.split(\" \", 1)[0]\n    if version[-1] == \"+\":\n        version = version[:-1]\n    return Version.parse(version)\n\n\ndef test_python_get_version_on_the_fly() -> None:\n    python = Python.get_system_python()\n\n    assert python.version == Version.parse(\n        \".\".join([str(s) for s in sys.version_info[:3]])\n    )\n    assert python.patch_version == Version.parse(\n        \".\".join([str(s) for s in sys.version_info[:3]])\n    )\n    assert python.minor_version == Version.parse(\n        \".\".join([str(s) for s in sys.version_info[:2]])\n    )\n\n\ndef test_python_get_system_python() -> None:\n    python = Python.get_system_python()\n\n    assert python.executable.resolve() == findpython.find().executable.resolve()\n    assert python.version == Version.parse(\n        \".\".join(str(v) for v in sys.version_info[:3])\n    )\n\n\ndef test_python_get_preferred_default(config: Config, python_version: Version) -> None:\n    python = Python.get_preferred_python(config)\n\n    assert python.executable.resolve() == Path(sys.executable).resolve()\n    assert python.version == python_version\n\n\ndef test_get_preferred_python_use_poetry_python_disabled(\n    config: Config, mocker: MockerFixture\n) -> None:\n    mocker.patch(\n        \"poetry.utils.env.python.Python.get_active_python\",\n        return_value=Python(\n            python=findpython.PythonVersion(\n                executable=Path(\"/usr/bin/python3.7\"),\n                _version=packaging.version.Version(\"3.7.1\"),\n                _interpreter=Path(\"/usr/bin/python3.7\"),\n            )\n        ),\n    )\n\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = False\n    python = Python.get_preferred_python(config)\n\n    assert python.executable.as_posix().startswith(\"/usr/bin/python\")\n    assert python.version == Version.parse(\"3.7.1\")\n\n\ndef test_get_preferred_python_use_poetry_python_disabled_fallback(\n    config: Config, with_no_active_python: MagicMock\n) -> None:\n    config.config[\"virtualenvs\"][\"use-poetry-python\"] = False\n    python = Python.get_preferred_python(config)\n\n    assert with_no_active_python.call_count == 1\n    assert python.executable.resolve() == Path(sys.executable).resolve()\n\n\ndef test_fallback_on_detect_active_python(with_no_active_python: MagicMock) -> None:\n    active_python = Python.get_active_python()\n    assert active_python is None\n    assert with_no_active_python.call_count == 1\n\n\n@pytest.mark.skipif(sys.platform != \"win32\", reason=\"Windows only\")\ndef test_detect_active_python_with_bat(\n    tmp_path: Path, without_mocked_findpython: None, python_version: Version\n) -> None:\n    \"\"\"On Windows pyenv uses batch files for python management.\"\"\"\n    python_wrapper = tmp_path / \"python.bat\"\n\n    with python_wrapper.open(\"w\", encoding=\"locale\") as f:\n        f.write(\n            textwrap.dedent(f\"\"\"\n            @echo off\n            SET PYTHON_EXE=\"{sys.executable}\"\n            %PYTHON_EXE% %*\n        \"\"\")\n        )\n    os.environ[\"PATH\"] = str(python_wrapper.parent) + os.pathsep + os.environ[\"PATH\"]\n\n    python = Python.get_active_python()\n    assert python is not None\n\n    # TODO: Asses if Poetry needs to discover real path in these cases as\n    # this is not a symlink and won't be handled by findpython\n    assert python.executable.as_posix() == Path(sys.executable).as_posix()\n    assert python.version == python_version\n\n\ndef test_python_find_compatible(\n    project_factory: ProjectFactory, mocked_python_register: MockedPythonRegister\n) -> None:\n    # Note: This test may fail on Windows systems using Python from the Microsoft Store,\n    # as the executable is named `py.exe`, which is not currently recognized by\n    # Python.get_compatible_python. This issue will be resolved in #2117.\n    # However, this does not cause problems in our case because Poetry's own\n    # Python interpreter is used before attempting to find another compatible version.\n    fixture = Path(__file__).parent.parent / \"fixtures\" / \"simple_project\"\n    poetry = project_factory(\"simple-project\", source=fixture)\n    mocked_python_register(\"3.12\")\n    python = Python.get_compatible_python(poetry)\n\n    assert Version.from_parts(3, 4) <= python.version <= Version.from_parts(4, 0)\n"
  },
  {
    "path": "tests/utils/test_threading.py",
    "content": "from __future__ import annotations\n\nimport functools\nimport logging\nimport os\nimport sys\nimport time\n\nfrom concurrent.futures import wait\nfrom concurrent.futures.thread import ThreadPoolExecutor\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom poetry.utils.threading import AtomicCachedProperty\nfrom poetry.utils.threading import atomic_cached_property\n\n\nif TYPE_CHECKING:\n    from collections.abc import Generator\n\n    from pytest import LogCaptureFixture\n    from pytest_mock import MockerFixture\n\n\nWORKER_COUNT = (os.cpu_count() or 1) + 4\nEXPECTED_VALUE = sum(range(1_00_000))\nIS_PY_312 = (sys.version_info.major, sys.version_info.minor) >= (3, 12)\n\n\nclass Example:\n    def __init__(self, value: int = 0, name: str = \"default\") -> None:\n        self.value = value\n        self._name = name\n\n    @classmethod\n    def compute_value(cls, name: str, ts: float) -> int:\n        logging.getLogger().info(\n            \"Example compute_value called with name=%s time=%f\", name, ts\n        )\n        return sum(range(1_00_000))\n\n    def _compute_value(self) -> int:\n        # we block the thread here to ensure contention\n        time.sleep(0.05)\n        return self.compute_value(self._name, time.time())\n\n    @functools.cached_property\n    def value_functools_cached_property(self) -> int:\n        return self._compute_value() + self.value\n\n    @property\n    @functools.cache  # noqa: B019\n    def value_functools_cache(self) -> int:\n        return self._compute_value() + self.value\n\n    @atomic_cached_property\n    def value_atomic_cached_property(self) -> int:\n        return self._compute_value() + self.value\n\n\n@pytest.fixture(autouse=True)\ndef capture_logging(caplog: LogCaptureFixture) -> Generator[None]:\n    with caplog.at_level(logging.DEBUG):\n        yield\n\n\ndef test_threading_property_types() -> None:\n    assert isinstance(Example.value_atomic_cached_property, AtomicCachedProperty)\n    assert isinstance(\n        Example.value_functools_cached_property, functools.cached_property\n    )\n    assert isinstance(Example.value_functools_cache, property)\n\n\ndef test_threading_single_thread_safe() -> None:\n    instance = Example()\n    assert (\n        instance.value_functools_cached_property\n        == instance.value_atomic_cached_property\n        == EXPECTED_VALUE\n    )\n\n\ndef run_in_threads(instance: Example, property_name: str) -> None:\n    results = []\n\n    def access_property() -> None:\n        results.append(instance.__getattribute__(property_name))\n\n    executor = ThreadPoolExecutor(max_workers=WORKER_COUNT)\n    futures = [executor.submit(access_property) for _ in range(WORKER_COUNT)]\n\n    wait(futures)\n    assert len(results) == WORKER_COUNT\n    assert all(result == (EXPECTED_VALUE + instance.value) for result in results)\n\n\n@pytest.mark.parametrize(\n    [\"property_name\", \"expected_call_count\"],\n    [\n        (\"value_atomic_cached_property\", 1),\n        # prior to Python 3.12, cached_property did have a thread lock\n        (\"value_functools_cached_property\", WORKER_COUNT if IS_PY_312 else 1),\n        (\"value_functools_cache\", WORKER_COUNT),\n    ],\n)\ndef test_threading_property_caching(\n    property_name: str,\n    expected_call_count: int,\n    mocker: MockerFixture,\n    caplog: LogCaptureFixture,\n) -> None:\n    compute_value_spy = mocker.spy(Example, \"compute_value\")\n    run_in_threads(Example(), property_name)\n    assert compute_value_spy.call_count == len(caplog.messages) == expected_call_count\n\n\n@pytest.mark.parametrize(\n    [\"property_name\", \"expected_call_count\"],\n    [\n        (\"value_atomic_cached_property\", 2),\n        # prior to Python 3.12, cached_property did have a thread lock\n        (\"value_functools_cached_property\", (WORKER_COUNT if IS_PY_312 else 1) * 2),\n        (\"value_functools_cache\", WORKER_COUNT * 2),\n    ],\n)\ndef test_threading_atomic_cached_property_different_instances(\n    property_name: str,\n    expected_call_count: int,\n    mocker: MockerFixture,\n    caplog: LogCaptureFixture,\n) -> None:\n    compute_value_spy = mocker.spy(Example, \"compute_value\")\n\n    instance1 = Example(10, \"one\")\n    instance2 = Example(20, \"two\")\n\n    run_in_threads(instance1, property_name)\n    run_in_threads(instance2, property_name)\n\n    assert compute_value_spy.call_count == len(caplog.messages) == expected_call_count\n\n    assert instance1.__getattribute__(property_name) == EXPECTED_VALUE + 10\n    assert instance2.__getattribute__(property_name) == EXPECTED_VALUE + 20\n"
  },
  {
    "path": "tests/vcs/git/conftest.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport dulwich.repo\nimport pytest\n\nfrom tests.vcs.git.git_fixture import TempRepoFixture\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\n@pytest.fixture()\ndef temp_repo(tmp_path: Path) -> TempRepoFixture:\n    \"\"\"Temporary repository with 2 commits\"\"\"\n    repo = dulwich.repo.Repo.init(str(tmp_path), default_branch=b\"main\")\n    worktree = repo.get_worktree()\n\n    # init commit\n    (tmp_path / \"foo\").write_text(\"foo\", encoding=\"utf-8\")\n    worktree.stage([\"foo\"])\n\n    init_commit = worktree.commit(\n        committer=b\"User <user@example.com>\",\n        author=b\"User <user@example.com>\",\n        message=b\"init\",\n        no_verify=True,\n        sign=False,\n    )\n\n    # one commit which is not \"head\"\n    (tmp_path / \"bar\").write_text(\"bar\", encoding=\"utf-8\")\n    worktree.stage([\"bar\"])\n    middle_commit = worktree.commit(\n        committer=b\"User <user@example.com>\",\n        author=b\"User <user@example.com>\",\n        message=b\"extra\",\n        no_verify=True,\n        sign=False,\n    )\n\n    # extra commit\n    (tmp_path / \"third\").write_text(\"third file\", encoding=\"utf-8\")\n    worktree.stage([\"third\"])\n\n    head_commit = worktree.commit(\n        committer=b\"User <user@example.com>\",\n        author=b\"User <user@example.com>\",\n        message=b\"extra\",\n        no_verify=True,\n        sign=False,\n    )\n\n    repo[b\"refs/tags/v1\"] = head_commit\n\n    return TempRepoFixture(\n        path=tmp_path,\n        repo=repo,\n        init_commit=init_commit.decode(),\n        middle_commit=middle_commit.decode(),\n        head_commit=head_commit.decode(),\n    )\n"
  },
  {
    "path": "tests/vcs/git/git_fixture.py",
    "content": "from __future__ import annotations\n\nimport typing\n\n\nif typing.TYPE_CHECKING:\n    from pathlib import Path\n\n    import dulwich.repo\n\n\nclass TempRepoFixture(typing.NamedTuple):\n    path: Path\n    repo: dulwich.repo.Repo\n    init_commit: str\n    middle_commit: str\n    head_commit: str\n"
  },
  {
    "path": "tests/vcs/git/test_backend.py",
    "content": "from __future__ import annotations\n\nimport shutil\n\nfrom typing import TYPE_CHECKING\nfrom typing import cast\n\nimport pytest\n\nfrom dulwich.client import FetchPackResult\nfrom dulwich.refs import HEADREF\nfrom dulwich.refs import Ref\nfrom dulwich.repo import Repo\n\nfrom poetry.console.exceptions import PoetryRuntimeError\nfrom poetry.vcs.git.backend import Git\nfrom poetry.vcs.git.backend import GitRefSpec\nfrom poetry.vcs.git.backend import is_revision_sha\nfrom poetry.vcs.git.backend import peeled_tag\nfrom poetry.vcs.git.backend import urlpathjoin\nfrom tests.helpers import MOCK_DEFAULT_GIT_REVISION\n\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n    from pytest_mock import MockerFixture\n\n    from tests.vcs.git.git_fixture import TempRepoFixture\n\n\nVALID_SHA = \"c5c7624ef64f34d9f50c3b7e8118f7f652fddbbd\"\n\nFULL_SHA_MAIN = \"f7c3bc1d808e04732adf679965ccc34ca7ae3441\"\nFULL_SHA_TAG = \"d4f6c2a8b9e1073451f28c96a5db7e3f9c2a8b7e\"\nSHORT_SHA = \"f7c3bc1d\"\n\n\n@pytest.fixture()\ndef repo_mock(mocker: MockerFixture) -> Repo:\n    repo = mocker.MagicMock(spec=Repo)\n\n    repo.get_config.return_value.get.return_value = (\n        b\"https://github.com/python-poetry/poetry.git\"\n    )\n\n    repo.head.return_value = MOCK_DEFAULT_GIT_REVISION.encode(\"utf-8\")\n\n    # Mock object store for short SHA resolution\n    repo.object_store = mocker.MagicMock()\n    repo.object_store.iter_prefix.return_value = [FULL_SHA_MAIN.encode()]\n\n    return cast(\"Repo\", repo)\n\n\n@pytest.fixture()\ndef fetch_pack_result(mocker: MockerFixture) -> FetchPackResult:\n    mock_fetch_pack_result = mocker.MagicMock(spec=FetchPackResult)\n    mock_fetch_pack_result.refs = {\n        b\"refs/heads/main\": FULL_SHA_MAIN.encode(),\n        b\"refs/heads/feature\": b\"a9b8c7d6e5f4321098765432109876543210abcd\",\n        b\"refs/tags/v1.0.0\": FULL_SHA_TAG.encode(),\n        peeled_tag(b\"refs/tags/v1.0.0\"): FULL_SHA_TAG.encode(),\n        b\"HEAD\": FULL_SHA_MAIN.encode(),\n    }\n    mock_fetch_pack_result.symrefs = {b\"HEAD\": b\"refs/heads/main\"}\n\n    return cast(\"FetchPackResult\", mock_fetch_pack_result)\n\n\ndef test_invalid_revision_sha() -> None:\n    result = is_revision_sha(\"invalid_input\")\n    assert result is False\n\n\ndef test_valid_revision_sha() -> None:\n    result = is_revision_sha(VALID_SHA)\n    assert result is True\n\n\ndef test_invalid_revision_sha_min_len() -> None:\n    result = is_revision_sha(\"c5c7\")\n    assert result is False\n\n\ndef test_invalid_revision_sha_max_len() -> None:\n    result = is_revision_sha(VALID_SHA + \"42\")\n    assert result is False\n\n\n@pytest.mark.parametrize(\n    (\"url\"),\n    [\n        \"git@github.com:python-poetry/poetry.git\",\n        \"https://github.com/python-poetry/poetry.git\",\n        \"https://github.com/python-poetry/poetry\",\n        \"https://github.com/python-poetry/poetry/\",\n    ],\n)\ndef test_get_name_from_source_url(url: str) -> None:\n    name = Git.get_name_from_source_url(url)\n    assert name == \"poetry\"\n\n\n@pytest.mark.parametrize((\"tag\"), [\"my-tag\", b\"my-tag\"])\ndef test_peeled_tag(tag: str | bytes) -> None:\n    tag = peeled_tag(\"my-tag\")\n    assert tag == b\"my-tag^{}\"\n\n\ndef test_get_remote_url(repo_mock: Repo) -> None:\n    assert (\n        Git.get_remote_url(repo_mock) == \"https://github.com/python-poetry/poetry.git\"\n    )\n\n\ndef test_get_revision(repo_mock: Repo) -> None:\n    assert Git.get_revision(repo_mock) == MOCK_DEFAULT_GIT_REVISION\n\n\ndef test_info(repo_mock: Repo) -> None:\n    info = Git.info(repo_mock)\n\n    assert info.origin == \"https://github.com/python-poetry/poetry.git\"\n    assert (\n        info.revision == MOCK_DEFAULT_GIT_REVISION\n    )  # revision already mocked in helper\n\n\n@pytest.mark.parametrize(\n    \"url, expected_result\",\n    [\n        (\"ssh://git@github.com/org/repo\", \"ssh://git@github.com/other-repo\"),\n        (\"ssh://git@github.com/org/repo/\", \"ssh://git@github.com/org/other-repo\"),\n    ],\n)\ndef test_urlpathjoin(url: str, expected_result: str) -> None:\n    path = \"../other-repo\"\n    result = urlpathjoin(url, path)\n    assert result == expected_result\n\n\ndef test_git_refspec() -> None:\n    git_ref = GitRefSpec(\"main\", \"1234\", \"v2\")\n\n    assert git_ref.branch == \"main\"\n    assert git_ref.revision == \"1234\"\n    assert git_ref.tag == \"v2\"\n    assert git_ref.ref == b\"HEAD\"\n\n\n@pytest.mark.parametrize(\n    \"refspec, expected_ref, expected_branch, expected_revision, expected_tag\",\n    [\n        # Basic parameter tests\n        (\n            GitRefSpec(branch=\"main\"),\n            b\"refs/heads/main\",\n            \"main\",\n            None,\n            None,\n        ),\n        (\n            GitRefSpec(tag=\"v1.0.0\"),\n            peeled_tag(b\"refs/tags/v1.0.0\"),\n            None,\n            None,\n            \"v1.0.0\",\n        ),\n        (\n            GitRefSpec(branch=\"refs/heads/feature\"),\n            b\"refs/heads/feature\",\n            \"refs/heads/feature\",\n            None,\n            None,\n        ),\n        # Cross-parameter resolution tests\n        (\n            GitRefSpec(revision=\"v1.0.0\"),\n            peeled_tag(b\"refs/tags/v1.0.0\"),\n            None,\n            None,\n            \"v1.0.0\",\n        ),\n        (\n            GitRefSpec(revision=\"main\"),\n            b\"refs/heads/main\",\n            \"main\",\n            None,\n            None,\n        ),\n        (\n            GitRefSpec(branch=\"v1.0.0\"),\n            peeled_tag(b\"refs/tags/v1.0.0\"),\n            None,\n            None,\n            \"v1.0.0\",\n        ),\n        (\n            GitRefSpec(revision=\"refs/heads/main\"),\n            b\"refs/heads/main\",\n            \"refs/heads/main\",\n            None,\n            None,\n        ),\n        # SHA resolution tests with realistic values\n        (\n            GitRefSpec(revision=SHORT_SHA),\n            b\"refs/heads/main\",\n            None,\n            FULL_SHA_MAIN,\n            None,\n        ),\n        (\n            GitRefSpec(revision=FULL_SHA_MAIN),\n            b\"refs/heads/main\",\n            None,\n            FULL_SHA_MAIN,\n            None,\n        ),\n    ],\n)\ndef test_git_ref_spec_resolve(\n    fetch_pack_result: FetchPackResult,\n    repo_mock: Repo,\n    refspec: GitRefSpec,\n    expected_ref: bytes,\n    expected_branch: str | None,\n    expected_revision: str | None,\n    expected_tag: str | None,\n) -> None:\n    refspec.resolve(fetch_pack_result, repo_mock)\n\n    assert refspec.ref == expected_ref\n    assert refspec.branch == expected_branch\n    assert refspec.revision == expected_revision\n    assert refspec.tag == expected_tag\n\n\n@pytest.mark.skip_git_mock\ndef test_clone_success(tmp_path: Path, temp_repo: TempRepoFixture) -> None:\n    source_root_dir = tmp_path / \"test-repo\"\n    Git.clone(\n        url=temp_repo.path.as_uri(), source_root=source_root_dir, name=\"clone-test\"\n    )\n\n    target_dir = source_root_dir / \"clone-test\"\n    assert (target_dir / \".git\").is_dir()\n\n\n@pytest.mark.skip_git_mock\ndef test_short_sha_not_in_head(tmp_path: Path, temp_repo: TempRepoFixture) -> None:\n    source_root_dir = tmp_path / \"test-repo\"\n    Git.clone(\n        url=temp_repo.path.as_uri(),\n        revision=temp_repo.middle_commit[:6],\n        name=\"clone-test\",\n        source_root=source_root_dir,\n    )\n\n    target_dir = source_root_dir / \"clone-test\"\n    assert (target_dir / \".git\").is_dir()\n\n\n@pytest.mark.skip_git_mock\ndef test_clone_existing_locked_tag(tmp_path: Path, temp_repo: TempRepoFixture) -> None:\n    source_root_dir = tmp_path / \"test-repo\"\n    source_url = temp_repo.path.as_uri()\n    Git.clone(url=source_url, source_root=source_root_dir, name=\"clone-test\")\n\n    tag_ref = source_root_dir / \"clone-test\" / \".git\" / \"refs\" / \"tags\" / \"v1\"\n    assert tag_ref.is_file()\n\n    tag_ref_lock = tag_ref.with_name(\"v1.lock\")\n    shutil.copy(tag_ref, tag_ref_lock)\n\n    with pytest.raises(PoetryRuntimeError) as exc_info:\n        Git.clone(url=source_url, source_root=source_root_dir, name=\"clone-test\")\n\n    expected_short = (\n        f\"Failed to clone {source_url} at 'refs/heads/main',\"\n        f\" unable to acquire file lock for {tag_ref}.\"\n    )\n    assert str(exc_info.value) == expected_short\n    assert exc_info.value.get_text(debug=True, strip=True) == (\n        f\"{expected_short}\\n\\n\"\n        \"Note: This error arises from interacting with the specified vcs source\"\n        \" and is likely not a Poetry issue.\\n\"\n        \"This issue could be caused by any of the following;\\n\\n\"\n        \"- another process is holding the file lock\\n\"\n        \"- another process crashed while holding the file lock\\n\\n\"\n        f\"Try again later or remove the {tag_ref_lock} manually\"\n        \" if you are sure no other process is holding it.\"\n    )\n\n\n@pytest.mark.skip_git_mock\ndef test_clone_annotated_tag(tmp_path: Path) -> None:\n    \"\"\"Test cloning at an annotated tag (issue #10658).\"\"\"\n    from dulwich import porcelain\n    from dulwich.objects import Commit\n\n    # Create a source repository with an annotated tag\n    source_path = tmp_path / \"source-repo\"\n    source_path.mkdir()\n    repo = Repo.init(str(source_path))\n\n    # Create initial commit\n    test_file = source_path / \"test.txt\"\n    test_file.write_text(\"test content\", encoding=\"utf-8\")\n    porcelain.add(repo, str(test_file))\n    expected_commit_sha = porcelain.commit(\n        repo,\n        message=b\"Initial commit\",\n        author=b\"Test <test@example.com>\",\n        committer=b\"Test <test@example.com>\",\n    )\n\n    # Create an annotated tag\n    porcelain.tag_create(\n        repo,\n        tag=b\"v1.0.0\",\n        message=b\"Release 1.0.0\",\n        author=b\"Test <test@example.com>\",\n        annotated=True,\n    )\n\n    # Clone at the annotated tag\n    source_root_dir = tmp_path / \"clone-root\"\n    source_root_dir.mkdir()\n    cloned_repo = Git.clone(\n        url=source_path.as_uri(),\n        source_root=source_root_dir,\n        name=\"clone-test\",\n        tag=\"v1.0.0\",\n    )\n\n    # Verify HEAD points to a commit, not a tag object\n    head_sha = cloned_repo.refs[HEADREF]\n    head_obj = cloned_repo.object_store[head_sha]\n    assert isinstance(head_obj, Commit), (\n        f\"HEAD should point to a Commit, got {type(head_obj).__name__}\"\n    )\n    # Verify it's the correct commit\n    assert head_sha == expected_commit_sha, (\n        f\"HEAD should point to the expected commit {expected_commit_sha.hex()}, \"\n        f\"got {head_sha.hex()}\"\n    )\n\n    # Verify the clone succeeded and files are present\n    clone_dir = source_root_dir / \"clone-test\"\n    assert (clone_dir / \".git\").is_dir()\n    assert (clone_dir / \"test.txt\").exists()\n    assert (clone_dir / \"test.txt\").read_text(encoding=\"utf-8\") == \"test content\"\n\n\n@pytest.mark.skip_git_mock\ndef test_clone_nested_annotated_tags(tmp_path: Path) -> None:\n    \"\"\"Test cloning at a tag that points to another tag (nested tags).\"\"\"\n    from dulwich import porcelain\n    from dulwich.objects import Commit\n    from dulwich.objects import Tag\n\n    # Create a source repository with nested annotated tags\n    source_path = tmp_path / \"source-repo\"\n    source_path.mkdir()\n    repo = Repo.init(str(source_path))\n\n    # Create initial commit\n    test_file = source_path / \"test.txt\"\n    test_file.write_text(\"nested tag test\", encoding=\"utf-8\")\n    porcelain.add(repo, paths=[b\"test.txt\"])\n    commit_sha = porcelain.commit(\n        repo,\n        message=b\"Initial commit\",\n        committer=b\"Test <test@example.com>\",\n        author=b\"Test <test@example.com>\",\n    )\n\n    # Create first annotated tag pointing to the commit\n    tag1 = Tag()\n    tag1.name = b\"v1.0.0\"\n    tag1.object = (Commit, commit_sha)\n    tag1.message = b\"First tag\"\n    tag1.tag_time = 1234567890\n    tag1.tag_timezone = 0\n    tag1.tagger = b\"Test <test@example.com>\"\n    repo.object_store.add_object(tag1)\n    repo.refs[Ref(b\"refs/tags/v1.0.0\")] = tag1.id\n\n    # Create second annotated tag pointing to the first tag\n    tag2 = Tag()\n    tag2.name = b\"v1.0.0-release\"\n    tag2.object = (Tag, tag1.id)\n    tag2.message = b\"Second tag (points to first tag)\"\n    tag2.tag_time = 1234567891\n    tag2.tag_timezone = 0\n    tag2.tagger = b\"Test <test@example.com>\"\n    repo.object_store.add_object(tag2)\n    repo.refs[Ref(b\"refs/tags/v1.0.0-release\")] = tag2.id\n\n    # Clone at the nested tag\n    source_root_dir = tmp_path / \"clone-root\"\n    source_root_dir.mkdir()\n    cloned_repo = Git.clone(\n        url=source_path.as_uri(),\n        source_root=source_root_dir,\n        name=\"clone-test\",\n        tag=\"v1.0.0-release\",\n    )\n\n    # Verify HEAD points to a commit, not a tag object\n    head_sha = cloned_repo.refs[HEADREF]\n    head_obj = cloned_repo.object_store[head_sha]\n    assert isinstance(head_obj, Commit), (\n        f\"HEAD should point to a Commit (peeling nested tags), got {type(head_obj).__name__}\"\n    )\n\n    # Verify it's the correct commit\n    assert head_sha == commit_sha\n\n    # Verify the clone succeeded and files are present\n    clone_dir = source_root_dir / \"clone-test\"\n    assert (clone_dir / \".git\").is_dir()\n    assert (clone_dir / \"test.txt\").exists()\n    assert (clone_dir / \"test.txt\").read_text(encoding=\"utf-8\") == \"nested tag test\"\n"
  },
  {
    "path": "tests/vcs/git/test_system.py",
    "content": "from __future__ import annotations\n\nimport re\nimport shutil\nimport subprocess\nimport typing\n\nimport pytest\n\nfrom poetry.vcs.git.system import SystemGit\n\n\nif typing.TYPE_CHECKING:\n    from pathlib import Path\n\n    from tests.vcs.git.git_fixture import TempRepoFixture\n\nGIT_NOT_INSTALLED = shutil.which(\"git\") is None\n\n\ndef get_head_sha(cwd: Path) -> str:\n    return subprocess.check_output(\n        [\"git\", \"rev-parse\", \"HEAD\"],\n        cwd=cwd,\n        text=True,\n        encoding=\"utf-8\",\n    ).strip()\n\n\n@pytest.mark.skipif(GIT_NOT_INSTALLED, reason=\"These tests requires git cli\")\nclass TestSystemGit:\n    def test_clone_success(self, tmp_path: Path, temp_repo: TempRepoFixture) -> None:\n        target_dir = tmp_path / \"test-repo\"\n        SystemGit.clone(temp_repo.path.as_uri(), target_dir)\n        assert (target_dir / \".git\").is_dir()\n\n    def test_clone_invalid_parameter(self, tmp_path: Path) -> None:\n        with pytest.raises(\n            RuntimeError, match=re.escape(\"Invalid Git parameter: --upload-pack\")\n        ):\n            SystemGit.clone(\"--upload-pack=touch ./HELL\", tmp_path)\n\n    def test_checkout_1(self, temp_repo: TempRepoFixture) -> None:\n        # case 1 - with 'target' arg\n        SystemGit.checkout(temp_repo.init_commit[:12], temp_repo.path)\n        assert get_head_sha(temp_repo.path) == temp_repo.init_commit\n\n    def test_checkout_2(\n        self, monkeypatch: pytest.MonkeyPatch, temp_repo: TempRepoFixture\n    ) -> None:\n        # case 2 - without 'target' arg\n        monkeypatch.chdir(temp_repo.path)\n\n        SystemGit.checkout(temp_repo.init_commit[:12])\n        assert get_head_sha(temp_repo.path) == temp_repo.init_commit\n"
  }
]